forked from InjectiveLabs/sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
keys.go
205 lines (174 loc) · 5.55 KB
/
keys.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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package chain
import (
"bytes"
"crypto/rand"
"io"
"log"
"os"
"path/filepath"
cosmcrypto "github.com/cosmos/cosmos-sdk/crypto"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
cosmtypes "github.com/cosmos/cosmos-sdk/types"
"github.com/pkg/errors"
"github.com/InjectiveLabs/sdk-go/chain/crypto/ethsecp256k1"
"github.com/InjectiveLabs/sdk-go/chain/crypto/hd"
"github.com/InjectiveLabs/sdk-go/client/common"
)
const defaultKeyringKeyName = "validator"
var emptyCosmosAddress = cosmtypes.AccAddress{}
func InitCosmosKeyring(
cosmosKeyringDir string,
cosmosKeyringAppName string,
cosmosKeyringBackend string,
cosmosKeyFrom string,
cosmosKeyPassphrase string,
cosmosPrivKey string,
cosmosUseLedger bool,
) (cosmtypes.AccAddress, keyring.Keyring, error) {
switch {
case len(cosmosPrivKey) > 0:
if cosmosUseLedger {
err := errors.New("cannot combine ledger and privkey options")
return emptyCosmosAddress, nil, err
}
pkBytes, err := common.HexToBytes(cosmosPrivKey)
if err != nil {
err = errors.Wrap(err, "failed to hex-decode cosmos account privkey")
return emptyCosmosAddress, nil, err
}
// Specfic to Injective chain with Ethermint keys
// Should be secp256k1.PrivKey for generic Cosmos chain
cosmosAccPk := ðsecp256k1.PrivKey{
Key: pkBytes,
}
addressFromPk := cosmtypes.AccAddress(cosmosAccPk.PubKey().Address().Bytes())
var keyName string
// check that if cosmos 'From' specified separately, it must match the provided privkey,
if len(cosmosKeyFrom) > 0 {
addressFrom, err := cosmtypes.AccAddressFromBech32(cosmosKeyFrom)
if err == nil {
if !bytes.Equal(addressFrom.Bytes(), addressFromPk.Bytes()) {
err = errors.Errorf("expected account address %s but got %s from the private key", addressFrom.String(), addressFromPk.String())
return emptyCosmosAddress, nil, err
}
} else {
// use it as a name then
keyName = cosmosKeyFrom
}
}
if len(keyName) == 0 {
keyName = defaultKeyringKeyName
}
// wrap a PK into a Keyring
kb, err := KeyringForPrivKey(keyName, cosmosAccPk)
return addressFromPk, kb, err
case len(cosmosKeyFrom) > 0:
var fromIsAddress bool
addressFrom, err := cosmtypes.AccAddressFromBech32(cosmosKeyFrom)
if err == nil {
fromIsAddress = true
}
var passReader io.Reader = os.Stdin
if len(cosmosKeyPassphrase) > 0 {
passReader = newPassReader(cosmosKeyPassphrase)
}
var absoluteKeyringDir string
if filepath.IsAbs(cosmosKeyringDir) {
absoluteKeyringDir = cosmosKeyringDir
} else {
absoluteKeyringDir, _ = filepath.Abs(cosmosKeyringDir)
}
kb, err := keyring.New(
cosmosKeyringAppName,
cosmosKeyringBackend,
absoluteKeyringDir,
passReader,
hd.EthSecp256k1Option(),
)
if err != nil {
err = errors.Wrap(err, "failed to init keyring")
return emptyCosmosAddress, nil, err
}
var keyInfo keyring.Info
if fromIsAddress {
if keyInfo, err = kb.KeyByAddress(addressFrom); err != nil {
err = errors.Wrapf(err, "couldn't find an entry for the key %s in keybase", addressFrom.String())
return emptyCosmosAddress, nil, err
}
} else {
if keyInfo, err = kb.Key(cosmosKeyFrom); err != nil {
err = errors.Wrapf(err, "could not find an entry for the key '%s' in keybase", cosmosKeyFrom)
return emptyCosmosAddress, nil, err
}
}
switch keyType := keyInfo.GetType(); keyType {
case keyring.TypeLocal:
// kb has a key and it's totally usable
return keyInfo.GetAddress(), kb, nil
case keyring.TypeLedger:
// the kb stores references to ledger keys, so we must explicitly
// check that. kb doesn't know how to scan HD keys - they must be added manually before
if cosmosUseLedger {
return keyInfo.GetAddress(), kb, nil
}
err := errors.Errorf("'%s' key is a ledger reference, enable ledger option", keyInfo.GetName())
return emptyCosmosAddress, nil, err
case keyring.TypeOffline:
err := errors.Errorf("'%s' key is an offline key, not supported yet", keyInfo.GetName())
return emptyCosmosAddress, nil, err
case keyring.TypeMulti:
err := errors.Errorf("'%s' key is an multisig key, not supported yet", keyInfo.GetName())
return emptyCosmosAddress, nil, err
default:
err := errors.Errorf("'%s' key has unsupported type: %s", keyInfo.GetName(), keyType)
return emptyCosmosAddress, nil, err
}
default:
err := errors.New("insufficient cosmos key details provided")
return emptyCosmosAddress, nil, err
}
}
func newPassReader(pass string) io.Reader {
return &passReader{
pass: pass,
buf: new(bytes.Buffer),
}
}
type passReader struct {
pass string
buf *bytes.Buffer
}
var _ io.Reader = &passReader{}
func (r *passReader) Read(p []byte) (n int, err error) {
n, err = r.buf.Read(p)
if err == io.EOF || n == 0 {
r.buf.WriteString(r.pass + "\n")
n, err = r.buf.Read(p)
}
return
}
// KeyringForPrivKey creates a temporary in-mem keyring for a PrivKey.
// Allows to init Context when the key has been provided in plaintext and parsed.
func KeyringForPrivKey(name string, privKey cryptotypes.PrivKey) (keyring.Keyring, error) {
kb := keyring.NewInMemory(hd.EthSecp256k1Option())
tmpPhrase := randPhrase(64)
armored := cosmcrypto.EncryptArmorPrivKey(privKey, tmpPhrase, privKey.Type())
err := kb.ImportPrivKey(name, armored, tmpPhrase)
if err != nil {
err = errors.Wrap(err, "failed to import privkey")
return nil, err
}
return kb, nil
}
func randPhrase(size int) string {
buf := make([]byte, size)
_, err := rand.Read(buf)
orPanic(err)
return string(buf)
}
func orPanic(err error) {
if err != nil {
log.Panicln()
}
}