-
Notifications
You must be signed in to change notification settings - Fork 17
feat: add the query signers
command
#315
Changes from all commits
c11bbd7
0c2e3f1
b8e814d
b45cccc
f4b9b02
1b51592
a585a4a
e5dd8d8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
package query | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"math/big" | ||
"os" | ||
"strconv" | ||
"time" | ||
|
||
celestiatypes "github.com/celestiaorg/celestia-app/x/qgb/types" | ||
"github.com/celestiaorg/orchestrator-relayer/cmd/qgb/common" | ||
"github.com/celestiaorg/orchestrator-relayer/p2p" | ||
"github.com/celestiaorg/orchestrator-relayer/rpc" | ||
"github.com/celestiaorg/orchestrator-relayer/types" | ||
ds "github.com/ipfs/go-datastore" | ||
dssync "github.com/ipfs/go-datastore/sync" | ||
"github.com/libp2p/go-libp2p" | ||
"github.com/libp2p/go-libp2p/core/peer" | ||
"github.com/pkg/errors" | ||
"github.com/spf13/cobra" | ||
tmlog "github.com/tendermint/tendermint/libs/log" | ||
) | ||
|
||
func Command() *cobra.Command { | ||
queryCmd := &cobra.Command{ | ||
Use: "query", | ||
Aliases: []string{"q"}, | ||
Short: "Query relevant information from a running QGB", | ||
SilenceUsage: true, | ||
} | ||
|
||
queryCmd.AddCommand( | ||
Signers(), | ||
) | ||
|
||
queryCmd.SetHelpCommand(&cobra.Command{}) | ||
|
||
return queryCmd | ||
} | ||
|
||
func Signers() *cobra.Command { | ||
command := &cobra.Command{ | ||
Use: "signers <nonce>", | ||
Args: cobra.ExactArgs(1), | ||
Short: "Queries the QGB for attestations signers", | ||
Long: "Queries the QGB for attestations signers. The nonce is the attestation nonce that the command" + | ||
" will query signatures for. It should be either a specific nonce starting from 2 and on." + | ||
" Or, use 'latest' as argument to check the latest attestation nonce", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
config, err := parseFlags(cmd) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// creating the logger | ||
logger := tmlog.NewTMLogger(os.Stdout) | ||
logger.Debug("initializing queriers") | ||
|
||
ctx, cancel := context.WithCancel(cmd.Context()) | ||
defer cancel() | ||
|
||
stopFuncs := make([]func() error, 0) | ||
defer func() { | ||
for _, f := range stopFuncs { | ||
err := f() | ||
if err != nil { | ||
logger.Error(err.Error()) | ||
} | ||
} | ||
}() | ||
|
||
// create tm querier and app querier | ||
tmQuerier, appQuerier, stops, err := common.NewTmAndAppQuerier(logger, config.tendermintRPC, config.celesGRPC) | ||
stopFuncs = append(stopFuncs, stops...) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// creating the host | ||
h, err := libp2p.New() | ||
if err != nil { | ||
return err | ||
} | ||
addrInfo, err := peer.AddrInfoFromString(config.targetNode) | ||
if err != nil { | ||
return err | ||
} | ||
for i := 0; i < 5; i++ { | ||
logger.Debug("connecting to target node...") | ||
err := h.Connect(ctx, *addrInfo) | ||
if err != nil { | ||
logger.Error("couldn't connect to target node", "err", err.Error()) | ||
} | ||
if err == nil { | ||
logger.Debug("connected to target node") | ||
break | ||
} | ||
time.Sleep(5 * time.Second) | ||
} | ||
|
||
// creating the data store | ||
dataStore := dssync.MutexWrap(ds.NewMapDatastore()) | ||
|
||
// creating the dht | ||
dht, err := p2p.NewQgbDHT(cmd.Context(), h, dataStore, []peer.AddrInfo{}, logger) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// creating the p2p querier | ||
p2pQuerier := p2p.NewQuerier(dht, logger) | ||
|
||
nonce, err := parseNonce(ctx, appQuerier, args[0]) | ||
if err != nil { | ||
return err | ||
} | ||
if nonce == 1 { | ||
return fmt.Errorf("nonce 1 doesn't need to be signed. signatures start from nonce 2") | ||
} | ||
|
||
err = getSignaturesAndPrintThem(ctx, logger, appQuerier, tmQuerier, p2pQuerier, nonce) | ||
if err != nil { | ||
return err | ||
} | ||
Comment on lines
+102
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how long should this take and how heavy is this operation? should we just query an existing relayer instead of spinning up one in memory and querying the dht? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It takes no time to do this. Yes, it would make sense to add an RPC endpoint to get this information. However, we don't have time right now to implement it and we need a way to know who signed or not. We can merge this and open an issue to have this as an endpoint, what do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. okie dokie that's what I thought |
||
return nil | ||
}, | ||
} | ||
return addFlags(command) | ||
} | ||
|
||
func getSignaturesAndPrintThem(ctx context.Context, logger tmlog.Logger, appQuerier *rpc.AppQuerier, tmQuerier *rpc.TmQuerier, p2pQuerier *p2p.Querier, nonce uint64) error { | ||
logger.Info("getting signatures for nonce", "nonce", nonce) | ||
|
||
lastValset, err := appQuerier.QueryLastValsetBeforeNonce(ctx, nonce) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
att, err := appQuerier.QueryAttestationByNonce(ctx, nonce) | ||
if err != nil { | ||
return err | ||
} | ||
if att == nil { | ||
return celestiatypes.ErrAttestationNotFound | ||
} | ||
|
||
switch att.Type() { | ||
case celestiatypes.ValsetRequestType: | ||
vs, ok := att.(*celestiatypes.Valset) | ||
if !ok { | ||
return errors.Wrap(celestiatypes.ErrAttestationNotValsetRequest, strconv.FormatUint(nonce, 10)) | ||
} | ||
signBytes, err := vs.SignBytes() | ||
if err != nil { | ||
return err | ||
} | ||
confirms, err := p2pQuerier.QueryValsetConfirms(ctx, nonce, *lastValset, signBytes.Hex()) | ||
if err != nil { | ||
return err | ||
} | ||
confirmsMap := make(map[string]string) | ||
for _, confirm := range confirms { | ||
confirmsMap[confirm.EthAddress] = confirm.Signature | ||
} | ||
printConfirms(logger, confirmsMap, lastValset) | ||
case celestiatypes.DataCommitmentRequestType: | ||
dc, ok := att.(*celestiatypes.DataCommitment) | ||
if !ok { | ||
return errors.Wrap(types.ErrAttestationNotDataCommitmentRequest, strconv.FormatUint(nonce, 10)) | ||
} | ||
commitment, err := tmQuerier.QueryCommitment( | ||
ctx, | ||
dc.BeginBlock, | ||
dc.EndBlock, | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
dataRootHash := types.DataCommitmentTupleRootSignBytes(big.NewInt(int64(dc.Nonce)), commitment) | ||
confirms, err := p2pQuerier.QueryDataCommitmentConfirms(ctx, *lastValset, nonce, dataRootHash.Hex()) | ||
if err != nil { | ||
return err | ||
} | ||
confirmsMap := make(map[string]string) | ||
for _, confirm := range confirms { | ||
confirmsMap[confirm.EthAddress] = confirm.Signature | ||
} | ||
printConfirms(logger, confirmsMap, lastValset) | ||
default: | ||
return errors.Wrap(types.ErrUnknownAttestationType, strconv.FormatUint(nonce, 10)) | ||
} | ||
return nil | ||
} | ||
|
||
func parseNonce(ctx context.Context, querier *rpc.AppQuerier, nonce string) (uint64, error) { | ||
switch nonce { | ||
case "latest": | ||
return querier.QueryLatestAttestationNonce(ctx) | ||
default: | ||
return strconv.ParseUint(nonce, 10, 0) | ||
} | ||
} | ||
|
||
func printConfirms(logger tmlog.Logger, confirmsMap map[string]string, valset *celestiatypes.Valset) { | ||
signers := make(map[string]string) | ||
missingSigners := make([]string, 0) | ||
|
||
for _, validator := range valset.Members { | ||
val, ok := confirmsMap[validator.EvmAddress] | ||
if ok { | ||
signers[validator.EvmAddress] = val | ||
continue | ||
} | ||
missingSigners = append(missingSigners, validator.EvmAddress) | ||
} | ||
|
||
logger.Info("orchestrators that signed the attestation", "count", len(signers)) | ||
i := 0 | ||
for addr, sig := range signers { | ||
logger.Info(addr, "number", i, "signature", sig) | ||
i++ | ||
} | ||
|
||
logger.Info("orchestrators that missed signing the attestation", "count", len(missingSigners)) | ||
for i, addr := range missingSigners { | ||
logger.Info(addr, "number", i) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package query | ||
|
||
import ( | ||
"github.com/celestiaorg/orchestrator-relayer/cmd/qgb/relayer" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
const FlagP2PNode = "p2p-node" | ||
|
||
func addFlags(cmd *cobra.Command) *cobra.Command { | ||
cmd.Flags().StringP(relayer.FlagCelesGRPC, "c", "localhost:9090", "Specify the grpc address") | ||
cmd.Flags().StringP(relayer.FlagTendermintRPC, "t", "http://localhost:26657", "Specify the rest rpc address") | ||
cmd.Flags().StringP(FlagP2PNode, "n", "", "P2P target node multiaddress (eg. /ip4/127.0.0.1/tcp/30000/p2p/12D3KooWBSMasWzRSRKXREhediFUwABNZwzJbkZcYz5rYr9Zdmfn)") | ||
|
||
return cmd | ||
} | ||
|
||
type Config struct { | ||
celesGRPC, tendermintRPC string | ||
targetNode string | ||
} | ||
|
||
func parseFlags(cmd *cobra.Command) (Config, error) { | ||
tendermintRPC, err := cmd.Flags().GetString(relayer.FlagTendermintRPC) | ||
if err != nil { | ||
return Config{}, err | ||
} | ||
celesGRPC, err := cmd.Flags().GetString(relayer.FlagCelesGRPC) | ||
if err != nil { | ||
return Config{}, err | ||
} | ||
targetNode, err := cmd.Flags().GetString(FlagP2PNode) | ||
if err != nil { | ||
return Config{}, err | ||
} | ||
|
||
return Config{ | ||
celesGRPC: celesGRPC, | ||
tendermintRPC: tendermintRPC, | ||
targetNode: targetNode, | ||
}, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[not blocking]
ideally we just call this later and pass the slice after we have appended to it. This should work, I think its just not ideal since multiple routines could touch that slice simultaneously. also, since we're already
make
ing a slice, then we might as well use the expected capacityThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#322