/
cmd_connect.go
141 lines (114 loc) · 4.49 KB
/
cmd_connect.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
package bot
import (
"encoding/hex"
"fmt"
"strings"
"time"
signcmd "github.com/desmos-labs/desmos/v4/app/desmos/cmd/sign"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/tendermint/tendermint/libs/json"
"github.com/desmos-labs/hephaestus/utils"
"github.com/andersfylling/disgord"
"github.com/desmos-labs/hephaestus/types"
)
type CallData struct {
// Username is the plain-text Discord username of the user that wants to be verified
Username string `json:"username"`
}
// NewCallData returns a new CallData instance
func NewCallData(username string) *CallData {
return &CallData{
Username: username,
}
}
// HandleConnect handle a connection request. This request is done by the users when they want to connect their Desmos
// profile with their Discord account.
// The command expects one single argument which must be the JSON object returned from the "desmos sign" command.
//
// The handling of the command will fail in the following occasions:
// 1. The signed value does not correspond to the username of the user sending the message
// 2. Any of the values are badly encoded
func (bot *Bot) HandleConnect(s disgord.Session, data *disgord.MessageCreate) error {
// Get the arguments
msg := data.Message
parts := strings.SplitN(msg.Content, " ", 3)[1:]
if len(parts) == 0 {
bot.Reply(msg, s, fmt.Sprintf(`**Connect**
This command allows you to connect your Discord account to your Desmos profile.
To do this, you have to:
1. Sign your Discord username using the Desmos CLI or any Desmos-compatible application.
2. Use the %[1]s command to send the signature result.
__Signing your Discord username__
1. Copy your Discord username by clicking on it in the bottom part of your Discord client.
Your full username should be in the form of <username>#<identifier> (eg. Foo#123).
2. Open your Desmos CLI or application, and sign your username.
If you use the Desmos CLI, you can do this by using the following command:
`+"`desmos sign <Discord username> --from <your-key>`"+`
Eg. `+"`desmos sign \"Foo#123\" --from foo`"+`
__Sending the signed value__
The sign command should return a JSON object. The last thing you have to do is now send it to me using the %[1]s command. To do this, simply send me a message as the following:
`+"`!%[1]s <%[2]s/%[3]s> <JSON>`"+`
Eg. `+"`!%[1]s %[2]s {...}`"+`
`, types.CmdConnect, types.NetworkTestnet, types.NetworkMainnet))
return nil
}
// Get the network client to be used
var networkClient = bot.testnet
if parts[0] == types.NetworkMainnet {
networkClient = bot.mainnet
}
if networkClient == nil {
return types.NewWarnErr("Network not enabled for this operation")
}
// Get the signature data
username := utils.GetMsgAuthorUsername(msg)
signatureData, err := bot.getSignatureData(parts[1])
if err != nil {
return err
}
// Upload the data to Themis
err = networkClient.UploadDataToThemis(username, signatureData)
if err != nil {
return err
}
// Return to the user the call data for the Desmos command
callDataBz, err := json.Marshal(NewCallData(username))
if err != nil {
return types.NewWarnErr("Error while serializing call data: %s", err)
}
bot.Reply(msg, s, fmt.Sprintf("Your verification data has been stored successfully. "+
"All you have to do now is execute the following command:\n"+
"```"+
"desmos tx profiles link-app ibc-profiles [channel] discord \"%[1]s\" %[2]s --packet-timeout-height 0-0 --packet-timeout-timestamp %[3]d --from <key_name>"+
"```",
username,
hex.EncodeToString(callDataBz),
time.Now().Add(time.Hour).UnixNano(),
))
return nil
}
func (bot *Bot) getSignatureData(jsonData string) (*signcmd.SignatureData, error) {
var signatureData signcmd.SignatureData
err := json.Unmarshal([]byte(jsonData), &signatureData)
if err != nil {
return nil, types.NewWarnErr("Invalid data provided: %s", err)
}
// Verify the signature
pubKeyBz, err := hex.DecodeString(signatureData.PubKey)
if err != nil {
return nil, types.NewWarnErr("Error while reading public key: %s", err)
}
valueBz, err := hex.DecodeString(signatureData.Value)
if err != nil {
return nil, types.NewWarnErr("Error while reading value: %s", err)
}
sigBz, err := hex.DecodeString(signatureData.Signature)
if err != nil {
return nil, types.NewWarnErr("Error while reading signature: %s", err)
}
pubKey := secp256k1.PubKey(pubKeyBz)
if !pubKey.VerifySignature(valueBz, sigBz) {
return nil, types.NewWarnErr("Invalid signature. Make sure you have signed the message using the correct account")
}
return &signatureData, nil
}