From 596a61f039506ecf4c4ba3ad75a320b0739525ef Mon Sep 17 00:00:00 2001 From: aeddi Date: Sat, 17 Aug 2024 00:09:44 +0200 Subject: [PATCH 01/13] feat(gnostats): add required info to NodeInfoOther --- tm2/pkg/bft/node/node.go | 7 +++++++ tm2/pkg/p2p/node_info.go | 3 +++ 2 files changed, 10 insertions(+) diff --git a/tm2/pkg/bft/node/node.go b/tm2/pkg/bft/node/node.go index e29de3dd1ae..9a8f341de92 100644 --- a/tm2/pkg/bft/node/node.go +++ b/tm2/pkg/bft/node/node.go @@ -8,6 +8,7 @@ import ( "log/slog" "net" "net/http" + "runtime" "strings" "sync" "time" @@ -895,6 +896,12 @@ func makeNodeInfo( Other: p2p.NodeInfoOther{ TxIndex: txIndexerStatus, RPCAddress: config.RPC.ListenAddress, + OS: runtime.GOOS, + Arch: runtime.GOARCH, + // @TODO: Check with @zivkovicmilos if we need to add a field + // to the node config along with a cmd flag to opt-in for + // automatic location detection (+ a way to set it manually?) + Location: "", }, } diff --git a/tm2/pkg/p2p/node_info.go b/tm2/pkg/p2p/node_info.go index 48ba8f7776b..885b91bea47 100644 --- a/tm2/pkg/p2p/node_info.go +++ b/tm2/pkg/p2p/node_info.go @@ -45,6 +45,9 @@ type NodeInfo struct { type NodeInfoOther struct { TxIndex string `json:"tx_index"` RPCAddress string `json:"rpc_address"` + OS string `json:"os"` + Arch string `json:"arch"` + Location string `json:"location"` } // Validate checks the self-reported NodeInfo is safe. From 21bd18f83f7bb0fb0e904ebf298d404e1da91136 Mon Sep 17 00:00:00 2001 From: aeddi Date: Sat, 17 Aug 2024 00:11:49 +0200 Subject: [PATCH 02/13] feat(gnostats): implement collector --- contribs/gnostats/agent/collector.go | 156 +++++++++++++++++++++++ contribs/gnostats/go.mod | 31 ++++- contribs/gnostats/go.sum | 180 +++++++++++++++++++++++++-- 3 files changed, 354 insertions(+), 13 deletions(-) create mode 100644 contribs/gnostats/agent/collector.go diff --git a/contribs/gnostats/agent/collector.go b/contribs/gnostats/agent/collector.go new file mode 100644 index 00000000000..8f34c911c65 --- /dev/null +++ b/contribs/gnostats/agent/collector.go @@ -0,0 +1,156 @@ +package agent + +import ( + "context" + "fmt" + + ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" + + "github.com/gnolang/gnostats/proto" +) + +type collector struct { + // rpcClient is used by the collector to send RPC request to the Gno node + caller rpcClient +} + +// RPCClient and RPCBatch are local interfaces that include only the +// required methods to make them easily mockable ands allow testing +// of the collector +type rpcClient interface { + NewBatch() rpcBatch +} +type rpcBatch interface { + Status() error + NetInfo() error + NumUnconfirmedTxs() error + Block(*uint64) error + BlockResults(*uint64) error + Send(context.Context) ([]any, error) +} + +// CollectDynamic collects dynamic info from the Gno node using RPC +func (c *collector) CollectDynamic(ctx context.Context) (*proto.DynamicInfo, error) { + // Create a new batch of RPC requests + batch := c.caller.NewBatch() + + for _, request := range [](func() error){ + // Request Status to get address, moniker and validator info + batch.Status, + // Request NetInfo to get peers info + batch.NetInfo, + // Request NumUnconfirmedTxs to get pending txs + batch.NumUnconfirmedTxs, + // Request Block to get the last block timestamp and proposer + func() error { return batch.Block(nil) }, + // Request BlockResults to get the last block number, gas used and gas wanted + func() error { return batch.BlockResults(nil) }, + } { + if err := request(); err != nil { + return nil, fmt.Errorf("unable to batch request: %w", err) + } + } + + // Send the batch of requests + results, err := batch.Send(ctx) + if err != nil { + return nil, fmt.Errorf("unable to send batch: %w", err) + } + + // Cast responses to the appropriate types + var ( + status = results[0].(*ctypes.ResultStatus) + netInfo = results[1].(*ctypes.ResultNetInfo) + uncTxs = results[2].(*ctypes.ResultUnconfirmedTxs) + blk = results[3].(*ctypes.ResultBlock) + blkRes = results[4].(*ctypes.ResultBlockResults) + ) + + // Convert the list of peers from NetInfo to proto type + peers := make([]*proto.PeerInfo, len(netInfo.Peers)) + for i, peer := range netInfo.Peers { + peers[i] = &proto.PeerInfo{ + Moniker: peer.NodeInfo.Moniker, + } + if peer.NodeInfo.NetAddress != nil { + peers[i].P2PAddress = peer.NodeInfo.NetAddress.String() + } + } + + // Determine if the node is a validator using validatorInfo + // @TODO: Check if this is right with @zivkovicmilos + isValidator := status.ValidatorInfo.Address.ID().String() != "" + + // Get gas used / wanted in DeliverTxs (if any) + var gasUsed, gasWanted uint64 + if blkRes.Results != nil && len(blkRes.Results.DeliverTxs) > 0 { + gasUsed = uint64(blkRes.Results.DeliverTxs[0].GasUsed) + gasWanted = uint64(blkRes.Results.DeliverTxs[0].GasWanted) + } + + // Fill the DynamicInfo fields with the corresponding values + return &proto.DynamicInfo{ + Address: status.NodeInfo.ID().String(), + Moniker: status.NodeInfo.Moniker, + IsValidator: isValidator, + NetInfo: &proto.NetInfo{ + P2PAddress: status.NodeInfo.NetAddress.String(), + Peers: peers, + }, + PendingTxs: uint64(uncTxs.Total), + BlockInfo: &proto.BlockInfo{ + Number: uint64(blk.Block.Height), + Timestamp: uint64(blk.Block.Time.Unix()), + GasUsed: gasUsed, + GasWanted: gasWanted, + Proposer: blk.Block.ProposerAddress.ID().String(), + }, + }, nil +} + +// CollectStatic collects static info on the Gno node using RPC +func (c *collector) CollectStatic(ctx context.Context) (*proto.StaticInfo, error) { + // Use a batch instead of a single request to allow passing a context + batch := c.caller.NewBatch() + + // Request Status to get all node info + if err := batch.Status(); err != nil { + return nil, fmt.Errorf("unable to batch request: %w", err) + } + + // Send the batch of requests + results, err := batch.Send(ctx) + if err != nil { + return nil, fmt.Errorf("unable to send batch: %w", err) + } + + // Cast response to the appropriate type + status := results[0].(*ctypes.ResultStatus) + + // Format the OsVersion string + var ( + osVersion = "" + os = status.NodeInfo.Other.OS + arch = status.NodeInfo.Other.Arch + ) + if os != "" && arch != "" { + osVersion = fmt.Sprintf("%s - %s", os, arch) + } else if os != "" { + osVersion = os + } + + // Fill the StaticInfo fields with the corresponding values + return &proto.StaticInfo{ + Address: status.NodeInfo.ID().String(), + GnoVersion: status.NodeInfo.Version, + OsVersion: osVersion, + Location: "", // @TODO: see comment in makeNodeInfo + }, nil +} + +// NewCollector creates a new collector using the provided RPC client +func NewCollector(caller rpcClient) *collector { + return &collector{ + caller: caller, + } +} diff --git a/contribs/gnostats/go.mod b/contribs/gnostats/go.mod index 990c8193335..c713e9a79ff 100644 --- a/contribs/gnostats/go.mod +++ b/contribs/gnostats/go.mod @@ -3,6 +3,7 @@ module github.com/gnolang/gnostats go 1.22 require ( + github.com/gnolang/gno v0.1.1 github.com/rs/xid v1.5.0 github.com/stretchr/testify v1.9.0 google.golang.org/grpc v1.65.0 @@ -10,11 +11,33 @@ require ( ) require ( + github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect + github.com/btcsuite/btcd/btcutil v1.1.5 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect + golang.org/x/mod v0.19.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/contribs/gnostats/go.sum b/contribs/gnostats/go.sum index 9a24cff5df7..e4a0905d210 100644 --- a/contribs/gnostats/go.sum +++ b/contribs/gnostats/go.sum @@ -1,26 +1,188 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd h1:js1gPwhcFflTZ7Nzl7WHaOTlTr5hIrR4n1NM4v9n4Kw= +github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= +github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= +github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= +github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= +github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= +github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= +github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= +github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= +github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +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/gnolang/gno v0.1.1 h1:t41S0SWIUa3syI7XpRAuCneCgRc8gOJ2g8DkUedF72U= +github.com/gnolang/gno v0.1.1/go.mod h1:BTaBNeaoY/W95NN6QA4RCoQ6Z7mi8M+Zb1I1wMWGg2w= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216 h1:GKvsK3oLWG9B1GL7WP/VqwM6C92j5tIvB844oggL9Lk= +github.com/gnolang/overflow v0.0.0-20170615021017-4d914c927216/go.mod h1:xJhtEL7ahjM1WJipt89gel8tHzfIl/LyMY+lCYh38d8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/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/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -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= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= +go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0/go.mod h1:yeGZANgEcpdx/WK0IvvRFC+2oLiMS2u4L/0Rj2M2Qr0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 h1:aLmmtjRke7LPDQ3lvpFz+kNEH43faFhzW7v8BFIEydg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0/go.mod h1:TC1pyCt6G9Sjb4bQpShH+P5R53pO6ZuGnHuuln9xMeE= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08= +go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= +google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= 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/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= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From a39c99c5c41377b08890b15ac6f008fa67d7cc35 Mon Sep 17 00:00:00 2001 From: aeddi Date: Sat, 17 Aug 2024 00:13:57 +0200 Subject: [PATCH 03/13] test(gnostats): add collector unit tests --- contribs/gnostats/agent/collector_test.go | 370 ++++++++++++++++++++++ contribs/gnostats/go.mod | 1 + contribs/gnostats/go.sum | 2 + 3 files changed, 373 insertions(+) create mode 100644 contribs/gnostats/agent/collector_test.go diff --git a/contribs/gnostats/agent/collector_test.go b/contribs/gnostats/agent/collector_test.go new file mode 100644 index 00000000000..4d02db3d1de --- /dev/null +++ b/contribs/gnostats/agent/collector_test.go @@ -0,0 +1,370 @@ +package agent + +import ( + "context" + "fmt" + "testing" + "time" + + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" + "github.com/gnolang/gno/tm2/pkg/bft/state" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/gnolang/gnostats/proto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +// MockRPCClient is a mock of the rpcClient used for testing +type MockRPCClient struct { + mock.Mock +} + +func (m *MockRPCClient) NewBatch() rpcBatch { + args := m.Called() + return args.Get(0).(rpcBatch) +} + +// MockRPCBatch is a mock of the rpcBatch used for testing +type MockRPCBatch struct { + mock.Mock + + sendLatency time.Duration // Duration used to simulate a latency when calling Send() +} + +func (m *MockRPCBatch) Status() error { + args := m.Called() + return args.Error(0) +} + +func (m *MockRPCBatch) NetInfo() error { + args := m.Called() + return args.Error(0) +} + +func (m *MockRPCBatch) NumUnconfirmedTxs() error { + args := m.Called() + return args.Error(0) +} + +func (m *MockRPCBatch) Block(height *uint64) error { + args := m.Called(height) + return args.Error(0) +} + +func (m *MockRPCBatch) BlockResults(height *uint64) error { + args := m.Called(height) + return args.Error(0) +} + +// Send mock method supporting latency simulation to test context timeout +func (m *MockRPCBatch) Send(ctx context.Context) ([]any, error) { + args := m.Called(ctx) + + select { + case <-ctx.Done(): + return args.Get(0).([]any), ctx.Err() + case <-time.After(m.sendLatency): + return args.Get(0).([]any), args.Error(1) + } +} + +// mockNetAddr is a mock used for p2p.NewNetAddress() +type mockNetAddr struct { + network string + str string +} + +func (m mockNetAddr) Network() string { return m.network } +func (m mockNetAddr) String() string { return m.str } + +// Helper that generates a valid RPC batch result +func getBatchResults(t *testing.T) []any { + t.Helper() + + // Generate peers for NetInfo request + peers := make([]p2p.NodeInfo, 3) + for i, info := range []struct{ moniker, address string }{ + {"peer1", "1.1.1.1"}, + {"peer2", "2.2.2.2"}, + {"peer3", "3.3.3.3"}, + } { + peers[i].Moniker = info.moniker + peers[i].NetAddress = p2p.NewNetAddress( + crypto.ID(info.moniker), + mockNetAddr{ + network: "tcp", + str: info.address, + }, + ) + } + + return []any{ + &ctypes.ResultStatus{ + NodeInfo: p2p.NodeInfo{ + Moniker: "self", + NetAddress: p2p.NewNetAddress( + crypto.ID("self"), + mockNetAddr{ + network: "tcp", + str: "0.0.0.0", + }, + ), + Other: p2p.NodeInfoOther{ + OS: "plan9", + Arch: "ppc64", + Location: "", + }, + }, + }, + + &ctypes.ResultNetInfo{ + Peers: []ctypes.Peer{ + {NodeInfo: peers[0]}, + {NodeInfo: peers[1]}, + {NodeInfo: peers[2]}, + }, + }, + + &ctypes.ResultUnconfirmedTxs{Total: 5}, + + &ctypes.ResultBlock{ + Block: &types.Block{ + Header: types.Header{ + Height: 10, + Time: time.Now(), + ProposerAddress: crypto.Address{}, + }, + }, + }, + + &ctypes.ResultBlockResults{ + Results: &state.ABCIResponses{ + DeliverTxs: []abci.ResponseDeliverTx{{ + GasUsed: 100, + GasWanted: 200, + }}, + }, + }, + } +} + +func compareBatchResultToDynamicInfo(t *testing.T, results []any, dynamicInfo *proto.DynamicInfo) { + t.Helper() + + var ( + status = results[0].(*ctypes.ResultStatus) + netInfo = results[1].(*ctypes.ResultNetInfo) + uncTxs = results[2].(*ctypes.ResultUnconfirmedTxs) + blk = results[3].(*ctypes.ResultBlock) + blkRes = results[4].(*ctypes.ResultBlockResults) + ) + + assert.Equal(t, dynamicInfo.Address, status.NodeInfo.ID().String()) + assert.Equal(t, dynamicInfo.Moniker, status.NodeInfo.Moniker) + assert.Equal(t, dynamicInfo.IsValidator, status.ValidatorInfo.Address.ID().String() != "") + + assert.NotNil(t, dynamicInfo.NetInfo) + assert.Equal(t, dynamicInfo.NetInfo.P2PAddress, status.NodeInfo.NetAddress.String()) + assert.Equal(t, len(dynamicInfo.NetInfo.Peers), len(netInfo.Peers)) + for i, peer := range dynamicInfo.NetInfo.Peers { + assert.Equal(t, peer.Moniker, netInfo.Peers[i].NodeInfo.Moniker) + assert.NotNil(t, netInfo.Peers[i].NodeInfo.NetAddress) + assert.Equal(t, peer.P2PAddress, netInfo.Peers[i].NodeInfo.NetAddress.String()) + } + + assert.Equal(t, dynamicInfo.PendingTxs, uint64(uncTxs.Total)) + + assert.NotNil(t, dynamicInfo.BlockInfo) + assert.Equal(t, dynamicInfo.BlockInfo.Number, uint64(blk.Block.Height)) + assert.Equal(t, dynamicInfo.BlockInfo.Timestamp, uint64(blk.Block.Time.Unix())) + assert.Equal(t, dynamicInfo.BlockInfo.GasUsed, uint64(blkRes.Results.DeliverTxs[0].GasUsed)) + assert.Equal(t, dynamicInfo.BlockInfo.GasWanted, uint64(blkRes.Results.DeliverTxs[0].GasWanted)) + assert.Equal(t, dynamicInfo.BlockInfo.Proposer, blk.Block.ProposerAddress.ID().String()) +} + +func TestCollector_DynamicSuccess(t *testing.T) { + t.Parallel() + + // Setup RPC mocks + mockCaller := new(MockRPCClient) + mockBatch := new(MockRPCBatch) + + mockCaller.On("NewBatch").Return(mockBatch) + mockBatch.On("Status").Return(nil) + mockBatch.On("NetInfo").Return(nil) + mockBatch.On("NumUnconfirmedTxs").Return(nil) + mockBatch.On("Block", (*uint64)(nil)).Return(nil) + mockBatch.On("BlockResults", (*uint64)(nil)).Return(nil) + + ctx := context.Background() + results := getBatchResults(t) + mockBatch.On("Send", ctx).Return(results, nil) + + // Call the actual method to test (CollectDynamic) + c := &collector{caller: mockCaller} + dynamicInfo, err := c.CollectDynamic(ctx) + + // Assert that all expectations were met + assert.NoError(t, err) + assert.NotNil(t, dynamicInfo) + compareBatchResultToDynamicInfo(t, results, dynamicInfo) +} + +func TestCollector_DynamicFail(t *testing.T) { + t.Parallel() + + // Setup RPC mocks + mockCaller := new(MockRPCClient) + mockBatch := new(MockRPCBatch) + + mockCaller.On("NewBatch").Return(mockBatch) + mockBatch.On("Status").Return(fmt.Errorf("status error")) + + // Call the actual method to test (CollectDynamic) + c := &collector{caller: mockCaller} + dynamicInfo, err := c.CollectDynamic(context.Background()) + + // Assert that all expectations were met + assert.Error(t, err) + assert.Nil(t, dynamicInfo) +} + +func TestCollector_DynamicTimeout(t *testing.T) { + t.Parallel() + + // Setup RPC mocks + mockCaller := new(MockRPCClient) + mockBatch := new(MockRPCBatch) + + mockCaller.On("NewBatch").Return(mockBatch) + mockBatch.On("Status").Return(nil) + mockBatch.On("NetInfo").Return(nil) + mockBatch.On("NumUnconfirmedTxs").Return(nil) + mockBatch.On("Block", (*uint64)(nil)).Return(nil) + mockBatch.On("BlockResults", (*uint64)(nil)).Return(nil) + + // Set up a context and a sendLatency that will trigger a timeout + ctx, _ := context.WithTimeout(context.Background(), time.Millisecond) + mockBatch.sendLatency = time.Second + mockBatch.On("Send", ctx).Return([]any{}) + + // Call the actual method to test (CollectDynamic) + c := &collector{caller: mockCaller} + dynamicInfo, err := c.CollectDynamic(ctx) + + // Assert that the context timed out + assert.ErrorIs(t, err, context.DeadlineExceeded) + assert.Nil(t, dynamicInfo) +} + +func compareStatusRespToStaticInfo(t *testing.T, status *ctypes.ResultStatus, osExpected string, staticInfo *proto.StaticInfo) { + t.Helper() + + assert.Equal(t, staticInfo.Address, status.NodeInfo.ID().String()) + assert.Equal(t, staticInfo.GnoVersion, status.NodeInfo.Version) + assert.Equal(t, staticInfo.OsVersion, osExpected) + assert.Equal(t, staticInfo.Location, status.NodeInfo.Other.Location) +} + +func TestCollector_StaticSuccess(t *testing.T) { + t.Parallel() + + // Get predefined OS and Arch values + var ( + status = getBatchResults(t)[0].(*ctypes.ResultStatus) + os = status.NodeInfo.Other.OS + arch = status.NodeInfo.Other.Arch + ) + + // Setup multiple test cases (variation of the OsVersion) + testCases := []struct { + name string + os string + arch string + expected string + }{ + {"Both OS and Arch", os, arch, fmt.Sprintf("%s - %s", os, arch)}, + {"Only OS", os, "", os}, + {"Only Arch", "", arch, ""}, + {"Neither OS or Arch", "", "", ""}, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + t.Parallel() + + // Setup RPC mocks + mockCaller := new(MockRPCClient) + mockBatch := new(MockRPCBatch) + + mockCaller.On("NewBatch").Return(mockBatch) + mockBatch.On("Status").Return(nil) + + // Get predefined RPC batch results + results := getBatchResults(t) + status := results[0].(*ctypes.ResultStatus) + + // Override OS and Arch in the Status RPC results + status.NodeInfo.Other.OS = testCase.os + status.NodeInfo.Other.Arch = testCase.arch + ctx := context.Background() + mockBatch.On("Send", ctx).Return(results, nil) + + // Call the actual method to test (CollectStatic) + c := &collector{caller: mockCaller} + staticInfo, err := c.CollectStatic(ctx) + + // Assert that all expectations were met + assert.NoError(t, err) + assert.NotNil(t, staticInfo) + compareStatusRespToStaticInfo(t, status, testCase.expected, staticInfo) + }) + } +} + +func TestCollector_StaticFail(t *testing.T) { + t.Parallel() + + // Setup RPC mocks + mockCaller := new(MockRPCClient) + mockBatch := new(MockRPCBatch) + + ctx := context.Background() + mockCaller.On("NewBatch").Return(mockBatch) + mockBatch.On("Status").Return(fmt.Errorf("status error")) + + // Call the actual method to test (CollectStatic) + c := &collector{caller: mockCaller} + staticInfo, err := c.CollectStatic(ctx) + + // Assert that all expectations were met + assert.Error(t, err) + assert.Nil(t, staticInfo) +} + +func TestCollector_StaticTimeout(t *testing.T) { + t.Parallel() + + // Setup RPC mocks + mockCaller := new(MockRPCClient) + mockBatch := new(MockRPCBatch) + + mockCaller.On("NewBatch").Return(mockBatch) + mockBatch.On("Status").Return(nil) + + // Set up a context and a sendLatency that will trigger a timeout + ctx, _ := context.WithTimeout(context.Background(), time.Millisecond) + mockBatch.sendLatency = time.Second + mockBatch.On("Send", ctx).Return([]any{}) + + // Call the actual method to test (CollectStatic) + c := &collector{caller: mockCaller} + staticInfo, err := c.CollectStatic(ctx) + + // Assert that the context timed out + assert.ErrorIs(t, err, context.DeadlineExceeded) + assert.Nil(t, staticInfo) +} diff --git a/contribs/gnostats/go.mod b/contribs/gnostats/go.mod index c713e9a79ff..92824343606 100644 --- a/contribs/gnostats/go.mod +++ b/contribs/gnostats/go.mod @@ -23,6 +23,7 @@ require ( github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 // indirect diff --git a/contribs/gnostats/go.sum b/contribs/gnostats/go.sum index e4a0905d210..5eb10dd67dc 100644 --- a/contribs/gnostats/go.sum +++ b/contribs/gnostats/go.sum @@ -102,6 +102,8 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99 github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= From cab2b3ad08adefaa655ac23aef58773e60b4fc30 Mon Sep 17 00:00:00 2001 From: aeddi Date: Sat, 17 Aug 2024 00:14:38 +0200 Subject: [PATCH 04/13] feat(gnostats): implement agent --- contribs/gnostats/agent/agent.go | 81 ++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 contribs/gnostats/agent/agent.go diff --git a/contribs/gnostats/agent/agent.go b/contribs/gnostats/agent/agent.go new file mode 100644 index 00000000000..60e7b7be481 --- /dev/null +++ b/contribs/gnostats/agent/agent.go @@ -0,0 +1,81 @@ +package agent + +import ( + "context" + "fmt" + "time" + + "github.com/gnolang/gnostats/proto" +) + +// config holds both the injected Gno node RPC client and the Hub gRPC client. +type config struct { + hClient proto.HubClient + rClient rpcClient + pollInterval time.Duration // Minimum time interval between two data points +} + +type agent struct { + cfg config + cancel context.CancelFunc +} + +// Start registers with the Hub using Gno node static info, then pushes dynamic +// info from the Gno node to the Hub at intervals specified by pollInterval +func (a *agent) Start(ctx context.Context) error { + // Store a cancelFunc to make the agent stoppable using the Stop() method + ctx, a.cancel = context.WithCancel(ctx) + defer a.cancel() + + collector := NewCollector(a.cfg.rClient) + + // Get static info from the Gno node + staticInfo, err := collector.CollectStatic(ctx) + if err != nil { + return fmt.Errorf("unable to collect static info: %w", err) + } + + // Register with the Hub using static info + if _, err = a.cfg.hClient.Register(ctx, staticInfo); err != nil { + return fmt.Errorf("unable to register with hub: %w", err) + } + + // Get the Hub data point stream + stream, err := a.cfg.hClient.PushData(ctx) + if err != nil { + return fmt.Errorf("unable to get data stream: %w", err) + } + + // Push data points until the context is done + for { + // Get dynamic info from the Gno node + dynamicInfo, err := collector.CollectDynamic(ctx) + if err != nil { + return fmt.Errorf("unable to collect dynamic info: %w", err) + } + + // Push dynamic info to the Hub stream + if err = stream.Send(dynamicInfo); err != nil { + return fmt.Errorf("unable to send dynamic info: %w", err) + } + + select { + case <-time.After(a.cfg.pollInterval): + // Wait for the specified interval before pushing a new data point + case <-ctx.Done(): + return nil + } + } +} + +// Stop stops the agent +func (a *agent) Stop() { + a.cancel() +} + +// NewAgent creates a new agent using the provided config +func NewAgent(cfg config) *agent { + return &agent{ + cfg: cfg, + } +} From c51eb3a5fd66d52c141bb468d9c928ae500482e8 Mon Sep 17 00:00:00 2001 From: aeddi Date: Sat, 17 Aug 2024 00:15:13 +0200 Subject: [PATCH 05/13] test(gnostats): add agent unit tests --- contribs/gnostats/agent/agent_test.go | 217 ++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 contribs/gnostats/agent/agent_test.go diff --git a/contribs/gnostats/agent/agent_test.go b/contribs/gnostats/agent/agent_test.go new file mode 100644 index 00000000000..395c120366f --- /dev/null +++ b/contribs/gnostats/agent/agent_test.go @@ -0,0 +1,217 @@ +package agent + +import ( + "context" + "fmt" + mrand "math/rand" + "testing" + "time" + + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" + ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" + "github.com/gnolang/gno/tm2/pkg/bft/state" + "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/p2p" + "github.com/gnolang/gnostats/proto" + "github.com/stretchr/testify/mock" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" + "google.golang.org/protobuf/types/known/emptypb" +) + +var random = mrand.New(mrand.NewSource(time.Now().UnixNano())) + +// mockHubClient is a mock of the hubClient used for testing +type mockHubClient struct { + mock.Mock + static chan *proto.StaticInfo +} + +// Register pushes StaticInfo onto a channel accessible by the tests +func (m *mockHubClient) Register(ctx context.Context, in *proto.StaticInfo, _ ...grpc.CallOption) (*emptypb.Empty, error) { + args := m.Called(ctx, in) + m.static <- in + return nil, args.Error(0) +} + +// PushData returns a mockPushDataClient +func (m *mockHubClient) PushData(ctx context.Context, _ ...grpc.CallOption) (proto.Hub_PushDataClient, error) { + args := m.Called(ctx) + return args.Get(0).(proto.Hub_PushDataClient), args.Error(1) +} + +// GetDataStream is not used in this test file +func (m *mockHubClient) GetDataStream(_ context.Context, _ *emptypb.Empty, _ ...grpc.CallOption) (proto.Hub_GetDataStreamClient, error) { + panic("should never happen") +} + +// mockPushDataClient is a mock of the PushDataClient used for testing +type mockPushDataClient struct { + mock.Mock + dynamic chan *proto.DynamicInfo +} + +// Send pushes DynamicInfo onto a channel accessible by the tests +func (m *mockPushDataClient) Send(out *proto.DynamicInfo) error { + args := m.Called(out) + m.dynamic <- out + return args.Error(0) +} + +// The following methods won't be used in this test file +func (m *mockPushDataClient) CloseAndRecv() (*emptypb.Empty, error) { panic("should never happen") } +func (m *mockPushDataClient) Header() (metadata.MD, error) { panic("should never happen") } +func (m *mockPushDataClient) Trailer() metadata.MD { panic("should never happen") } +func (m *mockPushDataClient) CloseSend() error { panic("should never happen") } +func (m *mockPushDataClient) Context() context.Context { panic("should never happen") } +func (m *mockPushDataClient) SendMsg(msg any) error { panic("should never happen") } +func (m *mockPushDataClient) RecvMsg(msg any) error { panic("should never happen") } + +// Helpers that generate random string and int +func randomIntInRange(t *testing.T, min, max int) int { + t.Helper() + + return random.Intn(max-min+1) + min +} + +func randomStringOfLength(t *testing.T, length int) string { + t.Helper() + + const charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + + randBytes := make([]byte, length) + for i := range randBytes { + randBytes[i] = charset[random.Intn(len(charset))] + } + + return string(randBytes) +} + +func randomStringOfLengthInRange(t *testing.T, min, max int) string { + t.Helper() + + return randomStringOfLength(t, randomIntInRange(t, min, max)) +} + +func randomNodeInfo(t *testing.T) p2p.NodeInfo { + t.Helper() + + goos := []string{"aix", "android", "darwin", "dragonfly", "freebsd", "illumos", "ios", "js", "linux", "netbsd", "openbsd", "plan9", "solaris", "windows"} + goarch := []string{"386", "amd64", "arm", "arm64", "mips", "mips64", "mips64le", "mipsle", "ppc64", "ppc64le", "riscv64", "s390x", "wasm"} + + return p2p.NodeInfo{ + Moniker: randomStringOfLengthInRange(t, 1, 128), + NetAddress: p2p.NewNetAddress( + crypto.ID(randomStringOfLengthInRange(t, 64, 128)), + mockNetAddr{ + network: randomStringOfLengthInRange(t, 3, 6), + str: fmt.Sprintf( + "%d.%d.%d.%d", + randomIntInRange(t, 1, 255), + randomIntInRange(t, 0, 255), + randomIntInRange(t, 0, 255), + randomIntInRange(t, 0, 255), + ), + }, + ), + Other: p2p.NodeInfoOther{ + OS: goos[randomIntInRange(t, 0, len(goos)-1)], + Arch: goarch[randomIntInRange(t, 0, len(goarch)-1)], + Location: "", + }, + } + +} + +// Helper that generates a valid random RPC batch result +func getRandomBatchResults(t *testing.T) []any { + t.Helper() + + // Generate peers for NetInfo request + peers := make([]ctypes.Peer, randomIntInRange(t, 1, 32)) + for i := range peers { + peers[i] = ctypes.Peer{NodeInfo: randomNodeInfo(t)} + } + + return []any{ + &ctypes.ResultStatus{NodeInfo: randomNodeInfo(t)}, + &ctypes.ResultNetInfo{Peers: peers}, + &ctypes.ResultUnconfirmedTxs{Total: randomIntInRange(t, 0, 100)}, + + &ctypes.ResultBlock{ + Block: &types.Block{ + Header: types.Header{ + Height: int64(randomIntInRange(t, 1, 10000000)), + Time: time.Now(), + ProposerAddress: crypto.Address{}, + }, + }, + }, + + &ctypes.ResultBlockResults{ + Results: &state.ABCIResponses{ + DeliverTxs: []abci.ResponseDeliverTx{{ + GasUsed: int64(randomIntInRange(t, 5, 1000)), + GasWanted: int64(randomIntInRange(t, 5, 1000)), + }}, + }, + }, + } +} + +func TestAgent_E2E(t *testing.T) { + t.Parallel() + + // Setup RPC mocks + mockCaller := new(MockRPCClient) + mockBatch := new(MockRPCBatch) + + mockCaller.On("NewBatch").Return(mockBatch) + mockBatch.On("Status").Return(nil) + mockBatch.On("NetInfo").Return(nil) + mockBatch.On("NumUnconfirmedTxs").Return(nil) + mockBatch.On("Block", (*uint64)(nil)).Return(nil) + mockBatch.On("BlockResults", (*uint64)(nil)).Return(nil) + + // Setup gRPC mocks + mockHub := new(mockHubClient) + mockHub.static = make(chan *proto.StaticInfo) + + mockStream := new(mockPushDataClient) + mockStream.dynamic = make(chan *proto.DynamicInfo) + + mockHub.On("Register", mock.AnythingOfType("*context.cancelCtx"), mock.AnythingOfType("*proto.StaticInfo")).Return(nil, nil) + mockHub.On("PushData", mock.AnythingOfType("*context.cancelCtx")).Return(mockStream, nil) + mockStream.On("Send", mock.AnythingOfType("*proto.DynamicInfo")).Return(nil) + + // Inject both mocks of the clients into a new agent + agent := NewAgent(config{ + hClient: mockHub, + rClient: mockCaller, + pollInterval: 20 * time.Millisecond, + }) + + // Setup a first random batch result + results := getRandomBatchResults(t) + status := results[0].(*ctypes.ResultStatus) + mockBatch.On("Send", mock.Anything).Return(results, nil) + + // Test if registering with the Hub works as expected + go agent.Start(context.Background()) + static := <-mockHub.static + osVersion := fmt.Sprintf("%s - %s", status.NodeInfo.Other.OS, status.NodeInfo.Other.Arch) + compareStatusRespToStaticInfo(t, status, osVersion, static) + + // Test if the first five data pushes to the Hub work as expected + for i := 0; i < 5; i++ { + dynamic := <-mockStream.dynamic + compareBatchResultToDynamicInfo(t, results, dynamic) + + results = getRandomBatchResults(t) + mockBatch.On("Send").Unset() // Clear previous expected results + mockBatch.On("Send", mock.Anything).Return(results, nil) + } + + agent.Stop() +} From 2e3c73948129f9c50027975384af4c911d445038 Mon Sep 17 00:00:00 2001 From: aeddi Date: Tue, 27 Aug 2024 15:16:58 +0200 Subject: [PATCH 06/13] test(gnostats): make random source local --- contribs/gnostats/agent/agent_test.go | 63 ++++++++++++--------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/contribs/gnostats/agent/agent_test.go b/contribs/gnostats/agent/agent_test.go index 395c120366f..5b127152395 100644 --- a/contribs/gnostats/agent/agent_test.go +++ b/contribs/gnostats/agent/agent_test.go @@ -20,8 +20,6 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -var random = mrand.New(mrand.NewSource(time.Now().UnixNano())) - // mockHubClient is a mock of the hubClient used for testing type mockHubClient struct { mock.Mock @@ -69,13 +67,13 @@ func (m *mockPushDataClient) SendMsg(msg any) error { panic("sho func (m *mockPushDataClient) RecvMsg(msg any) error { panic("should never happen") } // Helpers that generate random string and int -func randomIntInRange(t *testing.T, min, max int) int { +func randomIntInRange(t *testing.T, random *mrand.Rand, min, max int) int { t.Helper() return random.Intn(max-min+1) + min } -func randomStringOfLength(t *testing.T, length int) string { +func randomStringOfLength(t *testing.T, random *mrand.Rand, length int) string { t.Helper() const charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" @@ -88,61 +86,59 @@ func randomStringOfLength(t *testing.T, length int) string { return string(randBytes) } -func randomStringOfLengthInRange(t *testing.T, min, max int) string { +func randomStringOfLengthInRange(t *testing.T, random *mrand.Rand, min, max int) string { t.Helper() - return randomStringOfLength(t, randomIntInRange(t, min, max)) + return randomStringOfLength(t, random, randomIntInRange(t, random, min, max)) } -func randomNodeInfo(t *testing.T) p2p.NodeInfo { +func randomNodeInfo(t *testing.T, random *mrand.Rand) p2p.NodeInfo { t.Helper() goos := []string{"aix", "android", "darwin", "dragonfly", "freebsd", "illumos", "ios", "js", "linux", "netbsd", "openbsd", "plan9", "solaris", "windows"} goarch := []string{"386", "amd64", "arm", "arm64", "mips", "mips64", "mips64le", "mipsle", "ppc64", "ppc64le", "riscv64", "s390x", "wasm"} return p2p.NodeInfo{ - Moniker: randomStringOfLengthInRange(t, 1, 128), + Moniker: randomStringOfLengthInRange(t, random, 1, 128), NetAddress: p2p.NewNetAddress( - crypto.ID(randomStringOfLengthInRange(t, 64, 128)), + crypto.ID(randomStringOfLengthInRange(t, random, 64, 128)), mockNetAddr{ - network: randomStringOfLengthInRange(t, 3, 6), + network: randomStringOfLengthInRange(t, random, 3, 6), str: fmt.Sprintf( "%d.%d.%d.%d", - randomIntInRange(t, 1, 255), - randomIntInRange(t, 0, 255), - randomIntInRange(t, 0, 255), - randomIntInRange(t, 0, 255), + randomIntInRange(t, random, 1, 255), + randomIntInRange(t, random, 0, 255), + randomIntInRange(t, random, 0, 255), + randomIntInRange(t, random, 0, 255), ), }, ), Other: p2p.NodeInfoOther{ - OS: goos[randomIntInRange(t, 0, len(goos)-1)], - Arch: goarch[randomIntInRange(t, 0, len(goarch)-1)], - Location: "", + OS: goos[randomIntInRange(t, random, 0, len(goos)-1)], + Arch: goarch[randomIntInRange(t, random, 0, len(goarch)-1)], }, } - } // Helper that generates a valid random RPC batch result -func getRandomBatchResults(t *testing.T) []any { +func getRandomBatchResults(t *testing.T, random *mrand.Rand) []any { t.Helper() // Generate peers for NetInfo request - peers := make([]ctypes.Peer, randomIntInRange(t, 1, 32)) + peers := make([]ctypes.Peer, randomIntInRange(t, random, 1, 32)) for i := range peers { - peers[i] = ctypes.Peer{NodeInfo: randomNodeInfo(t)} + peers[i] = ctypes.Peer{NodeInfo: randomNodeInfo(t, random)} } return []any{ - &ctypes.ResultStatus{NodeInfo: randomNodeInfo(t)}, + &ctypes.ResultStatus{NodeInfo: randomNodeInfo(t, random)}, &ctypes.ResultNetInfo{Peers: peers}, - &ctypes.ResultUnconfirmedTxs{Total: randomIntInRange(t, 0, 100)}, + &ctypes.ResultUnconfirmedTxs{Total: randomIntInRange(t, random, 0, 100)}, &ctypes.ResultBlock{ Block: &types.Block{ Header: types.Header{ - Height: int64(randomIntInRange(t, 1, 10000000)), + Height: int64(randomIntInRange(t, random, 1, 10000000)), Time: time.Now(), ProposerAddress: crypto.Address{}, }, @@ -152,8 +148,8 @@ func getRandomBatchResults(t *testing.T) []any { &ctypes.ResultBlockResults{ Results: &state.ABCIResponses{ DeliverTxs: []abci.ResponseDeliverTx{{ - GasUsed: int64(randomIntInRange(t, 5, 1000)), - GasWanted: int64(randomIntInRange(t, 5, 1000)), + GasUsed: int64(randomIntInRange(t, random, 5, 1000)), + GasWanted: int64(randomIntInRange(t, random, 5, 1000)), }}, }, }, @@ -186,14 +182,13 @@ func TestAgent_E2E(t *testing.T) { mockStream.On("Send", mock.AnythingOfType("*proto.DynamicInfo")).Return(nil) // Inject both mocks of the clients into a new agent - agent := NewAgent(config{ - hClient: mockHub, - rClient: mockCaller, - pollInterval: 20 * time.Millisecond, - }) + agent := NewAgent(mockHub, mockCaller, WithPollInterval(20*time.Millisecond)) + + // Init a new random source + random := mrand.New(mrand.NewSource(time.Now().UnixNano())) // Setup a first random batch result - results := getRandomBatchResults(t) + results := getRandomBatchResults(t, random) status := results[0].(*ctypes.ResultStatus) mockBatch.On("Send", mock.Anything).Return(results, nil) @@ -208,10 +203,8 @@ func TestAgent_E2E(t *testing.T) { dynamic := <-mockStream.dynamic compareBatchResultToDynamicInfo(t, results, dynamic) - results = getRandomBatchResults(t) + results = getRandomBatchResults(t, random) mockBatch.On("Send").Unset() // Clear previous expected results mockBatch.On("Send", mock.Anything).Return(results, nil) } - - agent.Stop() } From 64086522662a37538b626aeba8ecbd309561ad06 Mon Sep 17 00:00:00 2001 From: aeddi Date: Tue, 27 Aug 2024 15:21:23 +0200 Subject: [PATCH 07/13] feat(gnostats): remove Stop method --- contribs/gnostats/agent/agent.go | 12 +----------- contribs/gnostats/agent/agent_test.go | 4 ++-- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/contribs/gnostats/agent/agent.go b/contribs/gnostats/agent/agent.go index 60e7b7be481..e818bc144bc 100644 --- a/contribs/gnostats/agent/agent.go +++ b/contribs/gnostats/agent/agent.go @@ -16,17 +16,12 @@ type config struct { } type agent struct { - cfg config - cancel context.CancelFunc + cfg config } // Start registers with the Hub using Gno node static info, then pushes dynamic // info from the Gno node to the Hub at intervals specified by pollInterval func (a *agent) Start(ctx context.Context) error { - // Store a cancelFunc to make the agent stoppable using the Stop() method - ctx, a.cancel = context.WithCancel(ctx) - defer a.cancel() - collector := NewCollector(a.cfg.rClient) // Get static info from the Gno node @@ -68,11 +63,6 @@ func (a *agent) Start(ctx context.Context) error { } } -// Stop stops the agent -func (a *agent) Stop() { - a.cancel() -} - // NewAgent creates a new agent using the provided config func NewAgent(cfg config) *agent { return &agent{ diff --git a/contribs/gnostats/agent/agent_test.go b/contribs/gnostats/agent/agent_test.go index 5b127152395..f067bb2237c 100644 --- a/contribs/gnostats/agent/agent_test.go +++ b/contribs/gnostats/agent/agent_test.go @@ -177,8 +177,8 @@ func TestAgent_E2E(t *testing.T) { mockStream := new(mockPushDataClient) mockStream.dynamic = make(chan *proto.DynamicInfo) - mockHub.On("Register", mock.AnythingOfType("*context.cancelCtx"), mock.AnythingOfType("*proto.StaticInfo")).Return(nil, nil) - mockHub.On("PushData", mock.AnythingOfType("*context.cancelCtx")).Return(mockStream, nil) + mockHub.On("Register", mock.MatchedBy(func(ctx context.Context) bool { return true }), mock.AnythingOfType("*proto.StaticInfo")).Return(nil, nil) + mockHub.On("PushData", mock.MatchedBy(func(ctx context.Context) bool { return true })).Return(mockStream, nil) mockStream.On("Send", mock.AnythingOfType("*proto.DynamicInfo")).Return(nil) // Inject both mocks of the clients into a new agent From cba62bf17ab96ec088192721460d50afd1f04ef7 Mon Sep 17 00:00:00 2001 From: aeddi Date: Tue, 27 Aug 2024 15:25:02 +0200 Subject: [PATCH 08/13] refactor(gnostats): remove config struct / add option --- contribs/gnostats/agent/agent.go | 39 ++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/contribs/gnostats/agent/agent.go b/contribs/gnostats/agent/agent.go index e818bc144bc..78fccd2f8ce 100644 --- a/contribs/gnostats/agent/agent.go +++ b/contribs/gnostats/agent/agent.go @@ -8,21 +8,26 @@ import ( "github.com/gnolang/gnostats/proto" ) -// config holds both the injected Gno node RPC client and the Hub gRPC client. -type config struct { +// angent holds both the injected Gno node RPC client and the Hub gRPC client. +type agent struct { hClient proto.HubClient rClient rpcClient pollInterval time.Duration // Minimum time interval between two data points } -type agent struct { - cfg config +type Option func(*agent) + +// WithPollInterval sets the agent poll interval between two data points +func WithPollInterval(interval time.Duration) Option { + return func(c *agent) { + c.pollInterval = interval + } } // Start registers with the Hub using Gno node static info, then pushes dynamic // info from the Gno node to the Hub at intervals specified by pollInterval func (a *agent) Start(ctx context.Context) error { - collector := NewCollector(a.cfg.rClient) + collector := NewCollector(a.rClient) // Get static info from the Gno node staticInfo, err := collector.CollectStatic(ctx) @@ -31,12 +36,12 @@ func (a *agent) Start(ctx context.Context) error { } // Register with the Hub using static info - if _, err = a.cfg.hClient.Register(ctx, staticInfo); err != nil { + if _, err = a.hClient.Register(ctx, staticInfo); err != nil { return fmt.Errorf("unable to register with hub: %w", err) } // Get the Hub data point stream - stream, err := a.cfg.hClient.PushData(ctx) + stream, err := a.hClient.PushData(ctx) if err != nil { return fmt.Errorf("unable to get data stream: %w", err) } @@ -55,7 +60,7 @@ func (a *agent) Start(ctx context.Context) error { } select { - case <-time.After(a.cfg.pollInterval): + case <-time.After(a.pollInterval): // Wait for the specified interval before pushing a new data point case <-ctx.Done(): return nil @@ -63,9 +68,19 @@ func (a *agent) Start(ctx context.Context) error { } } -// NewAgent creates a new agent using the provided config -func NewAgent(cfg config) *agent { - return &agent{ - cfg: cfg, +// NewAgent creates a new agent using the provided clients and options +func NewAgent(hClient proto.HubClient, rClient rpcClient, options ...Option) *agent { + const defaultInverval = time.Second + + a := &agent{ + hClient: hClient, + rClient: rClient, + pollInterval: defaultInverval, + } + + for _, opt := range options { + opt(a) } + + return a } From fdfcab541fe324e5de9f90af380391f6b051faf1 Mon Sep 17 00:00:00 2001 From: aeddi Date: Tue, 27 Aug 2024 16:22:58 +0200 Subject: [PATCH 09/13] test(gnostats): set timeout for E2EE test --- contribs/gnostats/agent/agent_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contribs/gnostats/agent/agent_test.go b/contribs/gnostats/agent/agent_test.go index f067bb2237c..dadddb5485c 100644 --- a/contribs/gnostats/agent/agent_test.go +++ b/contribs/gnostats/agent/agent_test.go @@ -193,7 +193,10 @@ func TestAgent_E2E(t *testing.T) { mockBatch.On("Send", mock.Anything).Return(results, nil) // Test if registering with the Hub works as expected - go agent.Start(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + go agent.Start(ctx) static := <-mockHub.static osVersion := fmt.Sprintf("%s - %s", status.NodeInfo.Other.OS, status.NodeInfo.Other.Arch) compareStatusRespToStaticInfo(t, status, osVersion, static) From 83d08e58faaf681ef44bf25f27b5c4ec01ba9641 Mon Sep 17 00:00:00 2001 From: aeddi Date: Tue, 27 Aug 2024 17:28:04 +0200 Subject: [PATCH 10/13] refactor(gnostats): move osVersion retrieval to agent --- contribs/gnostats/agent/agent_test.go | 10 +- contribs/gnostats/agent/collector.go | 16 +-- contribs/gnostats/agent/collector_test.go | 76 +++-------- contribs/gnostats/proto/stats.pb.go | 143 ++++++++++---------- contribs/gnostats/proto/stats.proto | 1 - contribs/gnostats/proto/stats_grpc.pb.go | 153 ++++++++-------------- tm2/pkg/bft/node/node.go | 7 - tm2/pkg/p2p/node_info.go | 3 - 8 files changed, 147 insertions(+), 262 deletions(-) diff --git a/contribs/gnostats/agent/agent_test.go b/contribs/gnostats/agent/agent_test.go index dadddb5485c..89cfc16f255 100644 --- a/contribs/gnostats/agent/agent_test.go +++ b/contribs/gnostats/agent/agent_test.go @@ -95,9 +95,6 @@ func randomStringOfLengthInRange(t *testing.T, random *mrand.Rand, min, max int) func randomNodeInfo(t *testing.T, random *mrand.Rand) p2p.NodeInfo { t.Helper() - goos := []string{"aix", "android", "darwin", "dragonfly", "freebsd", "illumos", "ios", "js", "linux", "netbsd", "openbsd", "plan9", "solaris", "windows"} - goarch := []string{"386", "amd64", "arm", "arm64", "mips", "mips64", "mips64le", "mipsle", "ppc64", "ppc64le", "riscv64", "s390x", "wasm"} - return p2p.NodeInfo{ Moniker: randomStringOfLengthInRange(t, random, 1, 128), NetAddress: p2p.NewNetAddress( @@ -113,10 +110,6 @@ func randomNodeInfo(t *testing.T, random *mrand.Rand) p2p.NodeInfo { ), }, ), - Other: p2p.NodeInfoOther{ - OS: goos[randomIntInRange(t, random, 0, len(goos)-1)], - Arch: goarch[randomIntInRange(t, random, 0, len(goarch)-1)], - }, } } @@ -198,8 +191,7 @@ func TestAgent_E2E(t *testing.T) { go agent.Start(ctx) static := <-mockHub.static - osVersion := fmt.Sprintf("%s - %s", status.NodeInfo.Other.OS, status.NodeInfo.Other.Arch) - compareStatusRespToStaticInfo(t, status, osVersion, static) + compareStatusRespToStaticInfo(t, status, static) // Test if the first five data pushes to the Hub work as expected for i := 0; i < 5; i++ { diff --git a/contribs/gnostats/agent/collector.go b/contribs/gnostats/agent/collector.go index 8f34c911c65..02310a25ce5 100644 --- a/contribs/gnostats/agent/collector.go +++ b/contribs/gnostats/agent/collector.go @@ -3,6 +3,7 @@ package agent import ( "context" "fmt" + "runtime" ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" @@ -127,24 +128,11 @@ func (c *collector) CollectStatic(ctx context.Context) (*proto.StaticInfo, error // Cast response to the appropriate type status := results[0].(*ctypes.ResultStatus) - // Format the OsVersion string - var ( - osVersion = "" - os = status.NodeInfo.Other.OS - arch = status.NodeInfo.Other.Arch - ) - if os != "" && arch != "" { - osVersion = fmt.Sprintf("%s - %s", os, arch) - } else if os != "" { - osVersion = os - } - // Fill the StaticInfo fields with the corresponding values return &proto.StaticInfo{ Address: status.NodeInfo.ID().String(), GnoVersion: status.NodeInfo.Version, - OsVersion: osVersion, - Location: "", // @TODO: see comment in makeNodeInfo + OsVersion: fmt.Sprintf("%s - %s", runtime.GOOS, runtime.GOARCH), }, nil } diff --git a/contribs/gnostats/agent/collector_test.go b/contribs/gnostats/agent/collector_test.go index 4d02db3d1de..c620d7ea59c 100644 --- a/contribs/gnostats/agent/collector_test.go +++ b/contribs/gnostats/agent/collector_test.go @@ -3,6 +3,7 @@ package agent import ( "context" "fmt" + "runtime" "testing" "time" @@ -112,11 +113,6 @@ func getBatchResults(t *testing.T) []any { str: "0.0.0.0", }, ), - Other: p2p.NodeInfoOther{ - OS: "plan9", - Arch: "ppc64", - Location: "", - }, }, }, @@ -199,6 +195,7 @@ func TestCollector_DynamicSuccess(t *testing.T) { mockBatch.On("Block", (*uint64)(nil)).Return(nil) mockBatch.On("BlockResults", (*uint64)(nil)).Return(nil) + // Get predefined RPC batch results ctx := context.Background() results := getBatchResults(t) mockBatch.On("Send", ctx).Return(results, nil) @@ -260,69 +257,38 @@ func TestCollector_DynamicTimeout(t *testing.T) { assert.Nil(t, dynamicInfo) } -func compareStatusRespToStaticInfo(t *testing.T, status *ctypes.ResultStatus, osExpected string, staticInfo *proto.StaticInfo) { +func compareStatusRespToStaticInfo(t *testing.T, status *ctypes.ResultStatus, staticInfo *proto.StaticInfo) { t.Helper() assert.Equal(t, staticInfo.Address, status.NodeInfo.ID().String()) assert.Equal(t, staticInfo.GnoVersion, status.NodeInfo.Version) - assert.Equal(t, staticInfo.OsVersion, osExpected) - assert.Equal(t, staticInfo.Location, status.NodeInfo.Other.Location) + assert.Equal(t, staticInfo.OsVersion, fmt.Sprintf("%s - %s", runtime.GOOS, runtime.GOARCH)) } func TestCollector_StaticSuccess(t *testing.T) { t.Parallel() - // Get predefined OS and Arch values - var ( - status = getBatchResults(t)[0].(*ctypes.ResultStatus) - os = status.NodeInfo.Other.OS - arch = status.NodeInfo.Other.Arch - ) - - // Setup multiple test cases (variation of the OsVersion) - testCases := []struct { - name string - os string - arch string - expected string - }{ - {"Both OS and Arch", os, arch, fmt.Sprintf("%s - %s", os, arch)}, - {"Only OS", os, "", os}, - {"Only Arch", "", arch, ""}, - {"Neither OS or Arch", "", "", ""}, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - t.Parallel() - - // Setup RPC mocks - mockCaller := new(MockRPCClient) - mockBatch := new(MockRPCBatch) - - mockCaller.On("NewBatch").Return(mockBatch) - mockBatch.On("Status").Return(nil) + // Setup RPC mocks + mockCaller := new(MockRPCClient) + mockBatch := new(MockRPCBatch) - // Get predefined RPC batch results - results := getBatchResults(t) - status := results[0].(*ctypes.ResultStatus) + mockCaller.On("NewBatch").Return(mockBatch) + mockBatch.On("Status").Return(nil) - // Override OS and Arch in the Status RPC results - status.NodeInfo.Other.OS = testCase.os - status.NodeInfo.Other.Arch = testCase.arch - ctx := context.Background() - mockBatch.On("Send", ctx).Return(results, nil) + // Get predefined RPC batch results + ctx := context.Background() + results := getBatchResults(t) + mockBatch.On("Send", ctx).Return(results, nil) - // Call the actual method to test (CollectStatic) - c := &collector{caller: mockCaller} - staticInfo, err := c.CollectStatic(ctx) + // Call the actual method to test (CollectStatic) + c := &collector{caller: mockCaller} + staticInfo, err := c.CollectStatic(ctx) - // Assert that all expectations were met - assert.NoError(t, err) - assert.NotNil(t, staticInfo) - compareStatusRespToStaticInfo(t, status, testCase.expected, staticInfo) - }) - } + // Assert that all expectations were met + assert.NoError(t, err) + assert.NotNil(t, staticInfo) + status := results[0].(*ctypes.ResultStatus) + compareStatusRespToStaticInfo(t, status, staticInfo) } func TestCollector_StaticFail(t *testing.T) { diff --git a/contribs/gnostats/proto/stats.pb.go b/contribs/gnostats/proto/stats.pb.go index 641a8c57d1b..f38f919f3fc 100644 --- a/contribs/gnostats/proto/stats.pb.go +++ b/contribs/gnostats/proto/stats.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v5.27.3 +// protoc-gen-go v1.34.2 +// protoc v5.26.1 // source: proto/stats.proto package proto @@ -30,7 +30,6 @@ type StaticInfo struct { Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` // the ID of the node GnoVersion string `protobuf:"bytes,2,opt,name=gno_version,json=gnoVersion,proto3" json:"gno_version,omitempty"` // the gno version of the node OsVersion string `protobuf:"bytes,3,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"` // the OS info of the node - Location string `protobuf:"bytes,4,opt,name=location,proto3" json:"location,omitempty"` // the location of the node (Country) } func (x *StaticInfo) Reset() { @@ -86,13 +85,6 @@ func (x *StaticInfo) GetOsVersion() string { return "" } -func (x *StaticInfo) GetLocation() string { - if x != nil { - return x.Location - } - return "" -} - // DynamicInfo is the single node data push type DynamicInfo struct { state protoimpl.MessageState @@ -436,65 +428,64 @@ var file_proto_stats_proto_rawDesc = []byte{ 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0x82, 0x01, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x6e, 0x6f, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, - 0x67, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x73, - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xd5, 0x01, 0x0a, 0x0b, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, - 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0b, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x08, - 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, - 0x2e, 0x4e, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x78, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x54, - 0x78, 0x73, 0x12, 0x29, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x6e, 0x66, 0x6f, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, - 0x66, 0x6f, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x97, 0x01, - 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x6e, - 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x75, 0x6d, - 0x62, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, - 0x67, 0x61, 0x73, 0x5f, 0x77, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x67, 0x61, 0x73, 0x57, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x22, 0x4b, 0x0a, 0x07, 0x4e, 0x65, 0x74, 0x49, 0x6e, - 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x32, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x32, 0x70, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x70, - 0x65, 0x65, 0x72, 0x73, 0x22, 0x45, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, - 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x32, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x32, 0x70, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x22, 0x6a, 0x0a, 0x09, 0x44, - 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x0c, 0x64, 0x79, 0x6e, 0x61, - 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0b, 0x64, 0x79, - 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2c, 0x0a, 0x0b, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x32, 0xa1, 0x01, 0x0a, 0x03, 0x48, 0x75, 0x62, 0x12, - 0x35, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0a, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x50, - 0x6f, 0x69, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x2f, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x12, 0x0b, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x1a, - 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x32, 0x0a, 0x08, 0x50, 0x75, 0x73, 0x68, 0x44, - 0x61, 0x74, 0x61, 0x12, 0x0c, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x66, - 0x6f, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x66, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, + 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x67, 0x6e, 0x6f, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x67, + 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x73, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, + 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd5, 0x01, 0x0a, 0x0b, 0x44, 0x79, 0x6e, + 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, + 0x69, 0x73, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, + 0x23, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x08, 0x2e, 0x4e, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6e, 0x65, 0x74, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x5f, + 0x74, 0x78, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x70, 0x65, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x54, 0x78, 0x73, 0x12, 0x29, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, + 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, + 0x22, 0x97, 0x01, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, + 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, + 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x67, 0x61, 0x73, 0x5f, 0x75, 0x73, 0x65, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x67, 0x61, 0x73, 0x55, 0x73, 0x65, 0x64, 0x12, + 0x1d, 0x0a, 0x0a, 0x67, 0x61, 0x73, 0x5f, 0x77, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x67, 0x61, 0x73, 0x57, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x22, 0x4b, 0x0a, 0x07, 0x4e, 0x65, + 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x32, 0x70, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x32, 0x70, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, + 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x22, 0x45, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x49, + 0x6e, 0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x32, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x32, 0x70, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x6e, 0x69, 0x6b, 0x65, 0x72, 0x22, 0x6a, + 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x0c, 0x64, + 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x0b, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2c, 0x0a, 0x0b, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0b, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0a, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x32, 0xa1, 0x01, 0x0a, 0x03, 0x48, + 0x75, 0x62, 0x12, 0x35, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0a, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x30, 0x01, 0x12, 0x2f, 0x0a, 0x08, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x12, 0x0b, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x69, 0x63, 0x49, 0x6e, + 0x66, 0x6f, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x32, 0x0a, 0x08, 0x50, 0x75, + 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x12, 0x0c, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, + 0x49, 0x6e, 0x66, 0x6f, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x28, 0x01, 0x42, 0x09, + 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -510,7 +501,7 @@ func file_proto_stats_proto_rawDescGZIP() []byte { } var file_proto_stats_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_proto_stats_proto_goTypes = []interface{}{ +var file_proto_stats_proto_goTypes = []any{ (*StaticInfo)(nil), // 0: StaticInfo (*DynamicInfo)(nil), // 1: DynamicInfo (*BlockInfo)(nil), // 2: BlockInfo @@ -544,7 +535,7 @@ func file_proto_stats_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_proto_stats_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_proto_stats_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*StaticInfo); i { case 0: return &v.state @@ -556,7 +547,7 @@ func file_proto_stats_proto_init() { return nil } } - file_proto_stats_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_proto_stats_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*DynamicInfo); i { case 0: return &v.state @@ -568,7 +559,7 @@ func file_proto_stats_proto_init() { return nil } } - file_proto_stats_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_proto_stats_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*BlockInfo); i { case 0: return &v.state @@ -580,7 +571,7 @@ func file_proto_stats_proto_init() { return nil } } - file_proto_stats_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_proto_stats_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*NetInfo); i { case 0: return &v.state @@ -592,7 +583,7 @@ func file_proto_stats_proto_init() { return nil } } - file_proto_stats_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_proto_stats_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*PeerInfo); i { case 0: return &v.state @@ -604,7 +595,7 @@ func file_proto_stats_proto_init() { return nil } } - file_proto_stats_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_proto_stats_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*DataPoint); i { case 0: return &v.state diff --git a/contribs/gnostats/proto/stats.proto b/contribs/gnostats/proto/stats.proto index ab7e60fd51f..07a42394fe2 100644 --- a/contribs/gnostats/proto/stats.proto +++ b/contribs/gnostats/proto/stats.proto @@ -21,7 +21,6 @@ message StaticInfo { string address = 1; // the ID of the node string gno_version = 2; // the gno version of the node string os_version = 3; // the OS info of the node - string location = 4; // the location of the node (Country) } // DynamicInfo is the single node data push diff --git a/contribs/gnostats/proto/stats_grpc.pb.go b/contribs/gnostats/proto/stats_grpc.pb.go index 38a789d8dd0..69d8a0a1fab 100644 --- a/contribs/gnostats/proto/stats_grpc.pb.go +++ b/contribs/gnostats/proto/stats_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v5.27.3 +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.26.1 // source: proto/stats.proto package proto @@ -16,19 +16,27 @@ import ( // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Hub_GetDataStream_FullMethodName = "/Hub/GetDataStream" + Hub_Register_FullMethodName = "/Hub/Register" + Hub_PushData_FullMethodName = "/Hub/PushData" +) // HubClient is the client API for Hub service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// Hub is the stats gathering service (hub) type HubClient interface { // GetDataStream returns a stream of fresh data from the stats hub - GetDataStream(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (Hub_GetDataStreamClient, error) + GetDataStream(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DataPoint], error) // Register registers the node instance with the stats hub Register(ctx context.Context, in *StaticInfo, opts ...grpc.CallOption) (*emptypb.Empty, error) // PushData continuously pushes the node data to the stats hub - PushData(ctx context.Context, opts ...grpc.CallOption) (Hub_PushDataClient, error) + PushData(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[DynamicInfo, emptypb.Empty], error) } type hubClient struct { @@ -39,12 +47,13 @@ func NewHubClient(cc grpc.ClientConnInterface) HubClient { return &hubClient{cc} } -func (c *hubClient) GetDataStream(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (Hub_GetDataStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &Hub_ServiceDesc.Streams[0], "/Hub/GetDataStream", opts...) +func (c *hubClient) GetDataStream(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DataPoint], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Hub_ServiceDesc.Streams[0], Hub_GetDataStream_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &hubGetDataStreamClient{stream} + x := &grpc.GenericClientStream[emptypb.Empty, DataPoint]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } @@ -54,93 +63,65 @@ func (c *hubClient) GetDataStream(ctx context.Context, in *emptypb.Empty, opts . return x, nil } -type Hub_GetDataStreamClient interface { - Recv() (*DataPoint, error) - grpc.ClientStream -} - -type hubGetDataStreamClient struct { - grpc.ClientStream -} - -func (x *hubGetDataStreamClient) Recv() (*DataPoint, error) { - m := new(DataPoint) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Hub_GetDataStreamClient = grpc.ServerStreamingClient[DataPoint] func (c *hubClient) Register(ctx context.Context, in *StaticInfo, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, "/Hub/Register", in, out, opts...) + err := c.cc.Invoke(ctx, Hub_Register_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } -func (c *hubClient) PushData(ctx context.Context, opts ...grpc.CallOption) (Hub_PushDataClient, error) { - stream, err := c.cc.NewStream(ctx, &Hub_ServiceDesc.Streams[1], "/Hub/PushData", opts...) +func (c *hubClient) PushData(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[DynamicInfo, emptypb.Empty], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Hub_ServiceDesc.Streams[1], Hub_PushData_FullMethodName, cOpts...) if err != nil { return nil, err } - x := &hubPushDataClient{stream} + x := &grpc.GenericClientStream[DynamicInfo, emptypb.Empty]{ClientStream: stream} return x, nil } -type Hub_PushDataClient interface { - Send(*DynamicInfo) error - CloseAndRecv() (*emptypb.Empty, error) - grpc.ClientStream -} - -type hubPushDataClient struct { - grpc.ClientStream -} - -func (x *hubPushDataClient) Send(m *DynamicInfo) error { - return x.ClientStream.SendMsg(m) -} - -func (x *hubPushDataClient) CloseAndRecv() (*emptypb.Empty, error) { - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - m := new(emptypb.Empty) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Hub_PushDataClient = grpc.ClientStreamingClient[DynamicInfo, emptypb.Empty] // HubServer is the server API for Hub service. // All implementations must embed UnimplementedHubServer -// for forward compatibility +// for forward compatibility. +// +// Hub is the stats gathering service (hub) type HubServer interface { // GetDataStream returns a stream of fresh data from the stats hub - GetDataStream(*emptypb.Empty, Hub_GetDataStreamServer) error + GetDataStream(*emptypb.Empty, grpc.ServerStreamingServer[DataPoint]) error // Register registers the node instance with the stats hub Register(context.Context, *StaticInfo) (*emptypb.Empty, error) // PushData continuously pushes the node data to the stats hub - PushData(Hub_PushDataServer) error + PushData(grpc.ClientStreamingServer[DynamicInfo, emptypb.Empty]) error mustEmbedUnimplementedHubServer() } -// UnimplementedHubServer must be embedded to have forward compatible implementations. -type UnimplementedHubServer struct { -} +// UnimplementedHubServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedHubServer struct{} -func (UnimplementedHubServer) GetDataStream(*emptypb.Empty, Hub_GetDataStreamServer) error { +func (UnimplementedHubServer) GetDataStream(*emptypb.Empty, grpc.ServerStreamingServer[DataPoint]) error { return status.Errorf(codes.Unimplemented, "method GetDataStream not implemented") } func (UnimplementedHubServer) Register(context.Context, *StaticInfo) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") } -func (UnimplementedHubServer) PushData(Hub_PushDataServer) error { +func (UnimplementedHubServer) PushData(grpc.ClientStreamingServer[DynamicInfo, emptypb.Empty]) error { return status.Errorf(codes.Unimplemented, "method PushData not implemented") } func (UnimplementedHubServer) mustEmbedUnimplementedHubServer() {} +func (UnimplementedHubServer) testEmbeddedByValue() {} // UnsafeHubServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to HubServer will @@ -150,6 +131,13 @@ type UnsafeHubServer interface { } func RegisterHubServer(s grpc.ServiceRegistrar, srv HubServer) { + // If the following call pancis, it indicates UnimplementedHubServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } s.RegisterService(&Hub_ServiceDesc, srv) } @@ -158,21 +146,11 @@ func _Hub_GetDataStream_Handler(srv interface{}, stream grpc.ServerStream) error if err := stream.RecvMsg(m); err != nil { return err } - return srv.(HubServer).GetDataStream(m, &hubGetDataStreamServer{stream}) -} - -type Hub_GetDataStreamServer interface { - Send(*DataPoint) error - grpc.ServerStream -} - -type hubGetDataStreamServer struct { - grpc.ServerStream + return srv.(HubServer).GetDataStream(m, &grpc.GenericServerStream[emptypb.Empty, DataPoint]{ServerStream: stream}) } -func (x *hubGetDataStreamServer) Send(m *DataPoint) error { - return x.ServerStream.SendMsg(m) -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Hub_GetDataStreamServer = grpc.ServerStreamingServer[DataPoint] func _Hub_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(StaticInfo) @@ -184,7 +162,7 @@ func _Hub_Register_Handler(srv interface{}, ctx context.Context, dec func(interf } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/Hub/Register", + FullMethod: Hub_Register_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(HubServer).Register(ctx, req.(*StaticInfo)) @@ -193,30 +171,11 @@ func _Hub_Register_Handler(srv interface{}, ctx context.Context, dec func(interf } func _Hub_PushData_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(HubServer).PushData(&hubPushDataServer{stream}) -} - -type Hub_PushDataServer interface { - SendAndClose(*emptypb.Empty) error - Recv() (*DynamicInfo, error) - grpc.ServerStream -} - -type hubPushDataServer struct { - grpc.ServerStream + return srv.(HubServer).PushData(&grpc.GenericServerStream[DynamicInfo, emptypb.Empty]{ServerStream: stream}) } -func (x *hubPushDataServer) SendAndClose(m *emptypb.Empty) error { - return x.ServerStream.SendMsg(m) -} - -func (x *hubPushDataServer) Recv() (*DynamicInfo, error) { - m := new(DynamicInfo) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Hub_PushDataServer = grpc.ClientStreamingServer[DynamicInfo, emptypb.Empty] // Hub_ServiceDesc is the grpc.ServiceDesc for Hub service. // It's only intended for direct use with grpc.RegisterService, diff --git a/tm2/pkg/bft/node/node.go b/tm2/pkg/bft/node/node.go index 9a8f341de92..e29de3dd1ae 100644 --- a/tm2/pkg/bft/node/node.go +++ b/tm2/pkg/bft/node/node.go @@ -8,7 +8,6 @@ import ( "log/slog" "net" "net/http" - "runtime" "strings" "sync" "time" @@ -896,12 +895,6 @@ func makeNodeInfo( Other: p2p.NodeInfoOther{ TxIndex: txIndexerStatus, RPCAddress: config.RPC.ListenAddress, - OS: runtime.GOOS, - Arch: runtime.GOARCH, - // @TODO: Check with @zivkovicmilos if we need to add a field - // to the node config along with a cmd flag to opt-in for - // automatic location detection (+ a way to set it manually?) - Location: "", }, } diff --git a/tm2/pkg/p2p/node_info.go b/tm2/pkg/p2p/node_info.go index 885b91bea47..48ba8f7776b 100644 --- a/tm2/pkg/p2p/node_info.go +++ b/tm2/pkg/p2p/node_info.go @@ -45,9 +45,6 @@ type NodeInfo struct { type NodeInfoOther struct { TxIndex string `json:"tx_index"` RPCAddress string `json:"rpc_address"` - OS string `json:"os"` - Arch string `json:"arch"` - Location string `json:"location"` } // Validate checks the self-reported NodeInfo is safe. From a9076110ca759261381f42dacb3c7b9c6d141914 Mon Sep 17 00:00:00 2001 From: aeddi Date: Wed, 28 Aug 2024 09:34:40 +0200 Subject: [PATCH 11/13] refactor(gnostats): export Agent struct --- contribs/gnostats/agent/agent.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/contribs/gnostats/agent/agent.go b/contribs/gnostats/agent/agent.go index 78fccd2f8ce..02835405d95 100644 --- a/contribs/gnostats/agent/agent.go +++ b/contribs/gnostats/agent/agent.go @@ -8,25 +8,25 @@ import ( "github.com/gnolang/gnostats/proto" ) -// angent holds both the injected Gno node RPC client and the Hub gRPC client. -type agent struct { +// Agent holds both the injected Gno node RPC client and the Hub gRPC client +type Agent struct { hClient proto.HubClient rClient rpcClient pollInterval time.Duration // Minimum time interval between two data points } -type Option func(*agent) +type Option func(*Agent) // WithPollInterval sets the agent poll interval between two data points func WithPollInterval(interval time.Duration) Option { - return func(c *agent) { + return func(c *Agent) { c.pollInterval = interval } } // Start registers with the Hub using Gno node static info, then pushes dynamic // info from the Gno node to the Hub at intervals specified by pollInterval -func (a *agent) Start(ctx context.Context) error { +func (a *Agent) Start(ctx context.Context) error { collector := NewCollector(a.rClient) // Get static info from the Gno node @@ -69,18 +69,18 @@ func (a *agent) Start(ctx context.Context) error { } // NewAgent creates a new agent using the provided clients and options -func NewAgent(hClient proto.HubClient, rClient rpcClient, options ...Option) *agent { +func NewAgent(hClient proto.HubClient, rClient rpcClient, options ...Option) *Agent { const defaultInverval = time.Second - a := &agent{ + agent := &Agent{ hClient: hClient, rClient: rClient, pollInterval: defaultInverval, } for _, opt := range options { - opt(a) + opt(agent) } - return a + return agent } From 77fa3920fde92dce516a2c3db20b09c322c66a4e Mon Sep 17 00:00:00 2001 From: aeddi Date: Wed, 28 Aug 2024 11:00:00 +0200 Subject: [PATCH 12/13] fix(gnostats): get correct isValidator value --- contribs/gnostats/agent/agent_test.go | 23 ++++++++++- contribs/gnostats/agent/collector.go | 25 ++++++++---- contribs/gnostats/agent/collector_test.go | 47 ++++++++++++++++++++--- 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/contribs/gnostats/agent/agent_test.go b/contribs/gnostats/agent/agent_test.go index 89cfc16f255..f54ee97fc73 100644 --- a/contribs/gnostats/agent/agent_test.go +++ b/contribs/gnostats/agent/agent_test.go @@ -123,8 +123,28 @@ func getRandomBatchResults(t *testing.T, random *mrand.Rand) []any { peers[i] = ctypes.Peer{NodeInfo: randomNodeInfo(t, random)} } + // Generate random validators + validators := make([]*types.Validator, randomIntInRange(t, random, 3, 32)) + for i := range validators { + validators[i], _ = types.RandValidator(false, 42) + } + + // Get node validator info from validators list or create a new one + var validator *types.Validator + if random.Intn(2) == 0 { + validator = validators[randomIntInRange(t, random, 0, len(validators)-1)] + } else { + validator, _ = types.RandValidator(false, 42) + } + validatorInfo := ctypes.ValidatorInfo{ + Address: validator.Address, + PubKey: validator.PubKey, + VotingPower: validator.VotingPower, + } + return []any{ - &ctypes.ResultStatus{NodeInfo: randomNodeInfo(t, random)}, + &ctypes.ResultStatus{NodeInfo: randomNodeInfo(t, random), ValidatorInfo: validatorInfo}, + &ctypes.ResultValidators{Validators: validators}, &ctypes.ResultNetInfo{Peers: peers}, &ctypes.ResultUnconfirmedTxs{Total: randomIntInRange(t, random, 0, 100)}, @@ -158,6 +178,7 @@ func TestAgent_E2E(t *testing.T) { mockCaller.On("NewBatch").Return(mockBatch) mockBatch.On("Status").Return(nil) + mockBatch.On("Validators").Return(nil) mockBatch.On("NetInfo").Return(nil) mockBatch.On("NumUnconfirmedTxs").Return(nil) mockBatch.On("Block", (*uint64)(nil)).Return(nil) diff --git a/contribs/gnostats/agent/collector.go b/contribs/gnostats/agent/collector.go index 02310a25ce5..07714c5fc15 100644 --- a/contribs/gnostats/agent/collector.go +++ b/contribs/gnostats/agent/collector.go @@ -23,6 +23,7 @@ type rpcClient interface { } type rpcBatch interface { Status() error + Validators() error NetInfo() error NumUnconfirmedTxs() error Block(*uint64) error @@ -38,6 +39,8 @@ func (c *collector) CollectDynamic(ctx context.Context) (*proto.DynamicInfo, err for _, request := range [](func() error){ // Request Status to get address, moniker and validator info batch.Status, + // Request Validators to get the list of validators + batch.Validators, // Request NetInfo to get peers info batch.NetInfo, // Request NumUnconfirmedTxs to get pending txs @@ -60,11 +63,12 @@ func (c *collector) CollectDynamic(ctx context.Context) (*proto.DynamicInfo, err // Cast responses to the appropriate types var ( - status = results[0].(*ctypes.ResultStatus) - netInfo = results[1].(*ctypes.ResultNetInfo) - uncTxs = results[2].(*ctypes.ResultUnconfirmedTxs) - blk = results[3].(*ctypes.ResultBlock) - blkRes = results[4].(*ctypes.ResultBlockResults) + status = results[0].(*ctypes.ResultStatus) + validators = results[1].(*ctypes.ResultValidators) + netInfo = results[2].(*ctypes.ResultNetInfo) + uncTxs = results[3].(*ctypes.ResultUnconfirmedTxs) + blk = results[4].(*ctypes.ResultBlock) + blkRes = results[5].(*ctypes.ResultBlockResults) ) // Convert the list of peers from NetInfo to proto type @@ -78,9 +82,14 @@ func (c *collector) CollectDynamic(ctx context.Context) (*proto.DynamicInfo, err } } - // Determine if the node is a validator using validatorInfo - // @TODO: Check if this is right with @zivkovicmilos - isValidator := status.ValidatorInfo.Address.ID().String() != "" + // Determine if the node is a validator for the last block by searching for + // own validatorInfo address in validators list + isValidator := false + for _, validator := range validators.Validators { + if validator.Address.Compare(status.ValidatorInfo.Address) == 0 { + isValidator = true + } + } // Get gas used / wanted in DeliverTxs (if any) var gasUsed, gasWanted uint64 diff --git a/contribs/gnostats/agent/collector_test.go b/contribs/gnostats/agent/collector_test.go index c620d7ea59c..65e0337171d 100644 --- a/contribs/gnostats/agent/collector_test.go +++ b/contribs/gnostats/agent/collector_test.go @@ -12,6 +12,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/state" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/gnolang/gno/tm2/pkg/crypto" + "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" "github.com/gnolang/gno/tm2/pkg/p2p" "github.com/gnolang/gnostats/proto" "github.com/stretchr/testify/assert" @@ -40,6 +41,11 @@ func (m *MockRPCBatch) Status() error { return args.Error(0) } +func (m *MockRPCBatch) Validators() error { + args := m.Called() + return args.Error(0) +} + func (m *MockRPCBatch) NetInfo() error { args := m.Called() return args.Error(0) @@ -102,6 +108,16 @@ func getBatchResults(t *testing.T) []any { ) } + // Generate validators for Validators request + validators := make([]*types.Validator, 3) + for i, pubKey := range []crypto.PubKey{ + ed25519.GenPrivKeyFromSecret([]byte("validator1")).PubKey(), + ed25519.GenPrivKeyFromSecret([]byte("validator2")).PubKey(), + ed25519.GenPrivKeyFromSecret([]byte("validator3")).PubKey(), + } { + validators[i] = types.NewValidator(pubKey, 42) + } + return []any{ &ctypes.ResultStatus{ NodeInfo: p2p.NodeInfo{ @@ -114,6 +130,15 @@ func getBatchResults(t *testing.T) []any { }, ), }, + ValidatorInfo: ctypes.ValidatorInfo{ + Address: validators[2].Address, + PubKey: validators[2].PubKey, + VotingPower: validators[2].VotingPower, + }, + }, + + &ctypes.ResultValidators{ + Validators: validators, }, &ctypes.ResultNetInfo{ @@ -151,16 +176,24 @@ func compareBatchResultToDynamicInfo(t *testing.T, results []any, dynamicInfo *p t.Helper() var ( - status = results[0].(*ctypes.ResultStatus) - netInfo = results[1].(*ctypes.ResultNetInfo) - uncTxs = results[2].(*ctypes.ResultUnconfirmedTxs) - blk = results[3].(*ctypes.ResultBlock) - blkRes = results[4].(*ctypes.ResultBlockResults) + status = results[0].(*ctypes.ResultStatus) + validators = results[1].(*ctypes.ResultValidators) + netInfo = results[2].(*ctypes.ResultNetInfo) + uncTxs = results[3].(*ctypes.ResultUnconfirmedTxs) + blk = results[4].(*ctypes.ResultBlock) + blkRes = results[5].(*ctypes.ResultBlockResults) ) assert.Equal(t, dynamicInfo.Address, status.NodeInfo.ID().String()) assert.Equal(t, dynamicInfo.Moniker, status.NodeInfo.Moniker) - assert.Equal(t, dynamicInfo.IsValidator, status.ValidatorInfo.Address.ID().String() != "") + + isValidator := false + for _, validator := range validators.Validators { + if validator.Address.Compare(status.ValidatorInfo.Address) == 0 { + isValidator = true + } + } + assert.Equal(t, dynamicInfo.IsValidator, isValidator) assert.NotNil(t, dynamicInfo.NetInfo) assert.Equal(t, dynamicInfo.NetInfo.P2PAddress, status.NodeInfo.NetAddress.String()) @@ -190,6 +223,7 @@ func TestCollector_DynamicSuccess(t *testing.T) { mockCaller.On("NewBatch").Return(mockBatch) mockBatch.On("Status").Return(nil) + mockBatch.On("Validators").Return(nil) mockBatch.On("NetInfo").Return(nil) mockBatch.On("NumUnconfirmedTxs").Return(nil) mockBatch.On("Block", (*uint64)(nil)).Return(nil) @@ -238,6 +272,7 @@ func TestCollector_DynamicTimeout(t *testing.T) { mockCaller.On("NewBatch").Return(mockBatch) mockBatch.On("Status").Return(nil) + mockBatch.On("Validators").Return(nil) mockBatch.On("NetInfo").Return(nil) mockBatch.On("NumUnconfirmedTxs").Return(nil) mockBatch.On("Block", (*uint64)(nil)).Return(nil) From 78825486b022f8c9a1eaee8037b69cb283e52071 Mon Sep 17 00:00:00 2001 From: aeddi Date: Wed, 28 Aug 2024 21:46:27 +0200 Subject: [PATCH 13/13] fix(gnostats): get correct gas values --- contribs/gnostats/agent/agent_test.go | 18 ++++++++++-------- contribs/gnostats/agent/collector.go | 10 ++++++---- contribs/gnostats/agent/collector_test.go | 18 ++++++++++++------ 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/contribs/gnostats/agent/agent_test.go b/contribs/gnostats/agent/agent_test.go index f54ee97fc73..4624eec49e2 100644 --- a/contribs/gnostats/agent/agent_test.go +++ b/contribs/gnostats/agent/agent_test.go @@ -142,6 +142,15 @@ func getRandomBatchResults(t *testing.T, random *mrand.Rand) []any { VotingPower: validator.VotingPower, } + // Generate random deliverTxs + deliverTxs := make([]abci.ResponseDeliverTx, randomIntInRange(t, random, 0, 32)) + for i := range deliverTxs { + deliverTxs[i] = abci.ResponseDeliverTx{ + GasUsed: int64(randomIntInRange(t, random, 5, 1000)), + GasWanted: int64(randomIntInRange(t, random, 5, 1000)), + } + } + return []any{ &ctypes.ResultStatus{NodeInfo: randomNodeInfo(t, random), ValidatorInfo: validatorInfo}, &ctypes.ResultValidators{Validators: validators}, @@ -158,14 +167,7 @@ func getRandomBatchResults(t *testing.T, random *mrand.Rand) []any { }, }, - &ctypes.ResultBlockResults{ - Results: &state.ABCIResponses{ - DeliverTxs: []abci.ResponseDeliverTx{{ - GasUsed: int64(randomIntInRange(t, random, 5, 1000)), - GasWanted: int64(randomIntInRange(t, random, 5, 1000)), - }}, - }, - }, + &ctypes.ResultBlockResults{Results: &state.ABCIResponses{DeliverTxs: deliverTxs}}, } } diff --git a/contribs/gnostats/agent/collector.go b/contribs/gnostats/agent/collector.go index 07714c5fc15..e1299e9b6cf 100644 --- a/contribs/gnostats/agent/collector.go +++ b/contribs/gnostats/agent/collector.go @@ -91,11 +91,13 @@ func (c *collector) CollectDynamic(ctx context.Context) (*proto.DynamicInfo, err } } - // Get gas used / wanted in DeliverTxs (if any) + // Sum gas used / wanted for each DeliverTx var gasUsed, gasWanted uint64 - if blkRes.Results != nil && len(blkRes.Results.DeliverTxs) > 0 { - gasUsed = uint64(blkRes.Results.DeliverTxs[0].GasUsed) - gasWanted = uint64(blkRes.Results.DeliverTxs[0].GasWanted) + if blkRes.Results != nil { + for _, deliverTx := range blkRes.Results.DeliverTxs { + gasUsed += uint64(deliverTx.GasUsed) + gasWanted += uint64(deliverTx.GasWanted) + } } // Fill the DynamicInfo fields with the corresponding values diff --git a/contribs/gnostats/agent/collector_test.go b/contribs/gnostats/agent/collector_test.go index 65e0337171d..dcb561a9ed5 100644 --- a/contribs/gnostats/agent/collector_test.go +++ b/contribs/gnostats/agent/collector_test.go @@ -163,10 +163,10 @@ func getBatchResults(t *testing.T) []any { &ctypes.ResultBlockResults{ Results: &state.ABCIResponses{ - DeliverTxs: []abci.ResponseDeliverTx{{ - GasUsed: 100, - GasWanted: 200, - }}, + DeliverTxs: []abci.ResponseDeliverTx{ + {GasUsed: 100, GasWanted: 200}, + {GasUsed: 42, GasWanted: 24}, + }, }, }, } @@ -209,8 +209,14 @@ func compareBatchResultToDynamicInfo(t *testing.T, results []any, dynamicInfo *p assert.NotNil(t, dynamicInfo.BlockInfo) assert.Equal(t, dynamicInfo.BlockInfo.Number, uint64(blk.Block.Height)) assert.Equal(t, dynamicInfo.BlockInfo.Timestamp, uint64(blk.Block.Time.Unix())) - assert.Equal(t, dynamicInfo.BlockInfo.GasUsed, uint64(blkRes.Results.DeliverTxs[0].GasUsed)) - assert.Equal(t, dynamicInfo.BlockInfo.GasWanted, uint64(blkRes.Results.DeliverTxs[0].GasWanted)) + + var gasUsed, gasWanted uint64 + for _, deliverTx := range blkRes.Results.DeliverTxs { + gasUsed += uint64(deliverTx.GasUsed) + gasWanted += uint64(deliverTx.GasWanted) + } + assert.Equal(t, dynamicInfo.BlockInfo.GasUsed, gasUsed) + assert.Equal(t, dynamicInfo.BlockInfo.GasWanted, gasWanted) assert.Equal(t, dynamicInfo.BlockInfo.Proposer, blk.Block.ProposerAddress.ID().String()) }