diff --git a/go.mod b/go.mod index 5a17336..2550e45 100644 --- a/go.mod +++ b/go.mod @@ -31,10 +31,10 @@ require ( github.com/libp2p/go-libp2p-pubsub v0.14.1 github.com/multiformats/go-multiaddr v0.16.0 github.com/prometheus/client_golang v1.22.0 - github.com/rollkit/rollkit v0.14.2-0.20250630141726-fa03f78d8121 - github.com/rollkit/rollkit/core v0.0.0-20250630141726-fa03f78d8121 - github.com/rollkit/rollkit/da v0.0.0-20250630141726-fa03f78d8121 - github.com/rollkit/rollkit/sequencers/single v0.0.0-20250630141726-fa03f78d8121 + github.com/rollkit/rollkit v0.14.2-0.20250703132945-e191259d4cac + github.com/rollkit/rollkit/core v0.0.0-20250703132945-e191259d4cac + github.com/rollkit/rollkit/da v0.0.0-20250703132945-e191259d4cac + github.com/rollkit/rollkit/sequencers/single v0.0.0-20250703132945-e191259d4cac github.com/rs/cors v1.11.1 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 @@ -125,7 +125,7 @@ require ( github.com/filecoin-project/go-jsonrpc v0.7.1 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-chi/chi/v5 v5.2.2 // indirect @@ -134,6 +134,7 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/goccy/go-yaml v1.18.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -158,14 +159,11 @@ require ( github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.6.3 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/huandu/skiplist v1.2.0 // indirect @@ -173,10 +171,9 @@ require ( github.com/iancoleman/strcase v0.3.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/ipfs/boxo v0.27.4 // indirect + github.com/ipfs/boxo v0.30.0 // indirect github.com/ipfs/go-cid v0.5.0 // indirect github.com/ipfs/go-ds-badger4 v0.1.8 // indirect - github.com/ipfs/go-log v1.0.5 // indirect github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect @@ -193,20 +190,19 @@ require ( github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.2.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.29.1 // indirect - github.com/libp2p/go-libp2p-kbucket v0.6.5 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.33.1 // indirect + github.com/libp2p/go-libp2p-kbucket v0.7.0 // indirect github.com/libp2p/go-libp2p-record v0.3.1 // indirect - github.com/libp2p/go-libp2p-routing-helpers v0.7.4 // indirect + github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-netroute v0.2.2 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v5 v5.0.0 // indirect github.com/linxGnu/grocksdb v1.8.14 // indirect - github.com/magiconair/properties v1.8.7 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/miekg/dns v1.1.64 // indirect + github.com/miekg/dns v1.1.66 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/highwayhash v1.0.3 // indirect @@ -244,9 +240,8 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runtime-spec v1.2.1 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pion/datachannel v1.5.10 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect @@ -271,9 +266,9 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polydawn/refmt v0.89.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.16.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.51.0 // indirect github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect @@ -282,18 +277,17 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/zerolog v1.34.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/encoding v0.4.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.19.0 // indirect + github.com/spf13/viper v1.20.1 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect @@ -334,14 +328,13 @@ require ( golang.org/x/text v0.26.0 // indirect golang.org/x/tools v0.33.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - gonum.org/v1/gonum v0.15.1 // indirect - google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + gonum.org/v1/gonum v0.16.0 // indirect + google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // 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 gotest.tools/v3 v3.5.2 // indirect - lukechampine.com/blake3 v1.4.0 // indirect + lukechampine.com/blake3 v1.4.1 // indirect nhooyr.io/websocket v1.8.6 // indirect pgregory.net/rapid v1.1.0 // indirect pluginrpc.com/pluginrpc v0.5.0 // indirect diff --git a/go.sum b/go.sum index c40f4e9..17d9f7f 100644 --- a/go.sum +++ b/go.sum @@ -323,8 +323,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -374,6 +374,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 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= @@ -513,8 +515,6 @@ github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NM github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -526,8 +526,6 @@ github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6e github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= @@ -545,8 +543,6 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= @@ -572,8 +568,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/ipfs/boxo v0.27.4 h1:6nC8lY5GnR6whAbW88hFz6L13wZUj2vr5BRe3iTvYBI= -github.com/ipfs/boxo v0.27.4/go.mod h1:qEIRrGNr0bitDedTCzyzBHxzNWqYmyuHgK8LG9Q83EM= +github.com/ipfs/boxo v0.30.0 h1:7afsoxPGGqfoH7Dum/wOTGUB9M5fb8HyKPMlLfBvIEQ= +github.com/ipfs/boxo v0.30.0/go.mod h1:BPqgGGyHB9rZZcPSzah2Dc9C+5Or3U1aQe7EH1H7370= github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM= github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= @@ -586,13 +582,10 @@ github.com/ipfs/go-ds-badger4 v0.1.8 h1:frNczf5CjCVm62RJ5mW5tD/oLQY/9IKAUpKviRV9 github.com/ipfs/go-ds-badger4 v0.1.8/go.mod h1:FdqSLA5TMsyqooENB/Hf4xzYE/iH0z/ErLD6ogtfMrA= github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= -github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= -github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= -github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-log/v2 v2.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg= github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8= -github.com/ipfs/go-test v0.0.4 h1:DKT66T6GBB6PsDFLoO56QZPrOmzJkqU1FZH5C9ySkew= -github.com/ipfs/go-test v0.0.4/go.mod h1:qhIM1EluEfElKKM6fnWxGn822/z9knUGM1+I/OAQNKI= +github.com/ipfs/go-test v0.2.1 h1:/D/a8xZ2JzkYqcVcV/7HYlCnc7bv/pKHQiX5TdClkPE= +github.com/ipfs/go-test v0.2.1/go.mod h1:dzu+KB9cmWjuJnXFDYJwC25T3j1GcN57byN+ixmK39M= github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= @@ -669,16 +662,16 @@ github.com/libp2p/go-libp2p v0.41.1 h1:8ecNQVT5ev/jqALTvisSJeVNvXYJyK4NhQx1nNRXQ github.com/libp2p/go-libp2p v0.41.1/go.mod h1:DcGTovJzQl/I7HMrby5ZRjeD0kQkGiy+9w6aEkSZpRI= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= -github.com/libp2p/go-libp2p-kad-dht v0.29.1 h1:RyD1RnnkXOh1gwBCrMQ6ZVfTJECY5yDOY6qxt9VNqE4= -github.com/libp2p/go-libp2p-kad-dht v0.29.1/go.mod h1:tZEFTKWCsY0xngypKyAIwNDNZOBiikSUIgd/BjTF5Ms= -github.com/libp2p/go-libp2p-kbucket v0.6.5 h1:Fsl1YvZcMwqrR4DYrTO02yo9PGYs2HBQIT3lGXFMTxg= -github.com/libp2p/go-libp2p-kbucket v0.6.5/go.mod h1:U6WOd0BvnSp03IQSrjgM54tg7zh1UUNsXLJqAQzClTA= +github.com/libp2p/go-libp2p-kad-dht v0.33.1 h1:hKFhHMf7WH69LDjaxsJUWOU6qZm71uO47M/a5ijkiP0= +github.com/libp2p/go-libp2p-kad-dht v0.33.1/go.mod h1:CdmNk4VeGJa9EXM9SLNyNVySEvduKvb+5rSC/H4pLAo= +github.com/libp2p/go-libp2p-kbucket v0.7.0 h1:vYDvRjkyJPeWunQXqcW2Z6E93Ywx7fX0jgzb/dGOKCs= +github.com/libp2p/go-libp2p-kbucket v0.7.0/go.mod h1:blOINGIj1yiPYlVEX0Rj9QwEkmVnz3EP8LK1dRKBC6g= github.com/libp2p/go-libp2p-pubsub v0.14.1 h1:XK/rPKZKhPvRrtsjvfwrOZPnQQbGLmaEg7u6qnJfn8U= github.com/libp2p/go-libp2p-pubsub v0.14.1/go.mod h1:MKPU5vMI8RRFyTP0HfdsF9cLmL1nHAeJm44AxJGJx44= github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg= github.com/libp2p/go-libp2p-record v0.3.1/go.mod h1:T8itUkLcWQLCYMqtX7Th6r7SexyUJpIyPgks757td/E= -github.com/libp2p/go-libp2p-routing-helpers v0.7.4 h1:6LqS1Bzn5CfDJ4tzvP9uwh42IB7TJLNFJA6dEeGBv84= -github.com/libp2p/go-libp2p-routing-helpers v0.7.4/go.mod h1:we5WDj9tbolBXOuF1hGOkR+r7Uh1408tQbAKaT5n1LE= +github.com/libp2p/go-libp2p-routing-helpers v0.7.5 h1:HdwZj9NKovMx0vqq6YNPTh6aaNzey5zHD7HeLJtq6fI= +github.com/libp2p/go-libp2p-routing-helpers v0.7.5/go.mod h1:3YaxrwP0OBPDD7my3D0KxfR89FlcX/IEbxDEDfAmj98= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= @@ -720,8 +713,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ= -github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= +github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= +github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -864,8 +857,6 @@ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -880,8 +871,8 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= @@ -963,8 +954,8 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -983,8 +974,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= -github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc= @@ -1002,14 +993,14 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rollkit/rollkit v0.14.2-0.20250630141726-fa03f78d8121 h1:CDTRFiNOkYoYwH7kp8vBlA508wmqYvIex7uheD1HonM= -github.com/rollkit/rollkit v0.14.2-0.20250630141726-fa03f78d8121/go.mod h1:voFC1cTCp9osZRb7DWeLLWLAtRI0DWiWoXdpaS5DkeA= -github.com/rollkit/rollkit/core v0.0.0-20250630141726-fa03f78d8121 h1:hZd7CBZXDadofPmmYcnRJF++EgA0UFKcM9B/aukPrJA= -github.com/rollkit/rollkit/core v0.0.0-20250630141726-fa03f78d8121/go.mod h1:0RhbqC8Is970KRhr6zPUQOZkmKt6/WqPRDQWfd2P7P0= -github.com/rollkit/rollkit/da v0.0.0-20250630141726-fa03f78d8121 h1:XDUT6BWc6j/5FV8Gv2iq3AzaPFIFb8pSLabetiAx3KA= -github.com/rollkit/rollkit/da v0.0.0-20250630141726-fa03f78d8121/go.mod h1:oiubohaox9z/Zl5lopu5nXySOIoMtFPJUdrBrlzHIDs= -github.com/rollkit/rollkit/sequencers/single v0.0.0-20250630141726-fa03f78d8121 h1:cPzpb7Ne2lohk8QmvsBHrQ4JRBXo0RmzgYske6SwtDQ= -github.com/rollkit/rollkit/sequencers/single v0.0.0-20250630141726-fa03f78d8121/go.mod h1:xu+rEtcXynLSwB7u/jnNsRiBwmxCLhP3cjH+tEBOAvc= +github.com/rollkit/rollkit v0.14.2-0.20250703132945-e191259d4cac h1:sL+Qcxbjm3Gqaj7usmChWJqbBTZ7S4jYqq2JmS9kBK4= +github.com/rollkit/rollkit v0.14.2-0.20250703132945-e191259d4cac/go.mod h1:RXYW7SkD5sGpA6RpgNrRtZo9UnoOMNKYkffS37Utn+Y= +github.com/rollkit/rollkit/core v0.0.0-20250703132945-e191259d4cac h1:z0FkzJCSL4QfNKcCP5LcPcEBhZxVh1spF8DwrYBM4ac= +github.com/rollkit/rollkit/core v0.0.0-20250703132945-e191259d4cac/go.mod h1:0RhbqC8Is970KRhr6zPUQOZkmKt6/WqPRDQWfd2P7P0= +github.com/rollkit/rollkit/da v0.0.0-20250703132945-e191259d4cac h1:javMdMHVDTqzWhU7eky06dqf2ZZGOLxmYg+Ac28O7QE= +github.com/rollkit/rollkit/da v0.0.0-20250703132945-e191259d4cac/go.mod h1:oiubohaox9z/Zl5lopu5nXySOIoMtFPJUdrBrlzHIDs= +github.com/rollkit/rollkit/sequencers/single v0.0.0-20250703132945-e191259d4cac h1:+hf9Mxo28MRlW4avG+eNzJE28ejuCma5wyTXYMStiq0= +github.com/rollkit/rollkit/sequencers/single v0.0.0-20250703132945-e191259d4cac/go.mod h1:BXo3oPT5TDgrHNuG7HntzR+jCDYctYpAtkQx0BlyARg= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= @@ -1021,10 +1012,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= @@ -1077,8 +1066,8 @@ github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIK github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -1088,8 +1077,8 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -1113,7 +1102,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -1202,7 +1190,6 @@ go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAj go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= @@ -1217,14 +1204,12 @@ go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= @@ -1468,8 +1453,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= -gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -1492,8 +1477,8 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= @@ -1548,8 +1533,6 @@ 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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1576,8 +1559,8 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w= -lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/hack/.gitignore b/hack/.gitignore new file mode 100644 index 0000000..934e3e0 --- /dev/null +++ b/hack/.gitignore @@ -0,0 +1,3 @@ +downloads/ +testnet/ +config/ diff --git a/hack/README.md b/hack/README.md new file mode 100644 index 0000000..7a15364 --- /dev/null +++ b/hack/README.md @@ -0,0 +1,21 @@ +# Hack + +Local test scripts + +## Requirements + +* `gaiad` + `hermes` app are available. Use `download.sh` script to fetch +* Rollkit example app `gmd` is built with network integration and available in the path +* `local-da` is built and located in `../../rollkit/build/local-da` + +## Run local setup + +```shell +./init-gaia.sh # setup and start gaia app +./run_node.sh # setup and start gmd example app + local da + +go run ./attester --chain-id=rollkitnet-1 --home=$(pwd)/testnet/gm --mnemonic="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" --verbose + +./ibc-connection-hermes.sh # relayer +./ics20-token-transfer.sh # token transfer +``` diff --git a/hack/attester/.gitignore b/hack/attester/.gitignore new file mode 100644 index 0000000..b26c9b0 --- /dev/null +++ b/hack/attester/.gitignore @@ -0,0 +1 @@ +/attester diff --git a/hack/attester/README.md b/hack/attester/README.md new file mode 100644 index 0000000..88b97b5 --- /dev/null +++ b/hack/attester/README.md @@ -0,0 +1,3 @@ +# Attest everything attester + +little helper that pulls new blocks from the rpc node and submits (fake) attestations after each epoch diff --git a/hack/attester/main.go b/hack/attester/main.go new file mode 100644 index 0000000..c802556 --- /dev/null +++ b/hack/attester/main.go @@ -0,0 +1,549 @@ +package main + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "os/signal" + "path/filepath" + "strconv" + "syscall" + "time" + + pvm "github.com/cometbft/cometbft/privval" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/std" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/gogoproto/proto" + "github.com/spf13/cobra" + + networktypes "github.com/rollkit/go-execution-abci/modules/network/types" +) + +const ( + flagChainID = "chain-id" + flagNode = "node" + flagAPIAddr = "api-addr" + flagHome = "home" + flagVerbose = "verbose" + flagMnemonic = "mnemonic" +) + +func main() { + rootCmd := &cobra.Command{ + Use: "attester_ws", + Short: "Attester client for Rollkit using websocket", + Long: `Attester client for Rollkit that joins the attester set and attests to blocks at the end of each epoch.`, + DisableFlagParsing: false, + SuggestionsMinimumDistance: 2, + RunE: runAttester, + } + + // Add flags + rootCmd.Flags().String(flagChainID, "", "Chain ID of the blockchain") + rootCmd.Flags().String(flagNode, "tcp://localhost:26657", "RPC node address") + rootCmd.Flags().String(flagAPIAddr, "http://localhost:1317", "API node address") + rootCmd.Flags().String(flagHome, "", "Directory for config and data") + rootCmd.Flags().Bool(flagVerbose, false, "Enable verbose output") + rootCmd.Flags().String(flagMnemonic, "", "Mnemonic for the private key") + + _ = rootCmd.MarkFlagRequired(flagChainID) + _ = rootCmd.MarkFlagRequired(flagMnemonic) + + // Execute + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func runAttester(cmd *cobra.Command, args []string) error { + chainID, err := cmd.Flags().GetString(flagChainID) + if err != nil { + return err + } + + node, err := cmd.Flags().GetString(flagNode) + if err != nil { + return err + } + apiAddr, err := cmd.Flags().GetString(flagAPIAddr) + if err != nil { + return err + } + + home, err := cmd.Flags().GetString(flagHome) + if err != nil { + return err + } + + verbose, err := cmd.Flags().GetBool(flagVerbose) + if err != nil { + return err + } + + mnemonic, err := cmd.Flags().GetString(flagMnemonic) + if err != nil { + return err + } + + // Create context with cancellation + ctx, cancel := context.WithCancel(cmd.Context()) + defer cancel() + + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("gm", "gmpub") + config.SetBech32PrefixForValidator("gmvaloper", "gmvaloperpub") + config.Seal() + + // Create the sender key from the mnemonic + senderKey, err := createPrivateKeyFromMnemonic(mnemonic) + if err != nil { + return fmt.Errorf("failed to create private key from mnemonic: %w", err) + } + + // Get the account address from the private key + pubKey := senderKey.PubKey() + valAddr := sdk.ValAddress(pubKey.Address()) + + if verbose { + addr := sdk.AccAddress(pubKey.Address()) + fmt.Printf("Sender Account address: %s\n", addr.String()) + fmt.Printf("Sender Validator address: %s\n", valAddr.String()) + } + + pv := pvm.LoadOrGenFilePV(filepath.Join(home, "config", "priv_validator_key.json"), filepath.Join(home, "data", "priv_validator_state.json")) + + // Handle OS signals for graceful shutdown + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + go func() { + <-sigCh + fmt.Println("Received signal, shutting down...") + cancel() + }() + + // Step 1: Join attester set + fmt.Println("Joining attester set...") + if err := joinAttesterSet(ctx, chainID, node, verbose, valAddr, senderKey, pv); err != nil { + return fmt.Errorf("join attester set: %w", err) + } + + // Step 2: Query network parameters to get epoch length + fmt.Println("Querying network parameters...") + epochLength, err := getEpochLength(apiAddr) + if err != nil { + return fmt.Errorf("get epoch length: %w", err) + } + fmt.Printf("Epoch length: %d blocks\n", epochLength) + + // Get validator address from private key + valAccAddr := sdk.AccAddress(senderKey.PubKey().Address()) + + // Step 3 & 4: Watch new block events via websocket and attest at the end of each epoch + fmt.Println("Starting to watch for new blocks...") + if err := pullBlocksAndAttest(ctx, chainID, node, home, epochLength, valAccAddr.Bytes(), verbose, senderKey, pv); err != nil { + return fmt.Errorf("error watching blocks: %w", err) + } + + return nil +} + +// joinAttesterSet creates and submits a MsgJoinAttesterSet transaction +func joinAttesterSet(ctx context.Context, chainID, node string, verbose bool, valAddr sdk.ValAddress, privKey *secp256k1.PrivKey, pv *pvm.FilePV) error { + if verbose { + fmt.Printf("Using validator address: %s\n", valAddr.String()) + } + + // Create the message + msg := networktypes.NewMsgJoinAttesterSet(valAddr.String()) + + // Broadcast the transaction + txHash, err := broadcastTx(ctx, chainID, node, msg, privKey, verbose) + if err != nil { + return fmt.Errorf("broadcast join attester set tx: %w", err) + } + + fmt.Printf("Successfully joined attester set. Tx hash: %s\n", txHash) + return nil +} + +// getEpochLength queries the network parameters to get the epoch length +func getEpochLength(apiAddr string) (uint64, error) { + // Create a simple HTTP client to query the node + httpClient := &http.Client{ + Timeout: 10 * time.Second, + } + + // Get epoch parameters + paramsResp, err := httpClient.Get(fmt.Sprintf("%s/rollkit/network/v1/params", apiAddr)) + if err != nil { + return 0, fmt.Errorf("error getting params: %w", err) + } + defer paramsResp.Body.Close() //nolint:errcheck // test code + + var paramsResult struct { + Params struct { + EpochLength string `json:"epoch_length"` + } `json:"params"` + } + var buf bytes.Buffer + + if err := json.NewDecoder(io.TeeReader(paramsResp.Body, &buf)).Decode(¶msResult); err != nil { + return 0, fmt.Errorf("error decoding params: %w: got %s", err, buf.String()) + } + + epochLength, err := strconv.ParseUint(paramsResult.Params.EpochLength, 10, 64) + if err != nil { + return 0, fmt.Errorf("epoch length: %w", err) + } + if epochLength == 0 { + return 0, fmt.Errorf("epoch length is 0") + } + + return epochLength, nil +} + +// pullBlocksAndAttest polls for new blocks via HTTP and attests at the end of each epoch +func pullBlocksAndAttest( + ctx context.Context, + chainID, node, home string, + epochLength uint64, + valAddr sdk.ValAddress, + verbose bool, + senderKey *secp256k1.PrivKey, + pv *pvm.FilePV, +) error { + // Parse node URL + parsed, err := url.Parse(node) + if err != nil { + return fmt.Errorf("parse node URL: %w", err) + } + + httpClient := &http.Client{ + Timeout: 10 * time.Second, + } + + var lastAttested int64 = 0 + + // Poll for new blocks + for { + select { + case <-ctx.Done(): + return nil + default: + // Query latest block + resp, err := httpClient.Get(fmt.Sprintf("http://%s/block", parsed.Host)) + if err != nil { + fmt.Printf("Error querying block: %v\n", err) + time.Sleep(time.Second / 10) + continue + } + + var blockResponse struct { + Result struct { + Block struct { + Header struct { + Height string `json:"height"` + AppHash string `json:"app_hash"` + } `json:"header"` + } `json:"block"` + } `json:"result"` + } + + var buf bytes.Buffer + if err := json.NewDecoder(io.TeeReader(resp.Body, &buf)).Decode(&blockResponse); err != nil { + fmt.Printf("Error parsing response: %v: %s\n", err, buf.String()) + _ = resp.Body.Close() + time.Sleep(time.Second / 10) + continue + } + _ = resp.Body.Close() + + // Extract block height + height, err := strconv.ParseInt(blockResponse.Result.Block.Header.Height, 10, 64) + if err != nil { + fmt.Printf("Error parsing height: %v\n", err) + time.Sleep(time.Second / 10) + continue + } + + fmt.Printf("Current block: %d\n", height) + + // Check if this is the end of an epoch and we haven't attested to it yet + if height > 1 && height%int64(epochLength) == 0 && height > lastAttested { + fmt.Printf("End of epoch at height %d, submitting attestation\n", height) + + // Submit attestation with the block's app hash + appHash, err := hex.DecodeString(blockResponse.Result.Block.Header.AppHash) + if err != nil { + return fmt.Errorf("decoding app hash: %w", err) + } + err = submitAttestation(ctx, chainID, node, home, height, appHash, valAddr, verbose, senderKey, pv) + if err != nil { + return fmt.Errorf("submitting attestation: %w", err) + } + + lastAttested = height + } + + // Wait before next poll + time.Sleep(time.Second / 10) + } + } +} + +// formatCommandArgs formats command arguments for verbose output +func formatCommandArgs(args []string) string { + var result string + for i, arg := range args { + if i > 0 { + result += " " + } + // Add quotes if the argument contains spaces + if containsSpace(arg) { + result += "\"" + arg + "\"" + } else { + result += arg + } + } + return result +} + +// containsSpace checks if a string contains any space character +func containsSpace(s string) bool { + for _, c := range s { + if c == ' ' || c == '\t' || c == '\n' || c == '\r' { + return true + } + } + return false +} + +var accSeq uint64 = 0 + +// broadcastTx executes a command to broadcast a transaction using the Cosmos SDK +func broadcastTx(ctx context.Context, chainID, nodeAddr string, msg proto.Message, privKey *secp256k1.PrivKey, verbose bool) (string, error) { + // Get validator address from private key + valAddr := sdk.ValAddress(privKey.PubKey().Address()) + + if verbose { + fmt.Printf("Broadcasting transaction for validator: %s\n", valAddr.String()) + } + + // Initialize the transaction config with the proper codec and sign modes + interfaceRegistry := codectypes.NewInterfaceRegistry() + authtypes.RegisterInterfaces(interfaceRegistry) + std.RegisterInterfaces(interfaceRegistry) + protoCodec := codec.NewProtoCodec(interfaceRegistry) + txConfig := authtx.NewTxConfig(protoCodec, authtx.DefaultSignModes) + + // Create proper transaction + txBuilder := txConfig.NewTxBuilder() + err := txBuilder.SetMsgs(msg) + if err != nil { + return "", fmt.Errorf("setting messages: %w", err) + } + + txBuilder.SetGasLimit(200000) + txBuilder.SetFeeAmount(sdk.NewCoins()) + txBuilder.SetMemo("") + // Get account info from node + clientCtx, err := createClientContext(nodeAddr, chainID, txConfig) + if err != nil { + return "", fmt.Errorf("creating client context: %w", err) + } + clientCtx = clientCtx.WithInterfaceRegistry(interfaceRegistry).WithCodec(protoCodec) + addr := sdk.AccAddress(privKey.PubKey().Address()) + accountRetriever := authtypes.AccountRetriever{} + account, err := accountRetriever.GetAccount(clientCtx, addr) + if err != nil { + return "", fmt.Errorf("getting account: %w", err) + } + fmt.Printf("+++ chainid: %s, GetAccountNumber: %d\n", chainID, account.GetAccountNumber()) + // Sign transaction using account sequence + if accSeq == 0 { + accSeq = account.GetSequence() + } + + signerData := authsigning.SignerData{ + Address: addr.String(), + ChainID: chainID, + AccountNumber: account.GetAccountNumber(), + Sequence: accSeq, + PubKey: privKey.PubKey(), + } + + // For SIGN_MODE_DIRECT, we need to set a nil signature first + // to generate the correct sign bytes + sigData := signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + Signature: nil, + } + sig := signing.SignatureV2{ + PubKey: privKey.PubKey(), + Data: &sigData, + Sequence: accSeq, + } + + err = txBuilder.SetSignatures(sig) + if err != nil { + return "", fmt.Errorf("setting nil signatures: %w", err) + } + + // Now get the bytes to sign and create the real signature + signBytes, err := authsigning.GetSignBytesAdapter( + ctx, + txConfig.SignModeHandler(), + signing.SignMode_SIGN_MODE_DIRECT, + signerData, + txBuilder.GetTx(), + ) + if err != nil { + return "", fmt.Errorf("getting sign bytes: %w", err) + } + + // Sign those bytes + signature, err := privKey.Sign(signBytes) + if err != nil { + return "", fmt.Errorf("signing bytes: %w", err) + } + + // Construct the SignatureV2 struct with the actual signature + sigData = signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + Signature: signature, + } + sig = signing.SignatureV2{ + PubKey: privKey.PubKey(), + Data: &sigData, + Sequence: accSeq, + } + + err = txBuilder.SetSignatures(sig) + if err != nil { + return "", fmt.Errorf("setting signatures: %w", err) + } + + txBytes, err := txConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return "", fmt.Errorf("encoding transaction: %w", err) + } + + // Set broadcast mode to sync + clientCtx = clientCtx.WithBroadcastMode("sync") + + // Broadcast the transaction using the cosmos-sdk library + resp, err := clientCtx.BroadcastTx(txBytes) + if err != nil { + return "", fmt.Errorf("broadcasting transaction: %w", err) + } + accSeq++ + // Check if the transaction was successful + if resp.Code != 0 { + return "", fmt.Errorf("transaction failed with code %d: %s", resp.Code, resp.RawLog) + } + + if verbose { + fmt.Printf("Transaction successful with hash: %s\n", resp.TxHash) + } + + return resp.TxHash, nil +} + +// createClientContext creates a client.Context with the necessary fields for broadcasting transactions +func createClientContext(nodeAddr, chainID string, txConfig client.TxConfig) (client.Context, error) { + // Create a CometRPC client + rpcClient, err := client.NewClientFromNode(nodeAddr) + if err != nil { + return client.Context{}, fmt.Errorf("creating RPC client: %w", err) + } + + // Create a client.Context with the necessary fields + clientCtx := client.Context{ + Client: rpcClient, + ChainID: chainID, + TxConfig: txConfig, + BroadcastMode: "sync", + Output: os.Stdout, + AccountRetriever: authtypes.AccountRetriever{}, + } + + return clientCtx, nil +} + +// createPrivateKeyFromMnemonic derives a private key from a mnemonic using the standard +// BIP44 HD path (m/44'/118'/0'/0/0) +func createPrivateKeyFromMnemonic(mnemonic string) (*secp256k1.PrivKey, error) { + // Create master key from mnemonic + derivedPriv, err := hd.Secp256k1.Derive()( + mnemonic, + "", + hd.CreateHDPath(118, 0, 0).String(), // Cosmos HD path + ) + if err != nil { + return nil, fmt.Errorf("failed to derive private key: %w", err) + } + return &secp256k1.PrivKey{Key: derivedPriv}, nil +} + +// submitAttestation creates and submits an attestation for a block using direct RPC +func submitAttestation( + ctx context.Context, + chainID, node, home string, + height int64, + appHash []byte, + valAddr sdk.ValAddress, + verbose bool, + senderKey *secp256k1.PrivKey, + pv *pvm.FilePV, +) error { + // Create the vote + vote := &cmtproto.Vote{ + Type: cmtproto.PrecommitType, + ValidatorAddress: pv.GetAddress(), + Height: height, + Round: 0, + BlockID: cmtproto.BlockID{Hash: appHash, PartSetHeader: cmtproto.PartSetHeader{Total: 1, Hash: appHash}}, + Timestamp: time.Now(), + } + var err error + err = pv.SignVote(chainID, vote) + if err != nil { + return fmt.Errorf("sign vote: %w", err) + } + + voteBytes, err := proto.Marshal(vote) + if err != nil { + return fmt.Errorf("marshal vote: %w", err) + } + + msg := networktypes.NewMsgAttest( + valAddr.String(), + height, + voteBytes, + ) + + txHash, err := broadcastTx(ctx, chainID, node, msg, senderKey, verbose) + if err != nil { + return fmt.Errorf("broadcast attest tx: %w", err) + } + + fmt.Printf("Attestation submitted with hash: %s\n", txHash) + return nil +} diff --git a/hack/download.sh b/hack/download.sh new file mode 100755 index 0000000..5f30eaa --- /dev/null +++ b/hack/download.sh @@ -0,0 +1,101 @@ +#!/bin/bash + + +HERMES_VERSION=${1:-"v1.13.1"} +GAIAD_VERSION=${2:-"v24.0.0"} +COSMOS_RELAYER_VERSION=${3:-"v2.6.0"} + +ARCH=$(uname -m) +OS=$(uname -s) + +case "$ARCH" in + "arm64") + ARCH_LABEL="aarch64" + ;; + "x86_64") + ARCH_LABEL="x86_64" + ;; + *) + echo "Unsupported architecture: $ARCH" + exit 1 + ;; +esac + +case "$OS" in + "Darwin") + OS_LABEL="apple-darwin" + ;; + "Linux") + OS_LABEL="unknown-linux-gnu" + ;; + *) + echo "Unsupported operating system: $OS" + exit 1 + ;; +esac + +HERMES_URL="https://github.com/informalsystems/hermes/releases/download/$HERMES_VERSION/hermes-$HERMES_VERSION-${ARCH_LABEL}-${OS_LABEL}.tar.gz" +GAIAD_URL="https://github.com/cosmos/gaia/releases/download/$GAIAD_VERSION/gaiad-$GAIAD_VERSION-darwin-$ARCH" + +# For Cosmos Relayer, we need to map the architecture differently +COSMOS_RELAYER_ARCH="amd64" +if [ "$ARCH" == "arm64" ]; then + COSMOS_RELAYER_ARCH="arm64" +fi + +COSMOS_RELAYER_OS="linux" +if [ "$OS" == "Darwin" ]; then + COSMOS_RELAYER_OS="darwin" +fi + +COSMOS_RELAYER_URL="https://github.com/cosmos/relayer/releases/download/$COSMOS_RELAYER_VERSION/Cosmos.Relayer_${COSMOS_RELAYER_VERSION#v}_${COSMOS_RELAYER_OS}_${COSMOS_RELAYER_ARCH}.tar.gz" + +if [ "$OS" == "Linux" ]; then + GAIAD_URL="https://github.com/cosmos/gaia/releases/download/$GAIAD_VERSION/gaiad-$GAIAD_VERSION-linux-amd64" +fi + +# Define output directories +DOWNLOAD_DIR="./downloads" +mkdir -p "$DOWNLOAD_DIR" + +# Function to download a file +download_file() { + local url=$1 + local output_path=$2 + + echo "Downloading: $url" + curl -L -o "$output_path" "$url" + if [ $? -ne 0 ]; then + echo "Failed to download $url" + exit 1 + fi +} + +# Download hermes +HERMES_ARCHIVE="$DOWNLOAD_DIR/hermes.tar.gz" +download_file "$HERMES_URL" "$HERMES_ARCHIVE" + +# Extract hermes if tar.gz +if [[ "$HERMES_ARCHIVE" == *.tar.gz ]]; then + echo "Extracting: $HERMES_ARCHIVE" + tar -xzvf "$HERMES_ARCHIVE" -C "$DOWNLOAD_DIR" +elif [[ "$HERMES_ARCHIVE" == *.zip ]]; then + echo "Extracting: $HERMES_ARCHIVE" + unzip "$HERMES_ARCHIVE" -d "$DOWNLOAD_DIR" +fi + +GAIAD_BINARY="$DOWNLOAD_DIR/gaiad" +download_file "$GAIAD_URL" "$GAIAD_BINARY" + +# Make gaiad binary executable +chmod +x "$GAIAD_BINARY" + +# Download Cosmos Relayer +COSMOS_RELAYER_ARCHIVE="$DOWNLOAD_DIR/cosmos-relayer.tar.gz" +download_file "$COSMOS_RELAYER_URL" "$COSMOS_RELAYER_ARCHIVE" + +# Extract Cosmos Relayer +echo "Extracting: $COSMOS_RELAYER_ARCHIVE" +tar -xzvf "$COSMOS_RELAYER_ARCHIVE" --strip-components=1 -C "$DOWNLOAD_DIR" + +echo "Hermes, Gaiad, and Cosmos Relayer downloaded successfully to $DOWNLOAD_DIR" diff --git a/hack/ibc-connection-hermes.sh b/hack/ibc-connection-hermes.sh new file mode 100755 index 0000000..5625e8a --- /dev/null +++ b/hack/ibc-connection-hermes.sh @@ -0,0 +1,131 @@ +#!/bin/bash +set -e + +# Configuration variables +GAIA_CHAIN_ID="localnet-1" +WORDLED_CHAIN_ID="rollkitnet-1" + +GAIA_RPC="http://localhost:26654" +WORDLED_RPC="http://localhost:26657" + +GAIA_GRPC="http://localhost:9091" +WORDLED_GRPC="http://localhost:9090" + +GAIA_DENOM="stake" +WORDLED_DENOM="stake" + +RELAYER_WALLET="relayer" # Name of relayer account + +# Hardcoded mnemonic (must match initialization scripts) +RELAYER_MNEMONIC="reject camp lock magic dragon degree loop ignore quantum verify invest primary object afraid crane unveil parrot jelly rubber risk mirror globe torch category" + + +# Logging functions +log_info() { + echo "[INFO] $1" +} + +log_success() { + echo "[SUCCESS] $1" +} + +log_error() { + echo "[ERROR] $1" >&2 +} + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +HERMES_BIN="${HERMES_BIN:-$CURRENT_DIR/downloads/hermes}" + +# Verify Hermes is installed +if [ ! -f "$HERMES_BIN" ]; then + log_error "Hermes not found at $HERMES_BIN" + exit 1 +fi +log_success "Hermes installed" + +CONFIG_DIR="${CURRENT_DIR}/testnet/hermes" +rm -rf "$CONFIG_DIR" +mkdir -p "$CONFIG_DIR" + +# Generate configuration file (config.toml) +CONFIG_FILE="$CONFIG_DIR/config.toml" +log_info "Generating Hermes configuration at $CONFIG_FILE..." + +cat < "$CONFIG_FILE" +[global] +log_level = "trace" + +[mode] + [mode.clients] + enabled = true + refresh = true + misbehaviour = true + [mode.connections] + enabled = true + [mode.channels] + enabled = true + [mode.packets] + enabled = true + +[[chains]] +id = "$GAIA_CHAIN_ID" +rpc_addr = "$GAIA_RPC" +grpc_addr = "$GAIA_GRPC" +event_source = { mode = "pull", interval = "1s", max_retries = 4 } +store_prefix = "ibc" +account_prefix = "cosmos" +key_name = "$RELAYER_WALLET" +gas_price = { price = 3.5, denom = "stake" } +gas_multiplier = 1.4 +rpc_timeout = "10s" +trusting_period = "503h" +clock_drift = "10s" +key_store_folder = "$CONFIG_DIR/$GAIA_CHAIN_ID-keys" + +[[chains]] +id = "$WORDLED_CHAIN_ID" +rpc_addr = "$WORDLED_RPC" +grpc_addr = "$WORDLED_GRPC" +store_prefix = "ibc" +event_source = { mode = "pull", interval = "1s", max_retries = 4 } +account_prefix = "gm" +key_name = "$RELAYER_WALLET" +gas_price = { price = 0.025, denom = "stake" } +gas_multiplier = 1.4 +rpc_timeout = "10s" +trusting_period = "503h" +clock_drift = "10s" +key_store_folder = "$CONFIG_DIR/$WORDLED_CHAIN_ID-keys" +EOF + +log_success "Configuration file generated" + +# Import keys to relayer (Hermes) using mnemonic +TMP_MNEMONIC=$(mktemp) +echo "$RELAYER_MNEMONIC" > "$TMP_MNEMONIC" + +log_info "Importing key for $GAIA_CHAIN_ID..." +"$HERMES_BIN" --config "$CONFIG_FILE" keys add --chain "$GAIA_CHAIN_ID" --mnemonic-file "$TMP_MNEMONIC" +log_success "Key imported for $GAIA_CHAIN_ID" + +log_info "Importing key for $WORDLED_CHAIN_ID..." +"$HERMES_BIN" --config "$CONFIG_FILE" keys add --chain "$WORDLED_CHAIN_ID" --mnemonic-file "$TMP_MNEMONIC" +log_success "Key imported for $WORDLED_CHAIN_ID" + +rm "$TMP_MNEMONIC" + +# Show configured addresses (optional) +log_info "Showing configured addresses:" +"$HERMES_BIN" --config "$CONFIG_FILE" keys list --chain "$GAIA_CHAIN_ID" +"$HERMES_BIN" --config "$CONFIG_FILE" keys list --chain "$WORDLED_CHAIN_ID" + +# Create IBC channel between chains +log_info "Creating IBC channel between $GAIA_CHAIN_ID and $WORDLED_CHAIN_ID..." + "$HERMES_BIN" --config "$CONFIG_FILE" create channel --a-chain "$GAIA_CHAIN_ID" --a-port transfer \ + --b-chain "$WORDLED_CHAIN_ID" --b-port transfer \ + --new-client-connection --yes +log_success "IBC channel created" + +# Start Hermes +log_info "Starting Hermes..." +"$HERMES_BIN" --config "$CONFIG_FILE" start \ No newline at end of file diff --git a/hack/ibc-connection-rly.sh b/hack/ibc-connection-rly.sh new file mode 100755 index 0000000..fca0d88 --- /dev/null +++ b/hack/ibc-connection-rly.sh @@ -0,0 +1,135 @@ +#!/bin/bash +set -e + +# Configuration variables +GAIA_CHAIN_ID="localnet-1" +WORDLED_CHAIN_ID="rollkitnet-1" + +GAIA_RPC="http://localhost:26654" +WORDLED_RPC="http://localhost:26657" + +GAIA_GRPC="http://localhost:9091" +WORDLED_GRPC="http://localhost:9090" + +GAIA_DENOM="stake" +WORDLED_DENOM="stake" + +RELAYER_WALLET="relayer" # Name of relayer account + +# Hardcoded mnemonic (must match initialization scripts) +RELAYER_MNEMONIC="reject camp lock magic dragon degree loop ignore quantum verify invest primary object afraid crane unveil parrot jelly rubber risk mirror globe torch category" + +# Logging functions +log_info() { + echo "[INFO] $1" +} + +log_success() { + echo "[SUCCESS] $1" +} + +log_error() { + echo "[ERROR] $1" >&2 +} + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RELAYER_BIN="${RELAYER_BIN:-$CURRENT_DIR/downloads/rly}" + + +# Verify Go IBC Relayer is installed +if ! command -v $RELAYER_BIN &> /dev/null; then + log_error "Go IBC Relayer not found. Please install it with: go install github.com/cosmos/relayer/v2@latest" + exit 1 +fi +log_success "Go IBC Relayer installed" + +CONFIG_DIR="${CURRENT_DIR}/testnet/go-relayer" +rm -rf "$CONFIG_DIR" +mkdir -p "$CONFIG_DIR" + +# Initialize the relayer configuration +log_info "Initializing Go IBC Relayer configuration..." +$RELAYER_BIN config init --home "$CONFIG_DIR" +log_success "Relayer configuration initialized" + +# Add chains to the relayer configuration +log_info "Adding $GAIA_CHAIN_ID chain to the relayer..." +cat < gaia-config.json +{ + "type": "cosmos", + "value": { + "key": "$RELAYER_WALLET", + "chain-id": "$GAIA_CHAIN_ID", + "rpc-addr": "$GAIA_RPC", + "grpc-addr": "$GAIA_GRPC", + "account-prefix": "cosmos", + "keyring-backend": "test", + "dynamic-gas-price": true, + "gas-adjustment": 1.4, + "gas-prices": "3.5$GAIA_DENOM", + "debug": true, + "timeout": "10s", + "output-format": "json", + "sign-mode": "direct", + "trusting-period": "504h" + } +} +EOF +$RELAYER_BIN chains add $GAIA_CHAIN_ID -f gaia-config.json --home "$CONFIG_DIR" +log_success "$GAIA_CHAIN_ID chain added" + +log_info "Adding $WORDLED_CHAIN_ID chain to the relayer..." +cat < wordled-config.json +{ + "type": "cosmos", + "value": { + "key": "$RELAYER_WALLET", + "chain-id": "$WORDLED_CHAIN_ID", + "rpc-addr": "$WORDLED_RPC", + "grpc-addr": "$WORDLED_GRPC", + "account-prefix": "gm", + "keyring-backend": "test", + "dynamic-gas-price": true, + "gas-adjustment": 1.4, + "gas-prices": "0.025$WORDLED_DENOM", + "debug": true, + "timeout": "10s", + "output-format": "json", + "sign-mode": "direct", + "trusting-period": "504h" + } +} +EOF +$RELAYER_BIN chains add $WORDLED_CHAIN_ID -f wordled-config.json --home "$CONFIG_DIR" +log_success "$WORDLED_CHAIN_ID chain added" + +# Import keys to relayer using mnemonic +log_info "Importing key for $GAIA_CHAIN_ID..." +$RELAYER_BIN keys restore $GAIA_CHAIN_ID $RELAYER_WALLET "$RELAYER_MNEMONIC" --home "$CONFIG_DIR" +log_success "Key imported for $GAIA_CHAIN_ID" + +log_info "Importing key for $WORDLED_CHAIN_ID..." +$RELAYER_BIN keys restore $WORDLED_CHAIN_ID $RELAYER_WALLET "$RELAYER_MNEMONIC" --home "$CONFIG_DIR" +log_success "Key imported for $WORDLED_CHAIN_ID" +# Clean up config files +rm gaia-config.json wordled-config.json + +# Show configured addresses (optional) +log_info "Showing configured addresses:" +$RELAYER_BIN keys list $GAIA_CHAIN_ID --home "$CONFIG_DIR" +$RELAYER_BIN keys list $WORDLED_CHAIN_ID --home "$CONFIG_DIR" + +# Create a path between the chains +PATH_NAME="gaia-rollkit" +log_info "Creating path $PATH_NAME between $GAIA_CHAIN_ID and $WORDLED_CHAIN_ID..." +$RELAYER_BIN paths new $GAIA_CHAIN_ID $WORDLED_CHAIN_ID $PATH_NAME --home "$CONFIG_DIR" +log_success "Path created" + +# Create IBC channel between chains +log_info "Creating IBC connection and channel between $GAIA_CHAIN_ID and $WORDLED_CHAIN_ID..." +$RELAYER_BIN tx link $PATH_NAME --home "$CONFIG_DIR" --src-port transfer --dst-port transfer --order unordered --log-level=DEBUG --debug +log_success "IBC connection and channel created" + +# Start the relayer +log_info "Starting Go IBC Relayer..." +$RELAYER_BIN start $PATH_NAME --home "$CONFIG_DIR" diff --git a/hack/ics20-token-transfer.sh b/hack/ics20-token-transfer.sh new file mode 100755 index 0000000..b08b0ca --- /dev/null +++ b/hack/ics20-token-transfer.sh @@ -0,0 +1,166 @@ +#!/bin/bash +set -e + +# Configuration variables (matching ibc-connection-hermes.sh) +GAIA_CHAIN_ID="localnet-1" +ROLLKIT_CHAIN_ID="rollkitnet-1" + +GAIA_RPC="http://localhost:26654" +ROLLKIT_RPC="http://localhost:26657" + +GAIA_DENOM="stake" +ROLLKIT_DENOM="stake" + +GAIA_KEY_NAME="bob" +ROLLKIT_KEY_NAME="carl" + +# IBC channel information +CHANNEL_ID="" +TRANSFER_PORT="transfer" + +# Logging functions +log_info() { + echo "[INFO] $1" +} + +log_success() { + echo "[SUCCESS] $1" +} + +log_error() { + echo "[ERROR] $1" >&2 +} + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +GAIAD_BIN="${GAIAD_BIN:-$CURRENT_DIR/downloads/gaiad}" +GMD_BIN="gmd" +ROLLKIT_HOME="${1:-"${CURRENT_DIR}/testnet/gm"}" + +# Verify binaries are installed +if [ ! -f "$GAIAD_BIN" ]; then + log_error "Gaiad not found at $GAIAD_BIN" + exit 1 +fi +log_success "Gaiad installed" + +if ! command -v $GMD_BIN &> /dev/null; then + log_error "gmd binary not found in PATH" + exit 1 +fi +log_success "gmd installed" + +# Get validator addresses +log_info "Getting user addresses..." +USER_ADDRESS_GAIA=$($GAIAD_BIN keys show $GAIA_KEY_NAME -a --keyring-backend test --home "$CURRENT_DIR/testnet/gaia") +USER_ADDRESS_ROLLKIT=$($GMD_BIN keys show $ROLLKIT_KEY_NAME -a --keyring-backend test --home "$ROLLKIT_HOME") + +log_info "Gaia user address: $USER_ADDRESS_GAIA" +log_info "Rollkit user address: $USER_ADDRESS_ROLLKIT" + +# Check initial balances +log_info "Checking initial balances..." +log_info "Gaia $GAIA_KEY_NAME balance:" +$GAIAD_BIN q bank balances $USER_ADDRESS_GAIA --node $GAIA_RPC --home "$CURRENT_DIR/testnet/gaia" + +log_info "Rollkit $ROLLKIT_KEY_NAME balance:" +$GMD_BIN q bank balances $USER_ADDRESS_ROLLKIT --node $ROLLKIT_RPC --home "$ROLLKIT_HOME" + +# Get channel ID +log_info "Getting IBC channel ID..." +CHANNEL_INFO=$($GAIAD_BIN q ibc channel channels --node $GAIA_RPC --home "$CURRENT_DIR/testnet/gaia" -o json) +CHANNEL_ID=$(echo $CHANNEL_INFO | jq -r '.channels[0].channel_id') + +if [ -z "$CHANNEL_ID" ] || [ "$CHANNEL_ID" == "null" ]; then + log_error "Failed to get channel ID. Make sure IBC connection is established." + exit 1 +fi + +log_info "Using IBC channel: $CHANNEL_ID" + +# Transfer Rollkit tokens to Gaia +ROLLKIT_TRANSFER_AMOUNT="101" + +log_info "Transferring ${ROLLKIT_TRANSFER_AMOUNT}${ROLLKIT_DENOM} from Rollkit to Gaia..." +TX_HASH=$($GMD_BIN tx ibc-transfer transfer $TRANSFER_PORT $CHANNEL_ID $USER_ADDRESS_GAIA ${ROLLKIT_TRANSFER_AMOUNT}${ROLLKIT_DENOM} \ + --from ${ROLLKIT_KEY_NAME} \ + --chain-id $ROLLKIT_CHAIN_ID \ + --node $ROLLKIT_RPC \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" \ + --gas auto \ + --gas-adjustment 1.4 \ + --gas-prices 1stake \ + --yes -o json | jq -r '.txhash') + +log_info "Querying gmd tx... $TX_HASH" +for i in {1..10}; do + if ! tx_result=$($GMD_BIN q tx --type=hash "$TX_HASH" -o json --home "$ROLLKIT_HOME" 2>/dev/null); then + sleep 1 + continue + fi +done +if [ "$(echo "$tx_result" | jq -r '.code')" != "0" ]; then + log_error "Transaction failed : $tx_result" + exit 1 +fi + + +# Check balances after Rollkit to Gaia transfer +log_info "Checking balances after Rollkit to Gaia transfer..." +log_info "Gaia user balance (should show IBC tokens):" +$GAIAD_BIN q bank balances $USER_ADDRESS_GAIA --node $GAIA_RPC --home "$CURRENT_DIR/testnet/gaia" + +log_info "Rollkit user balance:" +$GMD_BIN q bank balances $USER_ADDRESS_ROLLKIT --node $ROLLKIT_RPC --home "$ROLLKIT_HOME" + +# Transfer tokens from Gaia to Rollkit +TRANSFER_AMOUNT="102" +log_info "Transferring $TRANSFER_AMOUNT$GAIA_DENOM from Gaia to Rollkit..." +TX_HASH=$($GAIAD_BIN tx ibc-transfer transfer $TRANSFER_PORT $CHANNEL_ID $USER_ADDRESS_ROLLKIT ${TRANSFER_AMOUNT}${GAIA_DENOM} \ + --from ${GAIA_KEY_NAME} \ + --chain-id $GAIA_CHAIN_ID \ + --node $GAIA_RPC \ + --keyring-backend test \ + --home "$CURRENT_DIR/testnet/gaia" \ + --gas auto \ + --gas-adjustment 1.4 \ + --gas-prices 1stake \ + --yes -o json | jq -r '.txhash') + +log_info "Querying gaia tx... $TX_HASH" +for i in {1..20}; do + if ! tx_result=$($GAIAD_BIN q tx --type=hash "$TX_HASH" -o json --node $GAIA_RPC 2>/dev/null); then + echo "$tx_result" + sleep 1 + continue + fi +done +if [ "$(echo "$tx_result" | jq -r '.code')" != "0" ]; then + log_error "Transaction failed : $tx_result" + exit 1 +fi + +# Check balances after transfer to Rollkit +log_info "Checking balances after transfer to Rollkit..." +log_info "Gaia user balance:" +$GAIAD_BIN q bank balances $USER_ADDRESS_GAIA --node $GAIA_RPC --home "$CURRENT_DIR/testnet/gaia" + +log_info "Rollkit user balance (should show IBC tokens):" +ibc_tokens_visible=false + +for i in {1..${20}}; do + $GMD_BIN q bank balances $USER_ADDRESS_ROLLKIT --node $ROLLKIT_RPC --home "$ROLLKIT_HOME" + if $GMD_BIN q bank balances $USER_ADDRESS_ROLLKIT --node $ROLLKIT_RPC --home "$ROLLKIT_HOME" | grep -q "ibc"; then + log_success "IBC tokens are now visible in Rollkit balance" + ibc_tokens_visible=true + break + fi + sleep 1 +done + +if [ "$ibc_tokens_visible" = false ]; then + log_error "IBC tokens did not become visible within time frame" + exit 1 +fi + +log_success "ICS20 token transfer test completed!" diff --git a/hack/init-gaia.sh b/hack/init-gaia.sh new file mode 100755 index 0000000..e6fcc5a --- /dev/null +++ b/hack/init-gaia.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# Function for formatted logging +log() { + local color=$1 + local emoji=$2 + local message=$3 + shift 3 + printf "\e[${color}m${emoji} [$(date '+%T')] ${message}\e[0m\n" "$@" +} + +# Hardcoded mnemonic for validator account (igual que en Wordled) +VALIDATOR_MNEMONIC="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" +BOB_MNEMONIC="rack argue disorder flame appear broom smile effort one rubber buffalo suspect tool devote zebra between inhale trigger brief possible parrot nation expose place" +RELAYER_MNEMONIC="reject camp lock magic dragon degree loop ignore quantum verify invest primary object afraid crane unveil parrot jelly rubber risk mirror globe torch category" + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +GAIA_HOME=${1:-"${CURRENT_DIR}/testnet/gaia"} +GAIAD_BIN=${2:-"${CURRENT_DIR}/downloads/gaiad"} + +# Kill existing Gaia processes +log "31" "💀" "Checking for existing Gaia processes..." +GAIA_PID=$(pgrep gaiad) +if [ -n "$GAIA_PID" ]; then + log "31" "🔪" "Killing existing Gaia process (PID: $GAIA_PID)..." + kill -9 "$GAIA_PID" +else + log "32" "👌" "No existing Gaia process found." +fi + +# Clean previous configurations +log "32" "🔥" "Cleaning previous Gaia configurations..." +rm -rf "$GAIA_HOME" + +# 1. Initialize Gaia chain +log "36" "🆕" "Initializing Gaia chain..." +"$GAIAD_BIN" init my-node --chain-id localnet-1 --home "$GAIA_HOME" + +# 2. Create/Recover validator account usando la mnemónica fija +log "35" "👤" "Generating validator account from mnemonic..." +echo "$VALIDATOR_MNEMONIC" | "$GAIAD_BIN" keys add validator \ + --keyring-backend test \ + --home "$GAIA_HOME" \ + --recover > /dev/null 2>&1 +echo "$BOB_MNEMONIC" | "$GAIAD_BIN" keys add bob \ + --keyring-backend test \ + --home "$GAIA_HOME" \ + --recover > /dev/null 2>&1 +echo "$RELAYER_MNEMONIC" | "$GAIAD_BIN" keys add relayer \ + --keyring-backend test \ + --home "$GAIA_HOME" \ + --recover > /dev/null 2>&1 + +# 3. Add account to genesis +log "34" "📝" "Adding account to genesis..." +"$GAIAD_BIN" genesis add-genesis-account validator 10000000000000000stake --keyring-backend test --home "$GAIA_HOME" +"$GAIAD_BIN" genesis add-genesis-account bob 10000000000000000stake --keyring-backend test --home "$GAIA_HOME" +"$GAIAD_BIN" genesis add-genesis-account relayer 10000000000000000stake --keyring-backend test --home "$GAIA_HOME" + +# 4. Generate gentx +log "33" "📜" "Creating validator transaction..." +"$GAIAD_BIN" genesis gentx validator 1000000000stake \ + --chain-id localnet-1 \ + --keyring-backend test \ + --home "$GAIA_HOME" + +# 5. Collect gentxs +log "32" "📦" "Collecting genesis transactions..." +"$GAIAD_BIN" genesis collect-gentxs --home "$GAIA_HOME" + +# 6. Configure minimum gas prices +log "36" "⛽" "Setting minimum gas prices..." +sed -i.bak -E 's#minimum-gas-prices = ""#minimum-gas-prices = "0stake"#g' "$GAIA_HOME/config/app.toml" + +# 7. Modify consensus timeouts +log "35" "⏱️" "Adjusting consensus timeouts..." +sed -i.bak -E 's/timeout_commit = "5s"/timeout_commit = "1s"/g' "$GAIA_HOME/config/config.toml" +sed -i.bak -E 's/timeout_propose = "3s"/timeout_propose = "1s"/g' "$GAIA_HOME/config/config.toml" + +# 8. Start Gaia chain +log "34" "🚀" "Starting Gaia node..." +"$GAIAD_BIN" start --home "$GAIA_HOME" --minimum-gas-prices "0stake" --rpc.laddr tcp://0.0.0.0:26654 --rpc.pprof_laddr localhost:6061 --p2p.laddr tcp://0.0.0.0:26653 --grpc.address 0.0.0.0:9091 | tee "$GAIA_HOME/gaia.log" & +GAIA_PID=$! + +# Wait for initialization +log "33" "⏳" "Waiting for Gaia initialization (port 26654)..." +while ! nc -z localhost 26654; do + sleep 1 +done +log "32" "✅" "Gaia chain running successfully!" + +# Show recent logs +log "36" "📄" "Last lines of Gaia log:" +tail -n 5 "$GAIA_HOME/gaia.log" + +# Keep script alive +log "35" "👀" "Monitoring Gaia chain activity..." +wait $GAIA_PID \ No newline at end of file diff --git a/hack/run_gmd.sh b/hack/run_gmd.sh new file mode 100755 index 0000000..de0d7a1 --- /dev/null +++ b/hack/run_gmd.sh @@ -0,0 +1,147 @@ +#!/bin/bash +set -x + +# Function for cleanup on script interruption +cleanup() { + log "31" "🛑" "Cleaning up processes..." + if [ -n "$DA_PID" ]; then + kill -9 "$DA_PID" 2>/dev/null || true + fi + if [ -n "$ROLLKIT_PID" ]; then + kill -9 "$ROLLKIT_PID" 2>/dev/null || true + fi + exit 0 +} + +trap cleanup INT TERM + +# Function for formatted logging +log() { + local color=$1 + local emoji=$2 + local message=$3 + shift 3 + printf "\e[${color}m${emoji} [$(date '+%T')] ${message}\e[0m\n" "$@" +} + + +# Define paths +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROLLKIT_BIN="gmd" +ROLLKIT_HOME="${1:-"${CURRENT_DIR}/testnet/gm"}" +LOCAL_DA_PATH="${2:-"../../rollkit/build/local-da"}" +CHAIN_ID="${3:-"rollkitnet-1"}" + +# Clean previous configurations +log "32" "🔥" "Cleaning previous rollkit configurations..." +rm -rf "$ROLLKIT_HOME" + +"$ROLLKIT_BIN" init my-rollkit-node --chain-id "$CHAIN_ID" --home "$ROLLKIT_HOME" + +# Hardcoded mnemonic for validator account (igual que en Wordled) +VALIDATOR_MNEMONIC="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" +#ATTESTER_MNEMONIC="tennis sponsor brick almost coyote soup rib wisdom warm bean onion tray devote pretty crime grid rough boil wear december travel inch work note" +RELAYER_MNEMONIC="reject camp lock magic dragon degree loop ignore quantum verify invest primary object afraid crane unveil parrot jelly rubber risk mirror globe torch category" +USER_MNEMONIC="sport head real antique sad expect ignore feature claim manual heavy mouse coil rebuild police flag robust picture milk symptom suffer chuckle worry virus" + + +echo "$VALIDATOR_MNEMONIC" | "$ROLLKIT_BIN" keys add validator \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" \ + --recover > /dev/null 2>&1 + +#echo "$ATTESTER_MNEMONIC" | "$ROLLKIT_BIN" keys add attester \ +# --keyring-backend test \ +# --home "$ROLLKIT_HOME" \ +# --recover > /dev/null 2>&1 + +echo "$RELAYER_MNEMONIC" | "$ROLLKIT_BIN" keys add relayer \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" \ + --recover > /dev/null 2>&1 + +echo "$USER_MNEMONIC" | "$ROLLKIT_BIN" keys add carl \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" \ + --recover > /dev/null 2>&1 + +"$ROLLKIT_BIN" genesis add-genesis-account validator "10000000000000000stake" --keyring-backend test --home "$ROLLKIT_HOME" +#"$ROLLKIT_BIN" genesis add-genesis-account attester "10000000000000000stake" --keyring-backend test --home "$ROLLKIT_HOME" +"$ROLLKIT_BIN" genesis add-genesis-account relayer "10000000000000000stake" --keyring-backend test --home "$ROLLKIT_HOME" +"$ROLLKIT_BIN" genesis add-genesis-account carl "10000000000000000stake" --keyring-backend test --home "$ROLLKIT_HOME" + + +log "33" "📜" "Creating validator transaction..." +"$ROLLKIT_BIN" genesis gentx validator 1000000000stake \ + --chain-id "$CHAIN_ID" \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" + +# 5. Collect gentxs +log "32" "📦" "Collecting genesis transactions..." +"$ROLLKIT_BIN" genesis collect-gentxs --home "$ROLLKIT_HOME" + +# Set validator in consensus block +log "32" "🔄" "Setting validator in consensus block..." +# Extract validator address and pubkey from validator key file, then modify genesis file to set the validator +jq -r '.address as $addr | .pub_key | { + address: $addr, + pub_key: { type: "tendermint/PubKeyEd25519", value: .value }, + power: "1000", + name: "Rollkit Sequencer" +}' "$ROLLKIT_HOME/config/priv_validator_key.json" | \ +jq --slurpfile genesis "$ROLLKIT_HOME/config/genesis.json" \ + '. as $validator | ($genesis[0] | .consensus.validators += [$validator])' > \ + "$ROLLKIT_HOME/config/tmp_genesis.json" && \ +mv "$ROLLKIT_HOME/config/tmp_genesis.json" "$ROLLKIT_HOME/config/genesis.json" + +# 6. Configure minimum gas prices +log "33" "⛽" "Setting minimum gas prices..." +sed -i.bak -E 's#minimum-gas-prices = ""#minimum-gas-prices = "0stake"#g' "$ROLLKIT_HOME/config/app.toml" + +# 7. Modify consensus timeouts +log "34" "⏱️" "Adjusting consensus timeouts..." +sed -i.bak -E 's/timeout_commit = "5s"/timeout_commit = "1s"/g' "$ROLLKIT_HOME/config/config.toml" +sed -i.bak -E 's/timeout_propose = "3s"/timeout_propose = "1s"/g' "$ROLLKIT_HOME/config/config.toml" + +# enable api +sed -i.bak 's#enable = false#enable = true#g' "$ROLLKIT_HOME/config/app.toml" + + + +# Build and start local DA +echo "Building and starting local DA..." +# --- Kill previous local-da process if running --- +echo "Checking for existing local-da process on port 7980..." +# Find PID using lsof on the DA port (adjust if your DA uses a different port) +DA_PORT=7980 +EXISTING_PID=$(lsof -ti tcp:${DA_PORT}) + +if [ -n "$EXISTING_PID" ]; then + echo "Found existing processes on port $DA_PORT with PIDs: $EXISTING_PID. Killing..." + # Kill the process(es) + kill -9 $EXISTING_PID + # Allow a moment for the OS to release the port + sleep 2 +else + echo "No existing process found on port $DA_PORT." +fi +# --- End Kill previous local-da process --- + + +if [ ! -f "$LOCAL_DA_PATH" ]; then + echo "Error: local-da binary not found at $LOCAL_DA_PATH" + exit 1 +else + echo "Starting local DA in background..." + $LOCAL_DA_PATH & + DA_PID=$! + echo "Local DA started with PID $DA_PID" + sleep 2 # Give DA a moment to start +fi + +log "35" "🚀" "Starting ROLLKIT node..." +"$ROLLKIT_BIN" start --home "$ROLLKIT_HOME" --rollkit.node.aggregator --minimum-gas-prices "0stake" --rollkit.node.lazy_block_interval=150ms --rollkit.node.block_time=100ms --rollkit.da.block_time=500ms --pruning=nothing --rollkit.network.soft-confirmation --log_level=debug & +ROLLKIT_PID=$! +log "36" "✅" "ROLLKIT chain running successfully!" +wait $ROLLKIT_PID diff --git a/modules/network/keeper/fixtures_test.go b/modules/network/keeper/fixtures_test.go new file mode 100644 index 0000000..1959882 --- /dev/null +++ b/modules/network/keeper/fixtures_test.go @@ -0,0 +1,122 @@ +package keeper + +import ( + "context" + "maps" + "slices" + "strings" + "time" + + "cosmossdk.io/math" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/libp2p/go-libp2p/core/crypto" + + rollkittypes "github.com/rollkit/rollkit/types" + + "github.com/rollkit/go-execution-abci/modules/network/types" +) + +func HeaderFixture(signer *ed25519.PrivKey, appHash []byte, mutators ...func(*rollkittypes.SignedHeader)) *rollkittypes.SignedHeader { + header := rollkittypes.Header{ + BaseHeader: rollkittypes.BaseHeader{ + Height: 10, + Time: uint64(time.Now().UnixNano()), + ChainID: "testing", + }, + Version: rollkittypes.Version{Block: 1, App: 1}, + ProposerAddress: signer.PubKey().Address(), + AppHash: appHash, + DataHash: []byte("data_hash"), + ConsensusHash: []byte("consensus_hash"), + ValidatorHash: []byte("validator_hash"), + } + signedHeader := &rollkittypes.SignedHeader{ + Header: header, + Signature: appHash, + Signer: rollkittypes.Signer{PubKey: must(crypto.UnmarshalEd25519PublicKey(signer.PubKey().Bytes()))}, + } + for _, m := range mutators { + m(signedHeader) + } + return signedHeader +} + +func VoteFixture(myAppHash []byte, voteSigner *ed25519.PrivKey, mutators ...func(vote *cmtproto.Vote)) *cmtproto.Vote { + const chainID = "testing" + + vote := &cmtproto.Vote{ + Type: cmtproto.PrecommitType, + Height: 10, + Round: 0, + BlockID: cmtproto.BlockID{Hash: myAppHash, PartSetHeader: cmtproto.PartSetHeader{Total: 1, Hash: myAppHash}}, + Timestamp: time.Now().UTC(), + ValidatorAddress: voteSigner.PubKey().Address(), + ValidatorIndex: 0, + } + vote.Signature = must(voteSigner.Sign(cmttypes.VoteSignBytes(chainID, vote))) + + for _, m := range mutators { + m(vote) + } + return vote +} + +var _ types.StakingKeeper = &MockStakingKeeper{} + +type MockStakingKeeper struct { + activeSet map[string]stakingtypes.Validator +} + +func NewMockStakingKeeper() MockStakingKeeper { + return MockStakingKeeper{ + activeSet: make(map[string]stakingtypes.Validator), + } +} + +func (m *MockStakingKeeper) SetValidator(ctx context.Context, validator stakingtypes.Validator) error { + m.activeSet[validator.GetOperator()] = validator + return nil +} +func (m MockStakingKeeper) GetAllValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { + return slices.SortedFunc(maps.Values(m.activeSet), func(v1 stakingtypes.Validator, v2 stakingtypes.Validator) int { + return strings.Compare(v1.OperatorAddress, v2.OperatorAddress) + }), nil +} +func (m MockStakingKeeper) GetValidator(ctx context.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, err error) { + // First try to find the validator by address + validator, found := m.activeSet[addr.String()] + if found { + return validator, nil + } + + //// If not found by address, try to find by public key address + //addrStr := addr.String() + //for valAddrStr, pubKey := range m.pubKeys { + // if pubKey.Address().String() == addrStr { + // validator, found = m.activeSet[valAddrStr] + // if found { + // return validator, nil + // } + // } + //} + + return validator, sdkerrors.ErrNotFound +} + +func (m MockStakingKeeper) GetLastValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { + for _, validator := range m.activeSet { + if validator.IsBonded() { // Assuming IsBonded() identifies if a validator is in the last validators + validators = append(validators, validator) + } + } + return +} + +func (m MockStakingKeeper) GetLastTotalPower(ctx context.Context) (math.Int, error) { + return math.NewInt(int64(len(m.activeSet))), nil +} diff --git a/modules/network/keeper/grpc_query.go b/modules/network/keeper/grpc_query.go index ca53e7b..e1c87a9 100644 --- a/modules/network/keeper/grpc_query.go +++ b/modules/network/keeper/grpc_query.go @@ -190,3 +190,36 @@ func (q *queryServer) SoftConfirmationStatus(c context.Context, req *types.Query QuorumFraction: q.keeper.GetParams(ctx).QuorumFraction, }, nil } + +// ValidatorSignature queries the signature of a validator for a specific height +func (q *queryServer) ValidatorSignature(c context.Context, req *types.QueryValidatorSignatureRequest) (*types.QueryValidatorSignatureResponse, error) { + // TODO (Alex): refactor to vote + + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + fmt.Printf("+++ enpoint ValidatorSignature addr: %X, height: %d\n", []byte(req.Validator), req.BlockHeight) + + ctx := sdk.UnwrapSDKContext(c) + + signature, err := q.keeper.GetVote(ctx, req.BlockHeight, []byte(req.Validator)) + if err != nil { + if errors.Is(err, collections.ErrNotFound) { + return &types.QueryValidatorSignatureResponse{ + Signature: nil, + Found: false, + }, nil + } + return nil, status.Error(codes.Internal, fmt.Sprintf("failed to get signature: %v", err)) + } + + voteBz, err := signature.Marshal() + if err != nil { + return nil, status.Error(codes.Internal, fmt.Sprintf("marshal vote: %v", err)) + } + return &types.QueryValidatorSignatureResponse{ + Signature: voteBz, + Found: true, + }, nil +} diff --git a/modules/network/keeper/keeper.go b/modules/network/keeper/keeper.go index 05d22a8..4b0c017 100644 --- a/modules/network/keeper/keeper.go +++ b/modules/network/keeper/keeper.go @@ -3,6 +3,7 @@ package keeper import ( "errors" "fmt" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "cosmossdk.io/collections" "cosmossdk.io/core/store" @@ -29,7 +30,7 @@ type Keeper struct { AttestationBitmap collections.Map[int64, []byte] EpochBitmap collections.Map[uint64, []byte] AttesterSet collections.KeySet[string] - Signatures collections.Map[collections.Pair[int64, string], []byte] + Votes collections.Map[collections.Pair[int64, []byte], cmtproto.Vote] StoredAttestationInfo collections.Map[int64, types.AttestationBitmap] Params collections.Item[types.Params] Schema collections.Schema @@ -59,7 +60,7 @@ func NewKeeper( AttestationBitmap: collections.NewMap(sb, types.AttestationBitmapPrefix, "attestation_bitmap", collections.Int64Key, collections.BytesValue), EpochBitmap: collections.NewMap(sb, types.EpochBitmapPrefix, "epoch_bitmap", collections.Uint64Key, collections.BytesValue), AttesterSet: collections.NewKeySet(sb, types.AttesterSetPrefix, "attester_set", collections.StringKey), - Signatures: collections.NewMap(sb, types.SignaturePrefix, "signatures", collections.PairKeyCodec(collections.Int64Key, collections.StringKey), collections.BytesValue), + Votes: collections.NewMap(sb, types.SignaturePrefix, "votes", collections.PairKeyCodec(collections.Int64Key, collections.BytesKey), codec.CollValue[cmtproto.Vote](cdc)), StoredAttestationInfo: collections.NewMap(sb, types.StoredAttestationInfoPrefix, "stored_attestation_info", collections.Int64Key, codec.CollValue[types.AttestationBitmap](cdc)), // Initialize new collection Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)), } @@ -309,17 +310,14 @@ func (k Keeper) PruneOldBitmaps(ctx sdk.Context, currentEpoch uint64) error { return nil } -// SetSignature stores the vote signature for a given height and validator -func (k Keeper) SetSignature(ctx sdk.Context, height int64, validatorAddr string, signature []byte) error { - return k.Signatures.Set(ctx, collections.Join(height, validatorAddr), signature) -} +// SetVote stores the vote signature for a given height and validator +func (k Keeper) SetVote(ctx sdk.Context, height int64, validatorAddr []byte, signature cmtproto.Vote) error { + fmt.Printf("+++ SetVote addr: %X, height: %d\n", validatorAddr, height) -// GetSignature retrieves the vote signature for a given height and validator -func (k Keeper) GetSignature(ctx sdk.Context, height int64, validatorAddr string) ([]byte, error) { - return k.Signatures.Get(ctx, collections.Join(height, validatorAddr)) + return k.Votes.Set(ctx, collections.Join(height, validatorAddr), signature) } -// HasSignature checks if a signature exists for a given height and validator -func (k Keeper) HasSignature(ctx sdk.Context, height int64, validatorAddr string) (bool, error) { - return k.Signatures.Has(ctx, collections.Join(height, validatorAddr)) +// GetVote retrieves the vote signature for a given height and validator +func (k Keeper) GetVote(ctx sdk.Context, height int64, validatorAddr []byte) (cmtproto.Vote, error) { + return k.Votes.Get(ctx, collections.Join(height, validatorAddr)) } diff --git a/modules/network/keeper/msg_server.go b/modules/network/keeper/msg_server.go index 48176c7..84c1d3a 100644 --- a/modules/network/keeper/msg_server.go +++ b/modules/network/keeper/msg_server.go @@ -1,16 +1,19 @@ package keeper import ( + "bytes" "context" - "errors" "fmt" "cosmossdk.io/collections" sdkerr "cosmossdk.io/errors" "cosmossdk.io/math" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/gogoproto/proto" "github.com/rollkit/go-execution-abci/modules/network/types" ) @@ -30,33 +33,61 @@ var _ types.MsgServer = msgServer{} func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.MsgAttestResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if k.GetParams(ctx).SignMode == types.SignMode_SIGN_MODE_CHECKPOINT && - !k.IsCheckpointHeight(ctx, msg.Height) { - return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "height %d is not a checkpoint", msg.Height) - } - has, err := k.IsInAttesterSet(ctx, msg.Validator) - if err != nil { - return nil, sdkerr.Wrapf(err, "in attester set") + if err := k.validateAttestation(ctx, msg); err != nil { + return nil, err } - if !has { - return nil, sdkerr.Wrapf(sdkerrors.ErrUnauthorized, "validator %s not in attester set", msg.Validator) + println("+++ 1") + // can vote only for the last epoch + if delta := ctx.BlockHeight() - msg.Height; delta < 0 || delta > int64(k.GetParams(ctx).EpochLength)*2 { // todo (Alex): does factor 2 make sense? + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "exceeded voting window: %d blocks", delta) } - index, found := k.GetValidatorIndex(ctx, msg.Validator) + println("+++ 2") + valIndexPos, found := k.GetValidatorIndex(ctx, msg.Validator) if !found { return nil, sdkerr.Wrapf(sdkerrors.ErrNotFound, "validator index not found for %s", msg.Validator) } - // todo (Alex): we need to set a limit to not have validators attest old blocks. Also make sure that this relates with - // the retention period for pruning - bitmap, err := k.GetAttestationBitmap(ctx, msg.Height) - if err != nil && !errors.Is(err, collections.ErrNotFound) { - return nil, sdkerr.Wrap(err, "get attestation bitmap") + println("+++ 3") + vote, err := k.verifyVote(ctx, msg) + if err != nil { + return nil, err } - if bitmap == nil { + + println("+++ 4") + if err := k.updateAttestationBitmap(ctx, msg, valIndexPos); err != nil { + return nil, sdkerr.Wrap(err, "update attestation bitmap") + } + + println("+++ 5") + if err := k.SetVote(ctx, msg.Height, vote.ValidatorAddress, *vote); err != nil { + println("+++ 5b") + return nil, sdkerr.Wrap(err, "store signature") + } + + println("+++ 6") + if err := k.updateEpochBitmap(ctx, uint64(msg.Height), valIndexPos); err != nil { + return nil, err + } + + println("+++ 7") + // Emit event + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.TypeMsgAttest, + sdk.NewAttribute("validator", msg.Validator), + sdk.NewAttribute("height", math.NewInt(msg.Height).String()), + ), + ) + return &types.MsgAttestResponse{}, nil +} + +func (k msgServer) updateEpochBitmap(ctx sdk.Context, votedEpoch uint64, index uint16) error { + epochBitmap := k.GetEpochBitmap(ctx, votedEpoch) + if epochBitmap == nil { validators, err := k.stakingKeeper.GetLastValidators(ctx) if err != nil { - return nil, err + return err } numValidators := 0 for _, v := range validators { @@ -64,32 +95,43 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M numValidators++ } } - bitmap = k.bitmapHelper.NewBitmap(numValidators) + epochBitmap = k.bitmapHelper.NewBitmap(numValidators) } - - if k.bitmapHelper.IsSet(bitmap, int(index)) { - return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator %s already attested for height %d", msg.Validator, msg.Height) + k.bitmapHelper.SetBit(epochBitmap, int(index)) + if err := k.SetEpochBitmap(ctx, votedEpoch, epochBitmap); err != nil { + return sdkerr.Wrap(err, "set epoch bitmap") } + return nil +} - // TODO: Verify the vote signature here once we implement vote parsing +// validateAttestation validates the attestation request +func (k msgServer) validateAttestation(ctx sdk.Context, msg *types.MsgAttest) error { + if k.GetParams(ctx).SignMode == types.SignMode_SIGN_MODE_CHECKPOINT && + !k.IsCheckpointHeight(ctx, msg.Height) { + return sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "height %d is not a checkpoint", msg.Height) + } - // Set the bit - k.bitmapHelper.SetBit(bitmap, int(index)) - if err := k.SetAttestationBitmap(ctx, msg.Height, bitmap); err != nil { - return nil, sdkerr.Wrap(err, "set attestation bitmap") + has, err := k.IsInAttesterSet(ctx, msg.Validator) + if err != nil { + return sdkerr.Wrapf(err, "in attester set") } + if !has { + return sdkerr.Wrapf(sdkerrors.ErrUnauthorized, "validator %s not in attester set", msg.Validator) + } + return nil +} - // Store signature using the new collection method - if err := k.SetSignature(ctx, msg.Height, msg.Validator, msg.Vote); err != nil { - return nil, sdkerr.Wrap(err, "store signature") +// updateAttestationBitmap handles bitmap operations for attestation +func (k msgServer) updateAttestationBitmap(ctx sdk.Context, msg *types.MsgAttest, index uint16) error { + bitmap, err := k.GetAttestationBitmap(ctx, msg.Height) + if err != nil && !sdkerr.IsOf(err, collections.ErrNotFound) { + return err } - epoch := k.GetCurrentEpoch(ctx) - epochBitmap := k.GetEpochBitmap(ctx, epoch) - if epochBitmap == nil { + if bitmap == nil { validators, err := k.stakingKeeper.GetLastValidators(ctx) if err != nil { - return nil, err + return err } numValidators := 0 for _, v := range validators { @@ -97,23 +139,57 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M numValidators++ } } - epochBitmap = k.bitmapHelper.NewBitmap(numValidators) + bitmap = k.bitmapHelper.NewBitmap(numValidators) } - k.bitmapHelper.SetBit(epochBitmap, int(index)) - if err := k.SetEpochBitmap(ctx, epoch, epochBitmap); err != nil { - return nil, sdkerr.Wrap(err, "set epoch bitmap") + + if k.bitmapHelper.IsSet(bitmap, int(index)) { + return sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator %s already attested for height %d", msg.Validator, msg.Height) } - // Emit event - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.TypeMsgAttest, - sdk.NewAttribute("validator", msg.Validator), - sdk.NewAttribute("height", math.NewInt(msg.Height).String()), - ), - ) + k.bitmapHelper.SetBit(bitmap, int(index)) - return &types.MsgAttestResponse{}, nil + if err := k.SetAttestationBitmap(ctx, msg.Height, bitmap); err != nil { + return sdkerr.Wrap(err, "set attestation bitmap") + } + return nil +} + +// verifyVote verifies the vote signature and block hash +func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto.Vote, error) { + var vote cmtproto.Vote + if err := proto.Unmarshal(msg.Vote, &vote); err != nil { + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "unmarshal vote: %s", err) + } + if msg.Height != vote.Height { + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "vote height does not match attestation height") + } + if len(vote.Signature) == 0 { + return nil, sdkerrors.ErrInvalidRequest.Wrap("empty signature") + } + + // todo (Alex): validate app hash match, vote clock drift + + valAddress, err := sdk.ValAddressFromBech32(msg.Validator) + if err != nil { + return nil, sdkerr.Wrap(err, "invalid validator address") + } + validator, err := k.stakingKeeper.GetValidator(ctx, valAddress) + if err != nil { + return nil, sdkerr.Wrapf(err, "get validator") + } + pubKey, err := validator.ConsPubKey() + if err != nil { + return nil, sdkerr.Wrapf(err, "pubkey") + } + if !bytes.Equal(pubKey.Address().Bytes(), vote.ValidatorAddress) { + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "pubkey address does not match validator address") + } + voteSignBytes := cmttypes.VoteSignBytes(ctx.ChainID(), &vote) + if !pubKey.VerifySignature(voteSignBytes, vote.Signature) { + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "invalid vote signature") + } + + return &vote, nil } // JoinAttesterSet handles MsgJoinAttesterSet diff --git a/modules/network/keeper/msg_server_test.go b/modules/network/keeper/msg_server_test.go index 7f8a992..fcd7fad 100644 --- a/modules/network/keeper/msg_server_test.go +++ b/modules/network/keeper/msg_server_test.go @@ -1,17 +1,16 @@ package keeper import ( - "context" - "maps" - "slices" - "strings" + "crypto/sha256" "testing" "time" + "cosmossdk.io/collections" "cosmossdk.io/log" - "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/runtime" "github.com/cosmos/cosmos-sdk/testutil/integration" sdk "github.com/cosmos/cosmos-sdk/types" @@ -19,17 +18,24 @@ import ( moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/gogoproto/proto" + ds "github.com/ipfs/go-datastore" + kt "github.com/ipfs/go-datastore/keytransform" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + rollnode "github.com/rollkit/rollkit/node" + rstore "github.com/rollkit/rollkit/pkg/store" + rollkittypes "github.com/rollkit/rollkit/types" + "github.com/rollkit/go-execution-abci/modules/network/types" ) func TestJoinAttesterSet(t *testing.T) { - myValAddr := sdk.ValAddress("validator4") + myValAddr := sdk.ValAddress("validator") type testCase struct { - setup func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) + setup func(t *testing.T, env testEnv) msg *types.MsgJoinAttesterSet expErr error expSet bool @@ -37,150 +43,379 @@ func TestJoinAttesterSet(t *testing.T) { tests := map[string]testCase{ "valid": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) { + setup: func(t *testing.T, env testEnv) { validator := stakingtypes.Validator{ OperatorAddress: myValAddr.String(), Status: stakingtypes.Bonded, } - err := sk.SetValidator(ctx, validator) + err := env.SK.SetValidator(env.Ctx, validator) require.NoError(t, err, "failed to set validator") }, msg: &types.MsgJoinAttesterSet{Validator: myValAddr.String()}, expSet: true, }, "invalid_addr": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) {}, + setup: func(t *testing.T, env testEnv) {}, msg: &types.MsgJoinAttesterSet{Validator: "invalidAddr"}, expErr: sdkerrors.ErrInvalidAddress, }, "val not exists": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) {}, + setup: func(t *testing.T, env testEnv) {}, msg: &types.MsgJoinAttesterSet{Validator: myValAddr.String()}, expErr: sdkerrors.ErrNotFound, }, "val not bonded": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) { + setup: func(t *testing.T, env testEnv) { validator := stakingtypes.Validator{ OperatorAddress: myValAddr.String(), Status: stakingtypes.Unbonded, // Validator is not bonded } - err := sk.SetValidator(ctx, validator) + err := env.SK.SetValidator(env.Ctx, validator) require.NoError(t, err, "failed to set validator") }, msg: &types.MsgJoinAttesterSet{Validator: myValAddr.String()}, expErr: sdkerrors.ErrInvalidRequest, }, "already set": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) { + setup: func(t *testing.T, env testEnv) { validator := stakingtypes.Validator{ OperatorAddress: myValAddr.String(), Status: stakingtypes.Bonded, } - require.NoError(t, sk.SetValidator(ctx, validator)) - require.NoError(t, keeper.SetAttesterSetMember(ctx, myValAddr.String())) + require.NoError(t, env.SK.SetValidator(env.Ctx, validator)) + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, myValAddr.String())) }, msg: &types.MsgJoinAttesterSet{Validator: myValAddr.String()}, expErr: sdkerrors.ErrInvalidRequest, expSet: true, }, - //{ - // name: "failed to set attester set member", - // setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) { - // validatorAddr := sdk.ValAddress([]byte("validator5")) - // validator := stakingtypes.Validator{ - // OperatorAddress: validatorAddr.String(), - // Status: stakingtypes.Bonded, - // } - // err := sk.SetValidator(ctx, validator) - // require.NoError(t, err, "failed to set validator") - // keeper.forceError = true - // }, - // msg: &types.MsgJoinAttesterSet{ - // Validator: "validator5", - // }, - // expErr: sdkerrors.ErrInternal, - // expectResponse: false, - //}, } - for name, spec := range tests { t.Run(name, func(t *testing.T) { - sk := NewMockStakingKeeper() - - cdc := moduletestutil.MakeTestEncodingConfig().Codec + // Setup test environment + env := setupTestEnv(t, 10) - keys := storetypes.NewKVStoreKeys(types.StoreKey) - - logger := log.NewTestLogger(t) - cms := integration.CreateMultiStore(keys, logger) - authority := authtypes.NewModuleAddress("gov") - keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), sk, nil, nil, authority.String()) - server := msgServer{Keeper: keeper} - ctx := sdk.NewContext(cms, cmtproto.Header{ChainID: "test-chain", Time: time.Now().UTC(), Height: 10}, false, logger). - WithContext(t.Context()) - - spec.setup(t, ctx, &keeper, &sk) + // Apply test-specific setup + spec.setup(t, env) // when - rsp, err := server.JoinAttesterSet(ctx, spec.msg) + rsp, err := env.Server.JoinAttesterSet(env.Ctx, spec.msg) + // then if spec.expErr != nil { require.ErrorIs(t, err, spec.expErr) require.Nil(t, rsp) - exists, gotErr := keeper.AttesterSet.Has(ctx, spec.msg.Validator) + exists, gotErr := env.Keeper.AttesterSet.Has(env.Ctx, spec.msg.Validator) require.NoError(t, gotErr) assert.Equal(t, exists, spec.expSet) return } require.NoError(t, err) require.NotNil(t, rsp) - exists, gotErr := keeper.AttesterSet.Has(ctx, spec.msg.Validator) + exists, gotErr := env.Keeper.AttesterSet.Has(env.Ctx, spec.msg.Validator) require.NoError(t, gotErr) assert.True(t, exists) }) } } -var _ types.StakingKeeper = &MockStakingKeeper{} +func TestAttest(t *testing.T) { + const epochLength = 10 + var ( + myHash = sha256.Sum256([]byte("app_hash")) + myAppHash = myHash[:] + voteSigner = ed25519.GenPrivKey() + valAddrStr = sdk.ValAddress(voteSigner.PubKey().Address()).String() + ) -type MockStakingKeeper struct { - activeSet map[string]stakingtypes.Validator -} + // Setup test environment with block store + env := setupTestEnv(t, 2*epochLength) + + // Set up validator + validator, err := stakingtypes.NewValidator(valAddrStr, voteSigner.PubKey(), stakingtypes.Description{}) + require.NoError(t, err) + validator.Status = stakingtypes.Bonded + require.NoError(t, env.SK.SetValidator(env.Ctx, validator)) + + // Save block data + signedHeader := HeaderFixture(voteSigner, myAppHash) + data := &rollkittypes.Data{Txs: rollkittypes.Txs{}} + var signature rollkittypes.Signature + require.NoError(t, env.BlockStore.SaveBlockData(env.Ctx, signedHeader, data, &signature)) -func NewMockStakingKeeper() MockStakingKeeper { - return MockStakingKeeper{ - activeSet: make(map[string]stakingtypes.Validator), + var ( + validVote = VoteFixture(myAppHash, voteSigner) + validVoteBz = must(proto.Marshal(validVote)) + ) + parentCtx := env.Ctx + + specs := map[string]struct { + setup func(t *testing.T, env testEnv) sdk.Context + msg func(t *testing.T) *types.MsgAttest + expErr error + }{ + "valid attestation": { + setup: func(t *testing.T, env testEnv) sdk.Context { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env.Ctx + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: validVoteBz} + }, + }, + "invalid vote content": { + setup: func(t *testing.T, env testEnv) sdk.Context { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env.Ctx + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: []byte("not a valid proto vote")} + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "validator not in attester set": { + setup: func(t *testing.T, env testEnv) sdk.Context { + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env.Ctx + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: validVoteBz} + }, + expErr: sdkerrors.ErrUnauthorized, + }, + "invalid signature": { + setup: func(t *testing.T, env testEnv) sdk.Context { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env.Ctx + }, + msg: func(t *testing.T) *types.MsgAttest { + invalidVote := VoteFixture(myAppHash, voteSigner, func(vote *cmtproto.Vote) { + vote.Signature = []byte("invalid signature") + }) + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: must(proto.Marshal(invalidVote))} + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "not a checkpoint height": { + setup: func(t *testing.T, env testEnv) sdk.Context { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env.Ctx + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength + 1, Vote: validVoteBz} + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "vote window expired": { + setup: func(t *testing.T, env testEnv) sdk.Context { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env.Ctx.WithBlockHeight(2*epochLength + 1) + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: validVoteBz} + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "voting for a future epoch": { + setup: func(t *testing.T, env testEnv) sdk.Context { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env.Ctx.WithBlockHeight(2 * epochLength) + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: 3 * epochLength, Vote: validVoteBz} + }, + expErr: sdkerrors.ErrInvalidRequest, + }, } -} + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + // Create a new environment for each test case with a cached context + testEnv := env + testEnv.Ctx, _ = parentCtx.CacheContext() + ctx := spec.setup(t, testEnv) -func (m *MockStakingKeeper) SetValidator(ctx context.Context, validator stakingtypes.Validator) error { - m.activeSet[validator.GetOperator()] = validator - return nil + // when + srcMsg := spec.msg(t) + gotRsp, gotErr := testEnv.Server.Attest(ctx, srcMsg) + // then + if spec.expErr != nil { + require.Error(t, gotErr) + require.ErrorIs(t, gotErr, spec.expErr) + // and ensure the signature is not stored + _, err := testEnv.Keeper.GetVote(ctx, srcMsg.Height, valAddrStr) + assert.ErrorIs(t, err, collections.ErrNotFound) + return + } + + require.NoError(t, gotErr) + require.NotNil(t, gotRsp) + + // and attestation marked + bitmap, gotErr := testEnv.Keeper.GetAttestationBitmap(ctx, srcMsg.Height) + require.NoError(t, gotErr) + require.NotEmpty(t, bitmap) + require.Equal(t, byte(1), bitmap[0]) + + // and the signature was stored properly + gotSig, err := testEnv.Keeper.GetVote(ctx, srcMsg.Height, valAddrStr) + require.NoError(t, err) + var vote cmtproto.Vote + require.NoError(t, proto.Unmarshal(srcMsg.Vote, &vote)) + assert.Equal(t, vote.Signature, gotSig) + }) + } } -func (m MockStakingKeeper) GetAllValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { - return slices.SortedFunc(maps.Values(m.activeSet), func(v1 stakingtypes.Validator, v2 stakingtypes.Validator) int { - return strings.Compare(v1.OperatorAddress, v2.OperatorAddress) - }), nil -} -func (m MockStakingKeeper) GetValidator(ctx context.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, err error) { - validator, found := m.activeSet[addr.String()] - if !found { - return validator, sdkerrors.ErrNotFound +func TestVerifyVote(t *testing.T) { + var ( + myHash = sha256.Sum256([]byte("app_hash")) + myAppHash = myHash[:] + validatorPrivKey = ed25519.GenPrivKey() + valAddrStr = sdk.ValAddress(validatorPrivKey.PubKey().Address()).String() + otherValidatorPrivKey = ed25519.GenPrivKey() + ) + + // Setup test environment with block store + env := setupTestEnv(t, 10) + + // Set up validator + validator, err := stakingtypes.NewValidator(valAddrStr, validatorPrivKey.PubKey(), stakingtypes.Description{}) + require.NoError(t, err) + validator.Status = stakingtypes.Bonded + require.NoError(t, env.SK.SetValidator(env.Ctx, validator)) + + // Save block data + header := HeaderFixture(validatorPrivKey, myAppHash) + var signature rollkittypes.Signature + require.NoError(t, env.BlockStore.SaveBlockData(env.Ctx, header, &rollkittypes.Data{}, &signature)) + + parentCtx := env.Ctx + + testCases := map[string]struct { + voteFn func(t *testing.T) *cmtproto.Vote + sender string + expErr error + }{ + "valid vote": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, validatorPrivKey) + }, + sender: valAddrStr, + }, + "block data not found": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, validatorPrivKey, func(vote *cmtproto.Vote) { + vote.Height++ + vote.Signature = must(validatorPrivKey.Sign(cmttypes.VoteSignBytes("testing", vote))) + }) + }, + sender: valAddrStr, + expErr: sdkerrors.ErrInvalidRequest, + }, + "validator not found": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, ed25519.GenPrivKey()) + }, + expErr: sdkerrors.ErrUnauthorized, + sender: sdk.ValAddress(otherValidatorPrivKey.PubKey().Address()).String(), + }, + "invalid vote signature": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, validatorPrivKey, func(vote *cmtproto.Vote) { + vote.Signature = []byte("invalid signature") + }) + }, + sender: valAddrStr, + expErr: sdkerrors.ErrInvalidRequest, + }, + "invalid sender": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, validatorPrivKey) + }, + sender: sdk.ValAddress(otherValidatorPrivKey.PubKey().Address()).String(), + expErr: sdkerrors.ErrUnauthorized, + }, + } + for name, spec := range testCases { + t.Run(name, func(t *testing.T) { + ctx, _ := parentCtx.CacheContext() + + // when + vote, err := env.Server.verifyVote(ctx, &types.MsgAttest{ + Height: 10, + Validator: spec.sender, + Vote: must(proto.Marshal(spec.voteFn(t))), + }) + + // then + if spec.expErr != nil { + require.Error(t, err) + require.ErrorIs(t, err, spec.expErr) + require.Nil(t, vote) + return + } + require.NoError(t, err) + require.NotNil(t, vote) + }) } - return validator, nil } -func (m MockStakingKeeper) GetLastValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { - for _, validator := range m.activeSet { - if validator.IsBonded() { // Assuming IsBonded() identifies if a validator is in the last validators - validators = append(validators, validator) - } +// testEnv contains all the common components needed for testing +type testEnv struct { + Ctx sdk.Context + Keeper Keeper + Server msgServer + SK MockStakingKeeper + BlockStore rstore.Store +} + +func setupTestEnv(t *testing.T, height int64) testEnv { + t.Helper() + // Set up codec and store + cdc := moduletestutil.MakeTestEncodingConfig().Codec + keys := storetypes.NewKVStoreKeys(types.StoreKey) + cms := integration.CreateMultiStore(keys, log.NewTestLogger(t)) + + sk := NewMockStakingKeeper() + rollkitPrefixStore := kt.Wrap(ds.NewMapDatastore(), &kt.PrefixTransform{ + Prefix: ds.NewKey(rollnode.RollkitPrefix), + }) + bs := rstore.New(rollkitPrefixStore) + + authority := authtypes.NewModuleAddress("gov") + keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), &sk, nil, nil, authority.String()) + + ctx := sdk.NewContext(cms, cmtproto.Header{ + ChainID: "testing", + Time: time.Now().UTC(), + Height: height, + }, false, log.NewTestLogger(t)).WithContext(t.Context()) + + server := msgServer{Keeper: keeper} + + params := types.DefaultParams() + params.EpochLength = 10 // test default + require.NoError(t, keeper.SetParams(ctx, params)) + + return testEnv{ + Ctx: ctx, + Keeper: keeper, + Server: server, + SK: sk, + BlockStore: bs, } - return } -func (m MockStakingKeeper) GetLastTotalPower(ctx context.Context) (math.Int, error) { - return math.NewInt(int64(len(m.activeSet))), nil +func must[T any](r T, err error) T { + if err != nil { + panic(err) + } + return r } diff --git a/modules/network/types/params.go b/modules/network/types/params.go index f7a6866..8f12aca 100644 --- a/modules/network/types/params.go +++ b/modules/network/types/params.go @@ -18,7 +18,7 @@ var ( // Default parameter values var ( - DefaultEpochLength = uint64(10) // todo (Alex): what is a good default? + DefaultEpochLength = uint64(1) // todo (Alex): what is a good default? DefaultQuorumFraction = math.LegacyNewDecWithPrec(667, 3) // 2/3 DefaultMinParticipation = math.LegacyNewDecWithPrec(5, 1) // 1/2 DefaultPruneAfter = uint64(7) diff --git a/modules/network/types/query.pb.go b/modules/network/types/query.pb.go index 0428c66..7be1ccd 100644 --- a/modules/network/types/query.pb.go +++ b/modules/network/types/query.pb.go @@ -537,6 +537,112 @@ func (m *QuerySoftConfirmationStatusResponse) GetQuorumFraction() string { return "" } +// QueryValidatorSignatureRequest is the request type for the Query/ValidatorSignature RPC method. +type QueryValidatorSignatureRequest struct { + BlockHeight int64 `protobuf:"varint,1,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + Validator string `protobuf:"bytes,2,opt,name=validator,proto3" json:"validator,omitempty"` +} + +func (m *QueryValidatorSignatureRequest) Reset() { *m = QueryValidatorSignatureRequest{} } +func (m *QueryValidatorSignatureRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorSignatureRequest) ProtoMessage() {} +func (*QueryValidatorSignatureRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bae17cb7b1e48f90, []int{10} +} +func (m *QueryValidatorSignatureRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorSignatureRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorSignatureRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidatorSignatureRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorSignatureRequest.Merge(m, src) +} +func (m *QueryValidatorSignatureRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorSignatureRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorSignatureRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorSignatureRequest proto.InternalMessageInfo + +func (m *QueryValidatorSignatureRequest) GetBlockHeight() int64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + +func (m *QueryValidatorSignatureRequest) GetValidator() string { + if m != nil { + return m.Validator + } + return "" +} + +// QueryValidatorSignatureResponse is the response type for the Query/ValidatorSignature RPC method. +type QueryValidatorSignatureResponse struct { + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` +} + +func (m *QueryValidatorSignatureResponse) Reset() { *m = QueryValidatorSignatureResponse{} } +func (m *QueryValidatorSignatureResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorSignatureResponse) ProtoMessage() {} +func (*QueryValidatorSignatureResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bae17cb7b1e48f90, []int{11} +} +func (m *QueryValidatorSignatureResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorSignatureResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorSignatureResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryValidatorSignatureResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorSignatureResponse.Merge(m, src) +} +func (m *QueryValidatorSignatureResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorSignatureResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorSignatureResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorSignatureResponse proto.InternalMessageInfo + +func (m *QueryValidatorSignatureResponse) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *QueryValidatorSignatureResponse) GetFound() bool { + if m != nil { + return m.Found + } + return false +} + func init() { proto.RegisterType((*QueryParamsRequest)(nil), "rollkitsdk.network.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "rollkitsdk.network.v1.QueryParamsResponse") @@ -548,64 +654,73 @@ func init() { proto.RegisterType((*QueryValidatorIndexResponse)(nil), "rollkitsdk.network.v1.QueryValidatorIndexResponse") proto.RegisterType((*QuerySoftConfirmationStatusRequest)(nil), "rollkitsdk.network.v1.QuerySoftConfirmationStatusRequest") proto.RegisterType((*QuerySoftConfirmationStatusResponse)(nil), "rollkitsdk.network.v1.QuerySoftConfirmationStatusResponse") + proto.RegisterType((*QueryValidatorSignatureRequest)(nil), "rollkitsdk.network.v1.QueryValidatorSignatureRequest") + proto.RegisterType((*QueryValidatorSignatureResponse)(nil), "rollkitsdk.network.v1.QueryValidatorSignatureResponse") } func init() { proto.RegisterFile("rollkitsdk/network/v1/query.proto", fileDescriptor_bae17cb7b1e48f90) } var fileDescriptor_bae17cb7b1e48f90 = []byte{ - // 831 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0x41, 0x6f, 0x1b, 0x45, - 0x14, 0xf6, 0x26, 0xb6, 0xc1, 0x2f, 0x55, 0x4b, 0xa6, 0x6e, 0xb0, 0x96, 0xc6, 0x6d, 0xb6, 0x42, - 0x75, 0x0c, 0xf6, 0xd6, 0x01, 0x8a, 0x5a, 0x38, 0x40, 0x10, 0x88, 0xde, 0xca, 0x16, 0x71, 0xe8, - 0xc5, 0x1a, 0xef, 0x8e, 0xd7, 0xa3, 0xd8, 0x3b, 0x9b, 0x9d, 0x59, 0xb7, 0x55, 0x94, 0x0b, 0x1c, - 0xb9, 0x20, 0xf5, 0x4f, 0x70, 0xe6, 0xcc, 0x9d, 0x5e, 0x90, 0x2a, 0x71, 0xe1, 0x84, 0x50, 0xc2, - 0x0f, 0x41, 0xfb, 0x66, 0xbc, 0xb1, 0x93, 0xb5, 0x69, 0x39, 0xd9, 0xf3, 0xcd, 0xf7, 0xbd, 0xf7, - 0xbd, 0x99, 0xf7, 0x66, 0x61, 0x27, 0x11, 0xe3, 0xf1, 0x01, 0x57, 0x32, 0x38, 0x70, 0x23, 0xa6, - 0x9e, 0x88, 0xe4, 0xc0, 0x9d, 0xf6, 0xdc, 0xc3, 0x94, 0x25, 0xcf, 0xba, 0x71, 0x22, 0x94, 0x20, - 0xd7, 0xce, 0x28, 0x5d, 0x43, 0xe9, 0x4e, 0x7b, 0x76, 0x3d, 0x14, 0xa1, 0x40, 0x86, 0x9b, 0xfd, - 0xd3, 0x64, 0xfb, 0x7a, 0x28, 0x44, 0x38, 0x66, 0x2e, 0x8d, 0xb9, 0x4b, 0xa3, 0x48, 0x28, 0xaa, - 0xb8, 0x88, 0xa4, 0xd9, 0x6d, 0xfb, 0x42, 0x4e, 0x84, 0x74, 0x07, 0x54, 0x32, 0x9d, 0xc3, 0x9d, - 0xf6, 0x06, 0x4c, 0xd1, 0x9e, 0x1b, 0xd3, 0x90, 0x47, 0x48, 0x36, 0xdc, 0x25, 0xce, 0xd4, 0xb3, - 0x98, 0x99, 0x70, 0x4e, 0x1d, 0xc8, 0x37, 0x59, 0x90, 0x87, 0x34, 0xa1, 0x13, 0xe9, 0xb1, 0xc3, - 0x94, 0x49, 0xe5, 0x78, 0x70, 0x75, 0x01, 0x95, 0xb1, 0x88, 0x24, 0x23, 0x9f, 0x40, 0x35, 0x46, - 0xa4, 0x61, 0xdd, 0xb4, 0x5a, 0x1b, 0x7b, 0xdb, 0xdd, 0xc2, 0xba, 0xba, 0x5a, 0xb6, 0x5f, 0x7e, - 0xf1, 0xd7, 0x8d, 0x92, 0x67, 0x24, 0xce, 0xc7, 0xb0, 0x8d, 0x31, 0x3f, 0x57, 0x8a, 0x49, 0x5d, - 0xd3, 0x3e, 0x57, 0x13, 0x1a, 0x9b, 0xa4, 0x64, 0x0b, 0xaa, 0x23, 0xc6, 0xc3, 0x91, 0xc2, 0xe8, - 0xeb, 0x9e, 0x59, 0x39, 0x03, 0x68, 0x2e, 0x13, 0x1a, 0x5f, 0x9f, 0x41, 0x75, 0x80, 0x88, 0xf1, - 0xd5, 0x5a, 0xe2, 0xeb, 0x62, 0x04, 0xa3, 0x73, 0x3a, 0x70, 0x0d, 0x73, 0x7c, 0x19, 0x0b, 0x7f, - 0xf4, 0x20, 0x1a, 0x8a, 0x99, 0xa9, 0x3a, 0x54, 0x58, 0x86, 0x61, 0xe4, 0xb2, 0xa7, 0x17, 0xce, - 0x8f, 0x6b, 0xb0, 0x75, 0x9e, 0x6f, 0xbc, 0x14, 0x0a, 0xc8, 0x0e, 0x5c, 0x92, 0x8a, 0x26, 0xaa, - 0x6f, 0x2a, 0x5c, 0xc3, 0x0a, 0x37, 0x10, 0xfb, 0x1a, 0x21, 0xb2, 0x0d, 0xc0, 0xa2, 0x60, 0x46, - 0x58, 0x47, 0x42, 0x8d, 0x45, 0x81, 0xd9, 0xee, 0x41, 0x3d, 0xa6, 0x89, 0xe2, 0x3e, 0x8f, 0xb1, - 0x80, 0xbe, 0xa9, 0xb8, 0x7c, 0xd3, 0x6a, 0x5d, 0xf2, 0xae, 0x2e, 0xec, 0xe9, 0xe2, 0xc8, 0x7b, - 0xb0, 0x49, 0x7d, 0xc5, 0xa7, 0xac, 0x3f, 0xa5, 0x63, 0x1e, 0x50, 0x25, 0x12, 0xd9, 0xa8, 0xa0, - 0xad, 0xb7, 0xf4, 0xc6, 0x77, 0x39, 0x4e, 0xee, 0x41, 0x63, 0x2e, 0x46, 0x14, 0xce, 0x6b, 0xaa, - 0xa8, 0x79, 0x7b, 0x61, 0xff, 0x4c, 0xea, 0xdc, 0x05, 0x1b, 0x0f, 0x23, 0x87, 0x1e, 0x44, 0x01, - 0x7b, 0x3a, 0x3b, 0xc1, 0x06, 0xbc, 0x41, 0x83, 0x20, 0x61, 0x52, 0x77, 0x4d, 0xcd, 0x9b, 0x2d, - 0x9d, 0xc7, 0xf0, 0x4e, 0xa1, 0x2e, 0xef, 0xb6, 0x0a, 0xcf, 0x00, 0x73, 0xa9, 0xef, 0x2e, 0xb9, - 0xd4, 0x73, 0x6a, 0xad, 0x71, 0x3e, 0x05, 0x07, 0x63, 0x3f, 0x12, 0x43, 0xf5, 0x85, 0x88, 0x86, - 0x3c, 0x99, 0xe0, 0xd1, 0x3c, 0x52, 0x54, 0xa5, 0xf2, 0xbf, 0x5a, 0xee, 0x57, 0x0b, 0x6e, 0xad, - 0x94, 0x1b, 0x8b, 0x6d, 0xd8, 0xe4, 0xb2, 0x2f, 0xc5, 0x50, 0xf5, 0x7d, 0xcd, 0x62, 0x01, 0x86, - 0x7a, 0xd3, 0xbb, 0xc2, 0xe5, 0x9c, 0x98, 0x05, 0xe4, 0x06, 0x6c, 0x4c, 0x85, 0x62, 0x41, 0x3f, - 0x16, 0x4f, 0x58, 0x82, 0x1d, 0x50, 0xf6, 0x00, 0xa1, 0x87, 0x19, 0x92, 0x11, 0x94, 0x50, 0x74, - 0x6c, 0x08, 0xeb, 0x9a, 0x80, 0x90, 0x26, 0xdc, 0x86, 0x2b, 0x87, 0xa9, 0x48, 0xd2, 0x49, 0x7f, - 0x98, 0x64, 0xf7, 0x27, 0x22, 0xbc, 0xfd, 0x9a, 0x77, 0x59, 0xc3, 0x5f, 0x19, 0x74, 0xef, 0xf7, - 0x2a, 0x54, 0xd0, 0x3e, 0xf9, 0xc1, 0x82, 0xaa, 0x9e, 0x46, 0xb2, 0xbb, 0xe4, 0xfc, 0x2e, 0x8e, - 0xbf, 0xdd, 0x7e, 0x15, 0xaa, 0x3e, 0x02, 0xc7, 0xf9, 0xfe, 0x8f, 0x7f, 0x9e, 0xaf, 0x5d, 0x27, - 0xb6, 0x6b, 0x34, 0xf3, 0x2f, 0x8d, 0x1e, 0x7d, 0xf2, 0x8b, 0x05, 0x9b, 0x17, 0x66, 0x8f, 0x7c, - 0xb8, 0x2a, 0xcb, 0xb2, 0x57, 0xc2, 0xfe, 0xe8, 0x35, 0x55, 0xc6, 0xe6, 0x1d, 0xb4, 0xd9, 0x26, - 0xad, 0x22, 0x9b, 0xf4, 0x4c, 0xe6, 0x1e, 0xe9, 0x16, 0x38, 0x26, 0xcf, 0x2d, 0xa8, 0xe5, 0xe3, - 0x4d, 0xde, 0x5f, 0x95, 0xf6, 0xfc, 0xab, 0x61, 0x77, 0x5e, 0x91, 0x6d, 0xcc, 0xed, 0xa2, 0xb9, - 0x5b, 0x64, 0xa7, 0xc8, 0x1c, 0x3e, 0x20, 0xee, 0x11, 0xfe, 0x1c, 0x93, 0x9f, 0x2d, 0xb8, 0xbc, - 0xd8, 0xf1, 0xa4, 0xb7, 0x2a, 0x59, 0xe1, 0x4c, 0xda, 0x7b, 0xaf, 0x23, 0x31, 0x26, 0x5d, 0x34, - 0xb9, 0x4b, 0x6e, 0x17, 0x99, 0xcc, 0x1f, 0x0b, 0xf7, 0xc8, 0x4c, 0xf7, 0x31, 0xf9, 0xcd, 0x82, - 0xad, 0xe2, 0xf9, 0x21, 0xf7, 0x56, 0xe5, 0x5f, 0x39, 0xb2, 0xf6, 0xfd, 0xff, 0x23, 0x35, 0x25, - 0xdc, 0xc5, 0x12, 0xee, 0x90, 0x6e, 0x51, 0x09, 0xd9, 0x14, 0x77, 0xfc, 0x39, 0x71, 0xde, 0x0a, - 0xfb, 0xdf, 0xbe, 0x38, 0x69, 0x5a, 0x2f, 0x4f, 0x9a, 0xd6, 0xdf, 0x27, 0x4d, 0xeb, 0xa7, 0xd3, - 0x66, 0xe9, 0xe5, 0x69, 0xb3, 0xf4, 0xe7, 0x69, 0xb3, 0xf4, 0xf8, 0x7e, 0xc8, 0xd5, 0x28, 0x1d, - 0x74, 0x7d, 0x31, 0xc9, 0x63, 0x86, 0xa2, 0xc3, 0x9e, 0x32, 0x3f, 0xcd, 0x62, 0x74, 0xe8, 0xc0, - 0xe7, 0xee, 0x44, 0x04, 0xe9, 0x98, 0xc9, 0x3c, 0x1b, 0x7e, 0x80, 0x07, 0x55, 0xfc, 0x02, 0x7f, - 0xf0, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe2, 0x10, 0xc6, 0x5b, 0x40, 0x08, 0x00, 0x00, + // 938 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xce, 0xe6, 0x87, 0x89, 0x5f, 0xa2, 0x96, 0x4c, 0xd3, 0x10, 0x2d, 0x89, 0xd3, 0x6c, 0x85, + 0x9a, 0x04, 0xe2, 0xad, 0x03, 0x0d, 0x34, 0x70, 0x28, 0x41, 0x20, 0x7a, 0x2b, 0x1b, 0xe0, 0xd0, + 0x8b, 0x35, 0xde, 0x1d, 0x6f, 0x46, 0xb1, 0x77, 0x36, 0x3b, 0xb3, 0x6e, 0x2b, 0xcb, 0x17, 0x38, + 0x72, 0x41, 0xaa, 0xf8, 0x1f, 0x38, 0x73, 0xe6, 0x8a, 0xe8, 0xb1, 0x12, 0x17, 0x4e, 0x08, 0x25, + 0xfc, 0x21, 0x68, 0xdf, 0x8c, 0xd7, 0x76, 0xb2, 0x36, 0x09, 0x27, 0x7b, 0xbe, 0x79, 0xdf, 0x7b, + 0xdf, 0x9b, 0x79, 0xf3, 0x69, 0x61, 0x33, 0x11, 0xad, 0xd6, 0x09, 0x57, 0x32, 0x38, 0x71, 0x23, + 0xa6, 0x9e, 0x89, 0xe4, 0xc4, 0xed, 0xd4, 0xdc, 0xd3, 0x94, 0x25, 0x2f, 0xaa, 0x71, 0x22, 0x94, + 0x20, 0xb7, 0x07, 0x21, 0x55, 0x13, 0x52, 0xed, 0xd4, 0xec, 0x1d, 0x5f, 0xc8, 0xb6, 0x90, 0x6e, + 0x83, 0x4a, 0xa6, 0xe3, 0xdd, 0x4e, 0xad, 0xc1, 0x14, 0xad, 0xb9, 0x31, 0x0d, 0x79, 0x44, 0x15, + 0x17, 0x91, 0x4e, 0x61, 0x2f, 0x87, 0x22, 0x14, 0xf8, 0xd7, 0xcd, 0xfe, 0x19, 0x74, 0x2d, 0x14, + 0x22, 0x6c, 0x31, 0x97, 0xc6, 0xdc, 0xa5, 0x51, 0x24, 0x14, 0x52, 0xa4, 0xd9, 0x1d, 0xa3, 0x4c, + 0xbd, 0x88, 0x99, 0x09, 0x71, 0x96, 0x81, 0x7c, 0x95, 0x15, 0x7e, 0x42, 0x13, 0xda, 0x96, 0x1e, + 0x3b, 0x4d, 0x99, 0x54, 0x8e, 0x07, 0xb7, 0x46, 0x50, 0x19, 0x8b, 0x48, 0x32, 0xf2, 0x31, 0x94, + 0x62, 0x44, 0x56, 0xad, 0x3b, 0xd6, 0xd6, 0xc2, 0xde, 0x7a, 0xb5, 0xb0, 0xaf, 0xaa, 0xa6, 0x1d, + 0xce, 0xbe, 0xfa, 0x6b, 0x63, 0xca, 0x33, 0x14, 0xe7, 0x43, 0x58, 0xc7, 0x9c, 0x9f, 0x2a, 0xc5, + 0xa4, 0xd6, 0x79, 0xc8, 0x55, 0x9b, 0xc6, 0xa6, 0x28, 0x59, 0x81, 0xd2, 0x31, 0xe3, 0xe1, 0xb1, + 0xc2, 0xec, 0x33, 0x9e, 0x59, 0x39, 0x0d, 0xa8, 0x8c, 0x23, 0x1a, 0x5d, 0x8f, 0xa0, 0xd4, 0x40, + 0xc4, 0xe8, 0xda, 0x1a, 0xa3, 0xeb, 0x72, 0x06, 0xc3, 0x73, 0x76, 0xe1, 0x36, 0xd6, 0xf8, 0x3c, + 0x16, 0xfe, 0xf1, 0xe3, 0xa8, 0x29, 0xfa, 0xa2, 0x96, 0x61, 0x8e, 0x65, 0x18, 0x66, 0x9e, 0xf5, + 0xf4, 0xc2, 0xf9, 0x61, 0x1a, 0x56, 0x2e, 0xc6, 0x1b, 0x2d, 0x85, 0x04, 0xb2, 0x09, 0x8b, 0x52, + 0xd1, 0x44, 0xd5, 0x4d, 0x87, 0xd3, 0xd8, 0xe1, 0x02, 0x62, 0x5f, 0x22, 0x44, 0xd6, 0x01, 0x58, + 0x14, 0xf4, 0x03, 0x66, 0x30, 0xa0, 0xcc, 0xa2, 0xc0, 0x6c, 0xd7, 0x60, 0x39, 0xa6, 0x89, 0xe2, + 0x3e, 0x8f, 0xb1, 0x81, 0xba, 0xe9, 0x78, 0xf6, 0x8e, 0xb5, 0xb5, 0xe8, 0xdd, 0x1a, 0xd9, 0xd3, + 0xcd, 0x91, 0x77, 0x61, 0x89, 0xfa, 0x8a, 0x77, 0x58, 0xbd, 0x43, 0x5b, 0x3c, 0xa0, 0x4a, 0x24, + 0x72, 0x75, 0x0e, 0x65, 0xbd, 0xa9, 0x37, 0xbe, 0xcd, 0x71, 0xf2, 0x10, 0x56, 0x87, 0x72, 0x44, + 0xe1, 0x30, 0xa7, 0x84, 0x9c, 0xb7, 0x46, 0xf6, 0x07, 0x54, 0x67, 0x1f, 0x6c, 0x3c, 0x8c, 0x1c, + 0x7a, 0x1c, 0x05, 0xec, 0x79, 0xff, 0x04, 0x57, 0xe1, 0x0d, 0x1a, 0x04, 0x09, 0x93, 0x7a, 0x6a, + 0xca, 0x5e, 0x7f, 0xe9, 0x3c, 0x85, 0xb7, 0x0b, 0x79, 0xf9, 0xb4, 0xcd, 0xf1, 0x0c, 0x30, 0x97, + 0xfa, 0xce, 0x98, 0x4b, 0xbd, 0xc0, 0xd6, 0x1c, 0xe7, 0x13, 0x70, 0x30, 0xf7, 0x91, 0x68, 0xaa, + 0xcf, 0x44, 0xd4, 0xe4, 0x49, 0x1b, 0x8f, 0xe6, 0x48, 0x51, 0x95, 0xca, 0xff, 0x1a, 0xb9, 0x5f, + 0x2d, 0xb8, 0x3b, 0x91, 0x6e, 0x24, 0xee, 0xc0, 0x12, 0x97, 0x75, 0x29, 0x9a, 0xaa, 0xee, 0xeb, + 0x28, 0x16, 0x60, 0xaa, 0x79, 0xef, 0x26, 0x97, 0x43, 0x64, 0x16, 0x90, 0x0d, 0x58, 0xe8, 0x08, + 0xc5, 0x82, 0x7a, 0x2c, 0x9e, 0xb1, 0x04, 0x27, 0x60, 0xd6, 0x03, 0x84, 0x9e, 0x64, 0x48, 0x16, + 0xa0, 0x84, 0xa2, 0x2d, 0x13, 0x30, 0xa3, 0x03, 0x10, 0xd2, 0x01, 0xf7, 0xe0, 0xe6, 0x69, 0x2a, + 0x92, 0xb4, 0x5d, 0x6f, 0x26, 0xd9, 0xfd, 0x89, 0x08, 0x6f, 0xbf, 0xec, 0xdd, 0xd0, 0xf0, 0x17, + 0x06, 0x75, 0xa8, 0x79, 0x31, 0xf9, 0xd1, 0x1c, 0xf1, 0x30, 0xa2, 0x2a, 0x4d, 0x58, 0xbf, 0xf1, + 0x4d, 0x58, 0x6c, 0xb4, 0x84, 0x7f, 0x52, 0x1f, 0x69, 0x7f, 0x01, 0x31, 0x33, 0x70, 0x6b, 0x50, + 0xce, 0x47, 0x00, 0xd5, 0x96, 0xbd, 0x01, 0xe0, 0x7c, 0x03, 0x1b, 0x63, 0x4b, 0x98, 0xc3, 0x59, + 0x83, 0xb2, 0xec, 0x83, 0x58, 0x60, 0xd1, 0x1b, 0x00, 0xd9, 0x3b, 0x69, 0x8a, 0x34, 0x0a, 0x30, + 0xf5, 0xbc, 0xa7, 0x17, 0x7b, 0x3f, 0xcd, 0xc3, 0x1c, 0xe6, 0x25, 0xdf, 0x5b, 0x50, 0xd2, 0x3e, + 0x42, 0xb6, 0xc7, 0xdc, 0xfc, 0x65, 0xe3, 0xb2, 0x77, 0xae, 0x12, 0xaa, 0xf5, 0x39, 0xce, 0x77, + 0x7f, 0xfc, 0xf3, 0x72, 0x7a, 0x8d, 0xd8, 0xae, 0xe1, 0x0c, 0x7b, 0xa4, 0x36, 0x2d, 0xf2, 0x8b, + 0x05, 0x4b, 0x97, 0x5c, 0x83, 0x7c, 0x30, 0xa9, 0xca, 0x38, 0x7f, 0xb3, 0x1f, 0x5c, 0x93, 0x65, + 0x64, 0xde, 0x47, 0x99, 0x3b, 0x64, 0xab, 0x48, 0x26, 0x1d, 0xd0, 0xdc, 0xae, 0xbe, 0xcb, 0x1e, + 0x79, 0x69, 0x41, 0x39, 0x37, 0x26, 0xf2, 0xde, 0xa4, 0xb2, 0x17, 0xfd, 0xce, 0xde, 0xbd, 0x62, + 0xb4, 0x11, 0xb7, 0x8d, 0xe2, 0xee, 0x92, 0xcd, 0x22, 0x71, 0x68, 0x7d, 0x6e, 0x17, 0x7f, 0x7a, + 0xe4, 0x67, 0x0b, 0x6e, 0x8c, 0xbe, 0x55, 0x52, 0x9b, 0x54, 0xac, 0xd0, 0x4d, 0xec, 0xbd, 0xeb, + 0x50, 0x8c, 0x48, 0x17, 0x45, 0x6e, 0x93, 0x7b, 0x45, 0x22, 0xf3, 0x91, 0x76, 0xbb, 0xc6, 0x97, + 0x7a, 0xe4, 0x77, 0x0b, 0x56, 0x8a, 0x5f, 0x3e, 0x79, 0x38, 0xa9, 0xfe, 0x44, 0xb3, 0xb1, 0x0f, + 0xfe, 0x0f, 0xd5, 0xb4, 0xb0, 0x8f, 0x2d, 0xdc, 0x27, 0xd5, 0xa2, 0x16, 0x32, 0xff, 0xd9, 0xf5, + 0x87, 0xc8, 0x83, 0x51, 0xf8, 0xcd, 0x02, 0x72, 0xf9, 0x89, 0x92, 0x07, 0x57, 0x3a, 0xc5, 0x8b, + 0xae, 0x61, 0xef, 0x5f, 0x97, 0x66, 0xd4, 0x3f, 0x42, 0xf5, 0x07, 0xe4, 0xa3, 0x42, 0xf5, 0xfd, + 0x70, 0xb7, 0x3b, 0x6c, 0x49, 0x3d, 0xb7, 0x9b, 0x5f, 0x4d, 0xef, 0xf0, 0xeb, 0x57, 0x67, 0x15, + 0xeb, 0xf5, 0x59, 0xc5, 0xfa, 0xfb, 0xac, 0x62, 0xfd, 0x78, 0x5e, 0x99, 0x7a, 0x7d, 0x5e, 0x99, + 0xfa, 0xf3, 0xbc, 0x32, 0xf5, 0xf4, 0x20, 0xe4, 0xea, 0x38, 0x6d, 0x54, 0x7d, 0xd1, 0xce, 0xb3, + 0x87, 0x62, 0x97, 0x3d, 0x67, 0x7e, 0x9a, 0x9d, 0xc5, 0x2e, 0x6d, 0xf8, 0xdc, 0x6d, 0x8b, 0x20, + 0x6d, 0x31, 0x99, 0xd7, 0xc5, 0x4f, 0xa0, 0x46, 0x09, 0xbf, 0x81, 0xde, 0xff, 0x37, 0x00, 0x00, + 0xff, 0xff, 0x65, 0xa2, 0x2b, 0x9c, 0xc2, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -630,6 +745,8 @@ type QueryClient interface { ValidatorIndex(ctx context.Context, in *QueryValidatorIndexRequest, opts ...grpc.CallOption) (*QueryValidatorIndexResponse, error) // SoftConfirmationStatus queries if a height is soft-confirmed SoftConfirmationStatus(ctx context.Context, in *QuerySoftConfirmationStatusRequest, opts ...grpc.CallOption) (*QuerySoftConfirmationStatusResponse, error) + // ValidatorSignature queries the signature of a validator for a specific height + ValidatorSignature(ctx context.Context, in *QueryValidatorSignatureRequest, opts ...grpc.CallOption) (*QueryValidatorSignatureResponse, error) } type queryClient struct { @@ -685,6 +802,15 @@ func (c *queryClient) SoftConfirmationStatus(ctx context.Context, in *QuerySoftC return out, nil } +func (c *queryClient) ValidatorSignature(ctx context.Context, in *QueryValidatorSignatureRequest, opts ...grpc.CallOption) (*QueryValidatorSignatureResponse, error) { + out := new(QueryValidatorSignatureResponse) + err := c.cc.Invoke(ctx, "/rollkitsdk.network.v1.Query/ValidatorSignature", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Params queries the module parameters @@ -697,6 +823,8 @@ type QueryServer interface { ValidatorIndex(context.Context, *QueryValidatorIndexRequest) (*QueryValidatorIndexResponse, error) // SoftConfirmationStatus queries if a height is soft-confirmed SoftConfirmationStatus(context.Context, *QuerySoftConfirmationStatusRequest) (*QuerySoftConfirmationStatusResponse, error) + // ValidatorSignature queries the signature of a validator for a specific height + ValidatorSignature(context.Context, *QueryValidatorSignatureRequest) (*QueryValidatorSignatureResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -718,6 +846,9 @@ func (*UnimplementedQueryServer) ValidatorIndex(ctx context.Context, req *QueryV func (*UnimplementedQueryServer) SoftConfirmationStatus(ctx context.Context, req *QuerySoftConfirmationStatusRequest) (*QuerySoftConfirmationStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SoftConfirmationStatus not implemented") } +func (*UnimplementedQueryServer) ValidatorSignature(ctx context.Context, req *QueryValidatorSignatureRequest) (*QueryValidatorSignatureResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidatorSignature not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -813,6 +944,24 @@ func _Query_SoftConfirmationStatus_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _Query_ValidatorSignature_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorSignatureRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ValidatorSignature(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollkitsdk.network.v1.Query/ValidatorSignature", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ValidatorSignature(ctx, req.(*QueryValidatorSignatureRequest)) + } + return interceptor(ctx, in, info, handler) +} + var Query_serviceDesc = _Query_serviceDesc var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "rollkitsdk.network.v1.Query", @@ -838,6 +987,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "SoftConfirmationStatus", Handler: _Query_SoftConfirmationStatus_Handler, }, + { + MethodName: "ValidatorSignature", + Handler: _Query_ValidatorSignature_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "rollkitsdk/network/v1/query.proto", @@ -1188,6 +1341,81 @@ func (m *QuerySoftConfirmationStatusResponse) MarshalToSizedBuffer(dAtA []byte) return len(dAtA) - i, nil } +func (m *QueryValidatorSignatureRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidatorSignatureRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorSignatureRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Validator) > 0 { + i -= len(m.Validator) + copy(dAtA[i:], m.Validator) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Validator))) + i-- + dAtA[i] = 0x12 + } + if m.BlockHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorSignatureResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryValidatorSignatureResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorSignatureResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Found { + i-- + if m.Found { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -1344,6 +1572,38 @@ func (m *QuerySoftConfirmationStatusResponse) Size() (n int) { return n } +func (m *QueryValidatorSignatureRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.BlockHeight != 0 { + n += 1 + sovQuery(uint64(m.BlockHeight)) + } + l = len(m.Validator) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidatorSignatureResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Found { + n += 2 + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2263,6 +2523,211 @@ func (m *QuerySoftConfirmationStatusResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryValidatorSignatureRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidatorSignatureRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorSignatureRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Validator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryValidatorSignatureResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryValidatorSignatureResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorSignatureResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Found", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Found = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/modules/network/types/query.pb.gw.go b/modules/network/types/query.pb.gw.go index ca6c1a7..d4959f6 100644 --- a/modules/network/types/query.pb.gw.go +++ b/modules/network/types/query.pb.gw.go @@ -267,6 +267,82 @@ func local_request_Query_SoftConfirmationStatus_0(ctx context.Context, marshaler } +func request_Query_ValidatorSignature_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorSignatureRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["block_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "block_height") + } + + protoReq.BlockHeight, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "block_height", err) + } + + val, ok = pathParams["validator"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator") + } + + protoReq.Validator, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator", err) + } + + msg, err := client.ValidatorSignature(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ValidatorSignature_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorSignatureRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["block_height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "block_height") + } + + protoReq.BlockHeight, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "block_height", err) + } + + val, ok = pathParams["validator"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator") + } + + protoReq.Validator, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator", err) + } + + msg, err := server.ValidatorSignature(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -388,6 +464,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_ValidatorSignature_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_ValidatorSignature_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ValidatorSignature_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -529,6 +628,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_ValidatorSignature_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_ValidatorSignature_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ValidatorSignature_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -542,6 +661,8 @@ var ( pattern_Query_ValidatorIndex_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"rollkit", "network", "v1", "validator", "address"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_SoftConfirmationStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"rollkit", "network", "v1", "soft-confirmation", "height"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ValidatorSignature_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"rollkit", "network", "v1", "signature", "block_height", "validator"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -554,4 +675,6 @@ var ( forward_Query_ValidatorIndex_0 = runtime.ForwardResponseMessage forward_Query_SoftConfirmationStatus_0 = runtime.ForwardResponseMessage + + forward_Query_ValidatorSignature_0 = runtime.ForwardResponseMessage ) diff --git a/modules/proto/rollkitsdk/network/v1/query.proto b/modules/proto/rollkitsdk/network/v1/query.proto index aeaf332..4677066 100644 --- a/modules/proto/rollkitsdk/network/v1/query.proto +++ b/modules/proto/rollkitsdk/network/v1/query.proto @@ -2,39 +2,44 @@ syntax = "proto3"; package rollkitsdk.network.v1; -option go_package = "github.com/rollkit/go-execution-abci/modules/network/types"; - +import "cosmos/base/query/v1beta1/pagination.proto"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; -import "cosmos/base/query/v1beta1/pagination.proto"; import "rollkitsdk/network/v1/types.proto"; +option go_package = "github.com/rollkit/go-execution-abci/modules/network/types"; + // Query defines the gRPC querier service for the network module. service Query { // Params queries the module parameters rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/rollkit/network/v1/params"; } - + // AttestationBitmap queries the attestation bitmap for a specific height rpc AttestationBitmap(QueryAttestationBitmapRequest) returns (QueryAttestationBitmapResponse) { option (google.api.http).get = "/rollkit/network/v1/attestation/{height}"; } - + // EpochInfo queries information about a specific epoch rpc EpochInfo(QueryEpochInfoRequest) returns (QueryEpochInfoResponse) { option (google.api.http).get = "/rollkit/network/v1/epoch/{epoch}"; } - + // ValidatorIndex queries the bitmap index for a validator rpc ValidatorIndex(QueryValidatorIndexRequest) returns (QueryValidatorIndexResponse) { option (google.api.http).get = "/rollkit/network/v1/validator/{address}"; } - + // SoftConfirmationStatus queries if a height is soft-confirmed rpc SoftConfirmationStatus(QuerySoftConfirmationStatusRequest) returns (QuerySoftConfirmationStatusResponse) { option (google.api.http).get = "/rollkit/network/v1/soft-confirmation/{height}"; } + + // ValidatorSignature queries the signature of a validator for a specific height + rpc ValidatorSignature(QueryValidatorSignatureRequest) returns (QueryValidatorSignatureResponse) { + option (google.api.http).get = "/rollkit/network/v1/signature/{height}/{validator}"; + } } // QueryParamsRequest is the request type for the Query/Params RPC method. @@ -92,4 +97,16 @@ message QuerySoftConfirmationStatusResponse { uint64 voted_power = 2; uint64 total_power = 3; string quorum_fraction = 4; -} \ No newline at end of file +} + +// QueryValidatorSignatureRequest is the request type for the Query/ValidatorSignature RPC method. +message QueryValidatorSignatureRequest { + int64 height = 1; + string validator = 2; +} + +// QueryValidatorSignatureResponse is the response type for the Query/ValidatorSignature RPC method. +message QueryValidatorSignatureResponse { + bytes signature = 1; + bool found = 2; +} diff --git a/pkg/adapter/adapter.go b/pkg/adapter/adapter.go index b204c2b..6c6644c 100644 --- a/pkg/adapter/adapter.go +++ b/pkg/adapter/adapter.go @@ -277,10 +277,10 @@ func (a *Adapter) InitChain(ctx context.Context, genesisTime time.Time, initialH nValSet := cmttypes.NewValidatorSet(vals) - if len(nValSet.Validators) != 1 { - err := fmt.Errorf("expected exactly one validator") - return nil, 0, err - } + //if n := len(nValSet.Validators); n != 1 { + // err := fmt.Errorf("expected exactly one validator but got %d", n) + // return nil, 0, err + //} s.Validators = cmttypes.NewValidatorSet(nValSet.Validators) s.NextValidators = cmttypes.NewValidatorSet(nValSet.Validators).CopyIncrementProposerPriority(1) @@ -731,7 +731,3 @@ outerLoop: a.Logger.Debug("remaining stack after soft consensus", "count", len(a.stackedEvents), "soft_consensus", softCommitHeight) return nil } - -func (a *Adapter) GetExecutionMode() execution.ExecutionMode { - return execution.ExecutionModeDelayed -} diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index a05738a..fd1e542 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -1,6 +1,7 @@ package core import ( + "context" "encoding/binary" "errors" "fmt" @@ -12,10 +13,12 @@ import ( ctypes "github.com/cometbft/cometbft/rpc/core/types" rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types" cmttypes "github.com/cometbft/cometbft/types" - + "github.com/cosmos/gogoproto/proto" storepkg "github.com/rollkit/rollkit/pkg/store" rlktypes "github.com/rollkit/rollkit/types" + abci "github.com/cometbft/cometbft/abci/types" + networktypes "github.com/rollkit/go-execution-abci/modules/network/types" "github.com/rollkit/go-execution-abci/pkg/cometcompat" ) @@ -125,50 +128,14 @@ func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) } } - header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), heightValue) - if err != nil { - return nil, err - } - - lastCommit, err := getLastCommit(ctx.Context(), heightValue) - if err != nil { - return nil, fmt.Errorf("failed to get last commit for block %d: %w", heightValue, err) - } - - block, err := cometcompat.ToABCIBlock(header, data, lastCommit) + block, err := xxxBlock(ctx, heightValue) if err != nil { return nil, err } - // Then re-sign the final ABCI header if we have a signer - if env.Signer != nil { - // Create a vote for the final ABCI header - vote := cmtproto.Vote{ - Type: cmtproto.PrecommitType, - Height: int64(header.Height()), //nolint:gosec - Round: 0, - BlockID: cmtproto.BlockID{ - Hash: block.Header.Hash(), - PartSetHeader: cmtproto.PartSetHeader{}, - }, - Timestamp: block.Time, - ValidatorAddress: header.ProposerAddress, - ValidatorIndex: 0, - } - chainID := header.ChainID() - finalSignBytes := cmttypes.VoteSignBytes(chainID, &vote) - - newSignature, err := env.Signer.Sign(finalSignBytes) - if err != nil { - return nil, fmt.Errorf("failed to sign final ABCI header: %w", err) - } - - // Update the signature in the block - if len(block.LastCommit.Signatures) > 0 { - block.LastCommit.Signatures[0].Signature = newSignature - } + if len(block.LastCommit.Signatures) == 0 { + return nil, nil // not found } - return &ctypes.ResultBlock{ BlockID: cmttypes.BlockID{Hash: block.Hash()}, Block: block, @@ -214,69 +181,53 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro return nil, err } - header, rollkitData, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), height) + block, err := xxxBlock(ctx, height) if err != nil { return nil, err } - // Create a proper commit that will be used for ToABCIBlock - abciCommit := &cmttypes.Commit{ - Height: int64(header.Height()), //nolint:gosec - Round: 0, - BlockID: cmttypes.BlockID{ - Hash: cmbytes.HexBytes(header.Hash()), // This will be updated after ToABCIBlock - PartSetHeader: cmttypes.PartSetHeader{}, + return &ctypes.ResultCommit{ + SignedHeader: cmttypes.SignedHeader{ + Header: &block.Header, + Commit: block.LastCommit, }, - Signatures: []cmttypes.CommitSig{{ - BlockIDFlag: cmttypes.BlockIDFlagCommit, - Signature: header.Signature, // This will be updated if we have a signer - ValidatorAddress: header.ProposerAddress, - Timestamp: header.Time(), - }}, + CanonicalCommit: true, + }, nil +} + +func xxxBlock(ctx *rpctypes.Context, height uint64) (*cmttypes.Block, error) { + fmt.Printf("+++ height: %d\n", height) + // Check if the block has soft confirmation first + isSoftConfirmed, softConfirmationData, err := checkSoftConfirmation(ctx.Context(), height) + if err != nil { + return nil, fmt.Errorf("check soft confirmation status: %w", err) } - block, err := cometcompat.ToABCIBlock(header, rollkitData, abciCommit) + //if !isSoftConfirmed { + // return nil, fmt.Errorf("commit for height %d does not exist (block not soft confirmed)", height) + //} + _ = isSoftConfirmed + + header, rollkitData, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), height) if err != nil { return nil, err } - // Then re-sign the final ABCI header if we have a signer - if env.Signer != nil { - // Create a vote for the final ABCI header - vote := cmtproto.Vote{ - Type: cmtproto.PrecommitType, - Height: int64(header.Height()), //nolint:gosec - Round: 0, - BlockID: cmtproto.BlockID{ - Hash: block.Header.Hash(), - PartSetHeader: cmtproto.PartSetHeader{}, - }, - Timestamp: block.Time, - ValidatorAddress: header.ProposerAddress, - ValidatorIndex: 0, - } - chainID := header.ChainID() - finalSignBytes := cmttypes.VoteSignBytes(chainID, &vote) - - newSignature, err := env.Signer.Sign(finalSignBytes) - if err != nil { - return nil, fmt.Errorf("failed to sign final ABCI header: %w", err) - } + // Build commit with attestations from soft confirmation data + commit, err := buildCommitFromAttestations(ctx.Context(), height, softConfirmationData) + if err != nil { + return nil, fmt.Errorf("build commit from attestations: %w", err) + } - // Update the commit with the new signature - block.LastCommit.Signatures[0].Signature = newSignature + block, err := cometcompat.ToABCIBlock(header, rollkitData, commit) + if err != nil { + return nil, err } // Update the commit's BlockID to match the final ABCI block hash block.LastCommit.BlockID.Hash = block.Header.Hash() - - return &ctypes.ResultCommit{ - SignedHeader: cmttypes.SignedHeader{ - Header: &block.Header, - Commit: block.LastCommit, - }, - CanonicalCommit: true, - }, nil + block.LastCommit.BlockID.PartSetHeader.Hash = block.LastCommit.BlockID.Hash + return block, nil } // BlockResults gets block results at a given height. @@ -391,3 +342,169 @@ func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes. BlockMetas: blocks, }, nil } + +// checkSoftConfirmation checks if a block has soft confirmation and returns the attestation data +func checkSoftConfirmation(ctx context.Context, height uint64) (bool, *networktypes.QueryAttestationBitmapResponse, error) { + // Check soft confirmation status + softConfirmReq := &networktypes.QuerySoftConfirmationStatusRequest{ + Height: int64(height), + } + reqData, err := softConfirmReq.Marshal() + if err != nil { + return false, nil, fmt.Errorf("failed to marshal soft confirmation request: %w", err) + } + + // Query soft confirmation status + abciReq := &abci.RequestQuery{ + Path: "/rollkitsdk.network.v1.Query/SoftConfirmationStatus", + Data: reqData, + } + + abciRes, err := env.Adapter.App.Query(ctx, abciReq) + if err != nil || abciRes.Code != 0 { + var msg string + if abciRes != nil { + msg = abciRes.Log + } + env.Logger.Error("query soft confirmation status", "height", height, "error", err, "log", msg) + return false, nil, fmt.Errorf("failed to query soft confirmation status: %w", err) + } + + softConfirmResp := &networktypes.QuerySoftConfirmationStatusResponse{} + if err := softConfirmResp.Unmarshal(abciRes.Value); err != nil { + return false, nil, fmt.Errorf("failed to unmarshal soft confirmation response: %w", err) + } + + if !softConfirmResp.IsSoftConfirmed { + return false, nil, nil + } + + // Get attestation bitmap data + attestationReq := &networktypes.QueryAttestationBitmapRequest{ + Height: int64(height), + } + reqData, err = attestationReq.Marshal() + if err != nil { + return false, nil, fmt.Errorf("failed to marshal attestation bitmap request: %w", err) + } + + abciReq = &abci.RequestQuery{ + Path: "/rollkitsdk.network.v1.Query/AttestationBitmap", + Data: reqData, + } + + abciRes, err = env.Adapter.App.Query(ctx, abciReq) + if err != nil || abciRes.Code != 0 { + var msg string + if abciRes != nil { + msg = abciRes.Log + } + env.Logger.Error("query attestation bitmap", "height", height, "error", err, "log", msg) + return false, nil, fmt.Errorf("failed to query attestation bitmap: %w", err) + } + + var attestationResp networktypes.QueryAttestationBitmapResponse + if err := attestationResp.Unmarshal(abciRes.Value); err != nil { + return false, nil, fmt.Errorf("failed to unmarshal attestation bitmap response: %w", err) + } + + return true, &attestationResp, nil +} + +// buildCommitFromAttestations constructs a commit with real signatures from attestations +func buildCommitFromAttestations(ctx context.Context, height uint64, attestationData *networktypes.QueryAttestationBitmapResponse) (*cmttypes.Commit, error) { + // Get validators from genesis (since we know there's exactly one validator) + genesisValidators := env.Adapter.AppGenesis.Consensus.Validators + if len(genesisValidators) == 0 { + return nil, fmt.Errorf("no validators found in genesis") + } + votes := make([]cmttypes.CommitSig, len(genesisValidators)) + + var bitmap []byte + if attestationData != nil { + if bitmap = attestationData.Bitmap.Bitmap; bitmap == nil { + return nil, fmt.Errorf("no attestation bitmap found for height %d", height) + } + } + // Iterate only through the actual validators (not all bits in bitmap) + for i, genesisValidator := range genesisValidators { + // Check if this validator voted (bit is set in bitmap) + if attestationData != nil && i < len(bitmap)*8 && (bitmap[i/8]&(1<<(i%8))) != 0 { + // Try to get the real signature using the validator's address + validatorAddr := string(genesisValidator.Address.Bytes()) // todo (Alex): use proper format + vote, err := getValidatorSignatureFromQuery(ctx, int64(height), validatorAddr) + if err != nil { + return nil, fmt.Errorf("get validator signature for height %d: %w", height, err) + } + + votes[i] = cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: vote.ValidatorAddress, + Timestamp: vote.Timestamp, + Signature: vote.Signature, + } + + } else { + // Validator didn't vote, add absent vote + votes[i] = cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagAbsent, + } + } + } + + commit := &cmttypes.Commit{ + Height: int64(height), + Round: 0, // Default round + BlockID: cmttypes.BlockID{ + Hash: make([]byte, 32), // Should be actual block hash + PartSetHeader: cmttypes.PartSetHeader{ + Total: 1, + Hash: make([]byte, 32), + }, + }, + Signatures: votes, + } + + return commit, nil +} + +// getValidatorSignatureFromQuery queries the signature for a specific validator +func getValidatorSignatureFromQuery(ctx context.Context, height int64, validatorAddr string) (*cmtproto.Vote, error) { + sigReq := &networktypes.QueryValidatorSignatureRequest{ + BlockHeight: height, + Validator: validatorAddr, + } + + reqData, err := sigReq.Marshal() + if err != nil { + return nil, fmt.Errorf("failed to marshal signature request: %w", err) + } + + fmt.Printf("+++ getValidatorSignatureFromQuery addr: %X, height: %d\n", []byte(validatorAddr), height) + + abciReq := &abci.RequestQuery{ + Path: "/rollkitsdk.network.v1.Query/ValidatorSignature", + Data: reqData, + } + + res, err := env.Adapter.App.Query(ctx, abciReq) + if err != nil { + return nil, fmt.Errorf("signature query failed: %w", err) + } + if res.Code != 0 { + return nil, fmt.Errorf("signature query failed: %s", res.Log) + } + + var sigResp networktypes.QueryValidatorSignatureResponse + if err := sigResp.Unmarshal(res.Value); err != nil { + return nil, fmt.Errorf("failed to unmarshal signature response: %w", err) + } + if !sigResp.Found { + return nil, fmt.Errorf("vote not found") + } + var vote cmtproto.Vote + if err := proto.Unmarshal(sigResp.Signature, &vote); err != nil { + return nil, fmt.Errorf("failed to unmarshal signature payload: %w", err) + } + return &vote, nil +} diff --git a/pkg/rpc/core/blocks_test.go b/pkg/rpc/core/blocks_test.go index c2e7bec..125267d 100644 --- a/pkg/rpc/core/blocks_test.go +++ b/pkg/rpc/core/blocks_test.go @@ -256,7 +256,7 @@ func signBlock(t *testing.T, header types.Header, data *types.Data, privKey cryp Signature: types.Signature(make([]byte, 64)), } - abciBlock, err := cometcompat.ToABCIBlock(tempSignedHeader, data, tempCommit, nil) // todo (Alex): set correct valset + abciBlock, err := cometcompat.ToABCIBlock(tempSignedHeader, data, tempCommit) require.NoError(t, err) vote := cmtproto.Vote{ diff --git a/server/start.go b/server/start.go index 457b54a..a72aa92 100644 --- a/server/start.go +++ b/server/start.go @@ -681,6 +681,9 @@ func loadRollkitMigrationGenesis(rootDir string) (*rollkitMigrationGenesis, erro // This is used for normal startup scenarios where a full cometbft genesis document // is available and contains all the necessary information. func createRollkitGenesisFromCometBFT(cmtGenDoc *cmttypes.GenesisDoc) *genesis.Genesis { + if len(cmtGenDoc.Validators) == 0 { + panic("no .validators in genesis") + } rollkitGenesis := genesis.NewGenesis( cmtGenDoc.ChainID, uint64(cmtGenDoc.InitialHeight),