-
Notifications
You must be signed in to change notification settings - Fork 44
/
sign.go
159 lines (134 loc) · 4.87 KB
/
sign.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package sign
import (
"encoding/hex"
"encoding/json"
"fmt"
"strings"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/spf13/cobra"
)
type SignatureData struct {
Address string `json:"address"`
PubKey string `json:"pub_key"`
Signature string `json:"signature"`
Value string `json:"value"`
}
// GetSignCmd returns the command allowing to sign an arbitrary for later verification
func GetSignCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "sign [value]",
Short: "Sign the given value using the private key associated to either the address or the private key provided with the --from flag",
Long: `
Sign the given value using the private key associated to either the address or the private key provided with the --from flag.
If the provided address/key name is associated to a key that leverages a Ledger device, the signed value will be placed inside the memo field of a transaction before being signed.
Otherwise, the provided value will be converted to raw bytes and then signed without any further transformation.
In both cases, after the signature the following data will be printed inside a JSON object:
- the hex-encoded address associated to the key used to sign the value
- the hex-encoded public key associated to the private key used to sign the value
- the hex-encoded signed value
- the hex-encoded signature value
The printed JSON object can be safely used as the verification proof when connecting a Desmos profile to a centralized application.`,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
// Build a tx factory
txFactory, err := tx.NewFactoryCLI(clientCtx, cmd.Flags())
if err != nil {
return err
}
// Get the value of the "from" flag
from, _ := cmd.Flags().GetString(flags.FlagFrom)
_, fromName, _, err := client.GetFromFields(clientCtx, txFactory.Keybase(), from)
if err != nil {
return fmt.Errorf("error getting account from keybase: %w", err)
}
// Get the key from the keybase
key, err := txFactory.Keybase().Key(fromName)
if err != nil {
return err
}
// Sign the value based on the signing mode
var valueBz, sigBz []byte
if txFactory.SignMode() == signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON {
valueBz, sigBz, err = signAmino(clientCtx, txFactory, key, args[0])
} else {
valueBz, sigBz, err = signRaw(txFactory, key, args[0])
}
if err != nil {
return err
}
// Build the signature data output
pubKey, err := key.GetPubKey()
if err != nil {
return err
}
signatureData := SignatureData{
Address: strings.ToLower(pubKey.Address().String()),
Signature: strings.ToLower(hex.EncodeToString(sigBz)),
PubKey: strings.ToLower(hex.EncodeToString(pubKey.Bytes())),
Value: hex.EncodeToString(valueBz),
}
// Serialize the output as JSON and print it
bz, err := json.Marshal(&signatureData)
if err != nil {
return err
}
return clientCtx.PrintBytes(bz)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
// signRaw signs the given value directly by converting it into raw bytes
func signRaw(txFactory tx.Factory, key *keyring.Record, value string) (valueBz []byte, sigBz []byte, err error) {
valueBz = []byte(value)
sigBz, _, err = txFactory.Keybase().Sign(key.Name, valueBz)
return valueBz, sigBz, err
}
// signAmino puts the given value into a transaction memo field, and signs the transaction using the Amino encoding
func signAmino(clientCtx client.Context, txFactory tx.Factory, key *keyring.Record, value string) (valueBz []byte, sigBz []byte, err error) {
// Set a fake chain id
txFactory = txFactory.WithChainID("desmos")
// Set the memo to be the value to be signed
txFactory = txFactory.WithMemo(value)
// Build the fake transaction
txBuilder, err := txFactory.BuildUnsignedTx()
if err != nil {
return
}
// Sign the data with the private key
err = tx.Sign(txFactory, key.Name, txBuilder, true)
if err != nil {
return
}
// Encode the transaction
address, err := key.GetAddress()
if err != nil {
return
}
signMode := signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON
signerData := authsigning.SignerData{
ChainID: txFactory.ChainID(),
AccountNumber: txFactory.AccountNumber(),
Sequence: txFactory.Sequence(),
Address: address.String(),
}
valueBz, err = clientCtx.TxConfig.SignModeHandler().GetSignBytes(signMode, signerData, txBuilder.GetTx())
if err != nil {
return
}
// Get the signature bytes
sigs, err := txBuilder.GetTx().GetSignaturesV2()
if err != nil {
return
}
sigBz = sigs[0].Data.(*signing.SingleSignatureData).Signature
return valueBz, sigBz, nil
}