Skip to content
This repository has been archived by the owner on Apr 15, 2024. It is now read-only.

feat: support json output for query signers command #338

Merged
merged 9 commits into from
May 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 130 additions & 27 deletions cmd/qgb/query/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package query

import (
"context"
"encoding/json"
"fmt"
"math/big"
"os"
Expand Down Expand Up @@ -119,7 +120,7 @@ func Signers() *cobra.Command {
return fmt.Errorf("nonce 1 doesn't need to be signed. signatures start from nonce 2")
}

err = getSignaturesAndPrintThem(ctx, logger, appQuerier, tmQuerier, p2pQuerier, nonce)
err = getSignaturesAndPrintThem(ctx, logger, appQuerier, tmQuerier, p2pQuerier, nonce, config.outputFile)
if err != nil {
return err
}
Expand All @@ -129,7 +130,28 @@ func Signers() *cobra.Command {
return addFlags(command)
}

func getSignaturesAndPrintThem(ctx context.Context, logger tmlog.Logger, appQuerier *rpc.AppQuerier, tmQuerier *rpc.TmQuerier, p2pQuerier *p2p.Querier, nonce uint64) error {
type signature struct {
EvmAddress string `json:"evmAddress"`
Signature string `json:"signature"`
Signed bool `json:"signed"`
}
type queryOutput struct {
Signatures []signature `json:"signatures"`
Nonce uint64 `json:"nonce"`
MajorityThreshold uint64 `json:"majority_threshold"`
CurrentThreshold uint64 `json:"current_threshold"`
CanRelay bool `json:"can_relay"`
}

func getSignaturesAndPrintThem(
ctx context.Context,
logger tmlog.Logger,
appQuerier *rpc.AppQuerier,
tmQuerier *rpc.TmQuerier,
p2pQuerier *p2p.Querier,
nonce uint64,
outputFile string,
) error {
logger.Info("getting signatures for nonce", "nonce", nonce)

lastValset, err := appQuerier.QueryLastValsetBeforeNonce(ctx, nonce)
Expand Down Expand Up @@ -159,11 +181,15 @@ func getSignaturesAndPrintThem(ctx context.Context, logger tmlog.Logger, appQuer
if err != nil {
return err
}
confirmsMap := make(map[string]string)
for _, confirm := range confirms {
confirmsMap[confirm.EthAddress] = confirm.Signature
qOutput := toQueryOutput(toValsetConfirmsMap(confirms), nonce, *lastValset)
if outputFile == "" {
printConfirms(logger, qOutput)
} else {
err := writeConfirmsToJSONFile(logger, qOutput, outputFile)
if err != nil {
return err
}
}
printConfirms(logger, confirmsMap, lastValset)
case celestiatypes.DataCommitmentRequestType:
dc, ok := att.(*celestiatypes.DataCommitment)
if !ok {
Expand All @@ -182,11 +208,15 @@ func getSignaturesAndPrintThem(ctx context.Context, logger tmlog.Logger, appQuer
if err != nil {
return err
}
confirmsMap := make(map[string]string)
for _, confirm := range confirms {
confirmsMap[confirm.EthAddress] = confirm.Signature
qOutput := toQueryOutput(toDataCommitmentConfirmsMap(confirms), nonce, *lastValset)
if outputFile == "" {
printConfirms(logger, qOutput)
} else {
err := writeConfirmsToJSONFile(logger, qOutput, outputFile)
if err != nil {
return err
}
}
printConfirms(logger, confirmsMap, lastValset)
default:
return errors.Wrap(types.ErrUnknownAttestationType, strconv.FormatUint(nonce, 10))
}
Expand All @@ -202,28 +232,101 @@ func parseNonce(ctx context.Context, querier *rpc.AppQuerier, nonce string) (uin
}
}

func printConfirms(logger tmlog.Logger, confirmsMap map[string]string, valset *celestiatypes.Valset) {
signers := make(map[string]string)
missingSigners := make([]string, 0)
func toValsetConfirmsMap(confirms []types.ValsetConfirm) map[string]string {
// create a map for the signatures to get them easily
confirmsMap := make(map[string]string)
for _, confirm := range confirms {
confirmsMap[confirm.EthAddress] = confirm.Signature
}
return confirmsMap
}

func toDataCommitmentConfirmsMap(confirms []types.DataCommitmentConfirm) map[string]string {
// create a map for the signatures to get them easily
confirmsMap := make(map[string]string)
for _, confirm := range confirms {
confirmsMap[confirm.EthAddress] = confirm.Signature
}
return confirmsMap
}

func toQueryOutput(confirmsMap map[string]string, nonce uint64, lastValset celestiatypes.Valset) queryOutput {
currThreshold := uint64(0)
signatures := make([]signature, len(lastValset.Members))
// create the signature slice to be used for outputting the data
for key, val := range lastValset.Members {
sig, found := confirmsMap[val.EvmAddress]
if found {
signatures[key] = signature{
EvmAddress: val.EvmAddress,
Signature: sig,
Signed: true,
}
currThreshold += val.Power
} else {
signatures[key] = signature{
EvmAddress: val.EvmAddress,
Signature: "",
Signed: false,
}
}
}
return queryOutput{
Signatures: signatures,
Nonce: nonce,
MajorityThreshold: lastValset.TwoThirdsThreshold(),
CurrentThreshold: currThreshold,
CanRelay: lastValset.TwoThirdsThreshold() <= currThreshold,
}
}

for _, validator := range valset.Members {
val, ok := confirmsMap[validator.EvmAddress]
if ok {
signers[validator.EvmAddress] = val
continue
func printConfirms(logger tmlog.Logger, qOutput queryOutput) {
logger.Info(
"query output",
"nonce",
qOutput.Nonce,
"majority_threshold",
qOutput.MajorityThreshold,
"current_threshold",
qOutput.CurrentThreshold,
"can_relay",
qOutput.CanRelay,
)
logger.Info("orchestrators that signed the attestation")
for _, sig := range qOutput.Signatures {
if sig.Signed {
logger.Info(sig.EvmAddress, "signed", sig.Signed, "signature", sig.Signature)
}
missingSigners = append(missingSigners, validator.EvmAddress)
}
logger.Info("orchestrators that missed signing the attestation")
for _, sig := range qOutput.Signatures {
if !sig.Signed {
logger.Info(sig.EvmAddress, "signed", sig.Signed)
}
}
logger.Info("done")
}

func writeConfirmsToJSONFile(logger tmlog.Logger, qOutput queryOutput, outputFile string) error {
logger.Info("writing confirms json file", "path", outputFile)

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++
file, err := os.OpenFile(outputFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644)
if err != nil {
return err
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
logger.Error("failed to close file", "err", err.Error())
}
}(file)

logger.Info("orchestrators that missed signing the attestation", "count", len(missingSigners))
for i, addr := range missingSigners {
logger.Info(addr, "number", i)
encoder := json.NewEncoder(file)
err = encoder.Encode(qOutput)
if err != nil {
return err
}

logger.Info("output written to file successfully", "path", outputFile)
return nil
}
12 changes: 11 additions & 1 deletion cmd/qgb/query/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,24 @@ import (
"github.com/spf13/cobra"
)

const FlagP2PNode = "p2p-node"
const (
FlagP2PNode = "p2p-node"
FlagOutputFile = "output-file"
)

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)")
cmd.Flags().StringP(FlagOutputFile, "o", "", "Path to an output file path if the results need to be written to a json file. Leaving it as empty will result in printing the result to stdout")

return cmd
}

type Config struct {
celesGRPC, tendermintRPC string
targetNode string
outputFile string
}

func parseFlags(cmd *cobra.Command) (Config, error) {
Expand All @@ -33,10 +38,15 @@ func parseFlags(cmd *cobra.Command) (Config, error) {
if err != nil {
return Config{}, err
}
outputFile, err := cmd.Flags().GetString(FlagOutputFile)
if err != nil {
return Config{}, err
}

return Config{
celesGRPC: celesGRPC,
tendermintRPC: tendermintRPC,
targetNode: targetNode,
outputFile: outputFile,
}, nil
}