diff --git a/config/api_config.go b/config/api_config.go index 25a77f6a..e68d73c4 100644 --- a/config/api_config.go +++ b/config/api_config.go @@ -1,3 +1,6 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + package config import ( diff --git a/go.mod b/go.mod index 584b6c2d..301b50cf 100644 --- a/go.mod +++ b/go.mod @@ -11,8 +11,8 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 github.com/ethereum/go-ethereum v1.13.8 github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/onsi/ginkgo/v2 v2.20.1 - github.com/onsi/gomega v1.34.1 + github.com/onsi/ginkgo/v2 v2.20.2 + github.com/onsi/gomega v1.34.2 github.com/pingcap/errors v0.11.4 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.20.2 @@ -22,7 +22,7 @@ require ( github.com/stretchr/testify v1.9.0 go.uber.org/mock v0.4.0 go.uber.org/zap v1.27.0 - google.golang.org/grpc v1.65.0 + google.golang.org/grpc v1.66.0 google.golang.org/protobuf v1.34.2 ) @@ -80,7 +80,7 @@ require ( github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect + github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/google/renameio/v2 v2.0.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.0 // indirect @@ -146,14 +146,14 @@ require ( golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/net v0.28.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sys v0.24.0 // indirect golang.org/x/term v0.23.0 // indirect golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.24.0 // indirect gonum.org/v1/gonum v0.11.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 556ff3b2..4be54bcb 100644 --- a/go.sum +++ b/go.sum @@ -336,8 +336,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= @@ -494,16 +494,16 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= -github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= @@ -851,8 +851,8 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= @@ -1003,10 +1003,10 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= -google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= +google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1025,8 +1025,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= +google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/relayer/config/config.go b/relayer/config/config.go index 57ea0822..3f643dd2 100644 --- a/relayer/config/config.go +++ b/relayer/config/config.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "net/url" + "time" basecfg "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/peers" @@ -67,6 +68,9 @@ type Config struct { DeciderURL string `mapstructure:"decider-url" json:"decider-url"` SignatureCacheSize uint64 `mapstructure:"signature-cache-size" json:"signature-cache-size"` + // mapstructure doesn't handle time.Time out of the box so handle it manually + EtnaTime time.Time `json:"etna-time"` + // convenience field to fetch a blockchain's subnet ID blockchainIDToSubnetID map[ids.ID]ids.ID overwrittenOptions []string diff --git a/relayer/config/keys.go b/relayer/config/keys.go index 1c426fe0..fa211fc2 100644 --- a/relayer/config/keys.go +++ b/relayer/config/keys.go @@ -24,4 +24,5 @@ const ( ManualWarpMessagesKey = "manual-warp-messages" DBWriteIntervalSecondsKey = "db-write-interval-seconds" SignatureCacheSizeKey = "signature-cache-size" + EtnaTimeKey = "etna-time" ) diff --git a/relayer/config/viper.go b/relayer/config/viper.go index 507bd952..4e0e279b 100644 --- a/relayer/config/viper.go +++ b/relayer/config/viper.go @@ -84,6 +84,9 @@ func BuildConfig(v *viper.Viper) (Config, error) { return cfg, fmt.Errorf("failed to unmarshal viper config: %w", err) } + // Manually set EtnaTime field since it's not automatically parseable using mapstructure + cfg.EtnaTime = v.GetTime(EtnaTimeKey) + // Explicitly overwrite the configured account private key // If account-private-key is set as a flag or environment variable, // overwrite all destination subnet configurations to use that key diff --git a/relayer/main/main.go b/relayer/main/main.go index 358561c7..3ee92351 100644 --- a/relayer/main/main.go +++ b/relayer/main/main.go @@ -227,6 +227,7 @@ func main() { prometheus.DefaultRegisterer, ), messageCreator, + cfg.EtnaTime, ) if err != nil { logger.Fatal("Failed to create signature aggregator", zap.Error(err)) diff --git a/signature-aggregator/README.md b/signature-aggregator/README.md index 413a65d6..3f8b02ac 100644 --- a/signature-aggregator/README.md +++ b/signature-aggregator/README.md @@ -59,9 +59,9 @@ curl --location 'https://api.avax-test.network/ext/bc/C/rpc' \ The topic of the message will be `0x56600c567728a800c0aa927500f831cb451df66a7af570eb4df4dfbf4674887d` which is the output of`cast keccak "SendWarpMessage(address,bytes32,bytes)"` 4. Use the data field of the log message found in step 2 and send it to the locally running service via curl. ```bash -curl --location 'http://localhost:8080/aggregate-signatures/by-raw-message' \ +curl --location 'http://localhost:8080/aggregate-signatures' \ --header 'Content-Type: application/json' \ --data '{ - "data": "", + "message": "" }' ``` diff --git a/signature-aggregator/aggregator/aggregator.go b/signature-aggregator/aggregator/aggregator.go index 01817491..cea134a7 100644 --- a/signature-aggregator/aggregator/aggregator.go +++ b/signature-aggregator/aggregator/aggregator.go @@ -19,6 +19,7 @@ import ( "github.com/ava-labs/avalanchego/proto/pb/p2p" "github.com/ava-labs/avalanchego/proto/pb/sdk" "github.com/ava-labs/avalanchego/subnets" + "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/crypto/bls" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" @@ -27,6 +28,8 @@ import ( "github.com/ava-labs/awm-relayer/signature-aggregator/aggregator/cache" "github.com/ava-labs/awm-relayer/signature-aggregator/metrics" "github.com/ava-labs/awm-relayer/utils" + corethMsg "github.com/ava-labs/coreth/plugin/evm/message" + msg "github.com/ava-labs/subnet-evm/plugin/evm/message" "go.uber.org/zap" "google.golang.org/protobuf/proto" ) @@ -42,6 +45,9 @@ const ( ) var ( + codec = msg.Codec + corethCodec = corethMsg.Codec + // Errors errNotEnoughSignatures = errors.New("failed to collect a threshold of signatures") errNotEnoughConnectedStake = errors.New("failed to connect to a threshold of stake") @@ -57,6 +63,7 @@ type SignatureAggregator struct { subnetsMapLock sync.RWMutex metrics *metrics.SignatureAggregatorMetrics cache *cache.Cache + etnaTime time.Time } func NewSignatureAggregator( @@ -65,6 +72,7 @@ func NewSignatureAggregator( signatureCacheSize uint64, metrics *metrics.SignatureAggregatorMetrics, messageCreator message.Creator, + etnaTime time.Time, ) (*SignatureAggregator, error) { cache, err := cache.NewCache(signatureCacheSize, logger) if err != nil { @@ -81,6 +89,7 @@ func NewSignatureAggregator( messageCreator: messageCreator, currentRequestID: atomic.Uint32{}, cache: cache, + etnaTime: etnaTime, } sa.currentRequestID.Store(rand.Uint32()) return &sa, nil @@ -174,14 +183,7 @@ func (s *SignatureAggregator) CreateSignedMessage( )) } - reqBytes := networkP2P.ProtocolPrefix(networkP2P.SignatureRequestHandlerID) - messageBytes, err := proto.Marshal( - &sdk.SignatureRequest{ - Message: unsignedMessage.Bytes(), - Justification: justification, - }, - ) - reqBytes = append(reqBytes, messageBytes...) + reqBytes, err := s.marshalRequest(unsignedMessage, justification, sourceSubnet) if err != nil { msg := "Failed to marshal request bytes" s.logger.Error( @@ -517,15 +519,13 @@ func (s *SignatureAggregator) isValidSignatureResponse( return blsSignatureBuf{}, false } - sigResponse := sdk.SignatureResponse{} - err := proto.Unmarshal(appResponse.AppBytes, &sigResponse) + signature, err := s.unmarshalResponse(appResponse.AppBytes) if err != nil { s.logger.Error( "Error unmarshaling signature response", zap.Error(err), ) } - signature := sigResponse.Signature // If the node returned an empty signature, then it has not yet seen the warp message. Retry later. emptySignature := blsSignatureBuf{} @@ -560,9 +560,8 @@ func (s *SignatureAggregator) isValidSignatureResponse( ) return blsSignatureBuf{}, false } - blsSig := blsSignatureBuf{} - copy(blsSig[:], signature[:]) - return blsSig, true + + return signature, true } // aggregateSignatures constructs a BLS aggregate signature from the collected validator signatures. Also @@ -594,3 +593,61 @@ func (s *SignatureAggregator) aggregateSignatures( } return aggSig, vdrBitSet, nil } + +// TODO: refactor this to remove special handling based on etnaTime +// after Etna release, along with related config and testing code +func (s *SignatureAggregator) marshalRequest( + unsignedMessage *avalancheWarp.UnsignedMessage, + justification []byte, + sourceSubnet ids.ID, +) ([]byte, error) { + if !s.etnaTime.IsZero() && s.etnaTime.Before(time.Now()) { + // Post-Etna case + messageBytes, err := proto.Marshal( + &sdk.SignatureRequest{ + Message: unsignedMessage.Bytes(), + Justification: justification, + }, + ) + if err != nil { + return nil, err + } + return networkP2P.PrefixMessage( + networkP2P.ProtocolPrefix(networkP2P.SignatureRequestHandlerID), + messageBytes, + ), nil + } else { + // Pre-Etna case + if sourceSubnet == constants.PrimaryNetworkID { + req := corethMsg.MessageSignatureRequest{ + MessageID: unsignedMessage.ID(), + } + return corethMsg.RequestToBytes(corethCodec, req) + } else { + req := msg.MessageSignatureRequest{ + MessageID: unsignedMessage.ID(), + } + return msg.RequestToBytes(codec, req) + } + } +} + +func (s *SignatureAggregator) unmarshalResponse(responseBytes []byte) (blsSignatureBuf, error) { + if !s.etnaTime.IsZero() && s.etnaTime.Before(time.Now()) { + // Post-Etna case + var sigResponse sdk.SignatureResponse + err := proto.Unmarshal(responseBytes, &sigResponse) + if err != nil { + return blsSignatureBuf{}, err + } + return blsSignatureBuf(sigResponse.Signature), nil + } else { + // Pre-Etna case + var sigResponse msg.SignatureResponse + _, err := msg.Codec.Unmarshal(responseBytes, &sigResponse) + if err != nil { + return blsSignatureBuf{}, err + } + return sigResponse.Signature, nil + } +} diff --git a/signature-aggregator/config/config.go b/signature-aggregator/config/config.go index 19b769d2..ec6d570e 100644 --- a/signature-aggregator/config/config.go +++ b/signature-aggregator/config/config.go @@ -5,6 +5,7 @@ package config import ( "fmt" + "time" "github.com/ava-labs/avalanchego/utils/logging" basecfg "github.com/ava-labs/awm-relayer/config" @@ -34,6 +35,9 @@ type Config struct { APIPort uint16 `mapstructure:"api-port" json:"api-port"` MetricsPort uint16 `mapstructure:"metrics-port" json:"metrics-port"` SignatureCacheSize uint64 `mapstructure:"signature-cache-size" json:"signature-cache-size"` + + // mapstructure doesn't support time.Time out of the box so handle it manually + EtnaTime time.Time `json:"etna-time"` } func DisplayUsageText() { diff --git a/signature-aggregator/config/keys.go b/signature-aggregator/config/keys.go index 97829f77..d2f3e077 100644 --- a/signature-aggregator/config/keys.go +++ b/signature-aggregator/config/keys.go @@ -16,4 +16,5 @@ const ( APIPortKey = "api-port" MetricsPortKey = "metrics-port" SignatureCacheSizeKey = "signature-cache-size" + EtnaTimeKey = "etna-time" ) diff --git a/signature-aggregator/config/viper.go b/signature-aggregator/config/viper.go index dc33f918..0ca4a410 100644 --- a/signature-aggregator/config/viper.go +++ b/signature-aggregator/config/viper.go @@ -76,5 +76,8 @@ func BuildConfig(v *viper.Viper) (Config, error) { return cfg, fmt.Errorf("failed to unmarshal viper config: %w", err) } + // mapstructure doesn't support time.Time out of the box so handle it manually + cfg.EtnaTime = v.GetTime(EtnaTimeKey) + return cfg, nil } diff --git a/signature-aggregator/main/main.go b/signature-aggregator/main/main.go index 69b619a1..27edfd3b 100644 --- a/signature-aggregator/main/main.go +++ b/signature-aggregator/main/main.go @@ -117,6 +117,7 @@ func main() { cfg.SignatureCacheSize, metricsInstance, messageCreator, + cfg.EtnaTime, ) if err != nil { logger.Fatal("Failed to create signature aggregator", zap.Error(err)) diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 48a61808..4a475334 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -174,4 +174,7 @@ var _ = ginkgo.Describe("[AWM Relayer Integration Tests", func() { ginkgo.It("Signature Aggregator", func() { SignatureAggregatorAPI(localNetworkInstance) }) + ginkgo.It("Etna Upgrade", func() { + EtnaUpgrade(localNetworkInstance) + }) }) diff --git a/tests/etna_upgrade.go b/tests/etna_upgrade.go new file mode 100644 index 00000000..ce39d824 --- /dev/null +++ b/tests/etna_upgrade.go @@ -0,0 +1,93 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package tests + +import ( + "context" + "time" + + testUtils "github.com/ava-labs/awm-relayer/tests/utils" + "github.com/ava-labs/teleporter/tests/interfaces" + "github.com/ava-labs/teleporter/tests/utils" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/log" + . "github.com/onsi/gomega" +) + +// This tests basic functionality of the relayer post-etna upgrade +// since other tests write zero value of time.Time +// to the config and therefore are testing the pre-etna case +// - Relaying from Subnet A to Subnet B +// - Relaying from Subnet B to Subnet A +func EtnaUpgrade(network interfaces.LocalNetwork) { + subnetAInfo := network.GetPrimaryNetworkInfo() + subnetBInfo, _ := utils.GetTwoSubnets(network) + fundedAddress, fundedKey := network.GetFundedAccountInfo() + teleporterContractAddress := network.GetTeleporterContractAddress() + err := testUtils.ClearRelayerStorage() + Expect(err).Should(BeNil()) + // + // Fund the relayer address on all subnets + // + ctx := context.Background() + + log.Info("Funding relayer address on all subnets") + relayerKey, err := crypto.GenerateKey() + Expect(err).Should(BeNil()) + testUtils.FundRelayers(ctx, []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, fundedKey, relayerKey) + + // + // Set up relayer config + // + relayerConfig := testUtils.CreateDefaultRelayerConfig( + []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, + []interfaces.SubnetTestInfo{subnetAInfo, subnetBInfo}, + teleporterContractAddress, + fundedAddress, + relayerKey, + ) + relayerConfig.EtnaTime = time.Now().AddDate(0, 0, -1) + // The config needs to be validated in order to be passed to database.GetConfigRelayerIDs + relayerConfig.Validate() + + relayerConfigPath := testUtils.WriteRelayerConfig(relayerConfig, testUtils.DefaultRelayerCfgFname) + + // + // Test Relaying from Subnet A to Subnet B + // + log.Info("Test Relaying from Subnet A to Subnet B") + + log.Info("Starting the relayer") + relayerCleanup, readyChan := testUtils.RunRelayerExecutable( + ctx, + relayerConfigPath, + relayerConfig, + ) + defer relayerCleanup() + + // Wait for relayer to start up + startupCtx, startupCancel := context.WithTimeout(ctx, 15*time.Second) + defer startupCancel() + testUtils.WaitForChannelClose(startupCtx, readyChan) + + log.Info("Sending transaction from Subnet A to Subnet B") + testUtils.RelayBasicMessage( + ctx, + subnetAInfo, + subnetBInfo, + teleporterContractAddress, + fundedKey, + fundedAddress, + ) + + log.Info("Test Relaying from Subnet B to Subnet A") + testUtils.RelayBasicMessage( + ctx, + subnetBInfo, + subnetAInfo, + teleporterContractAddress, + fundedKey, + fundedAddress, + ) +}