Skip to content

Commit 9d8b469

Browse files
authored
Add mnemonics to registrar client (#23)
* support mnemonics in registrar client * fix drive keys from seed * update registrar client readme file * remove extra print lines * remove extra comments * lower case error messages
1 parent c74f8eb commit 9d8b469

16 files changed

+260
-181
lines changed

go.work.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
22
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
3+
github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA=
34
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
45
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
56
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=

node-registrar/client/README.md

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ The Node Registrar Client enables communication with the ThreeFold Grid's node r
2222

2323
* **Create Account**: Create new account on the registrar with uniqe key.
2424
* **Update Account**: Update the account configuration (relays & rmbEncKey).
25-
* **Ensure Account**: Ensures that an account is created with specific seed.
25+
* **Ensure Account**: Ensures that an account is created with specific seed/mnemonic.
2626
* **Get Account**: Get an account using either its twin\_id or its public\_key.
2727

2828
### Farms
@@ -42,10 +42,10 @@ The Node Registrar Client enables communication with the ThreeFold Grid's node r
4242

4343
#### Version Operations
4444

45-
| Method | Description | Parameters | Returns |
46-
|-----------------|----------------------------------|----------------------------|---------------------|
47-
| GetZosVersion | Get current zos version | None | (ZosVersion, error) |
48-
| SetZosVersion | Update zos version (admin-only) | version string, force bool | error |
45+
| Method | Description | Parameters | Returns |
46+
|-----------------|----------------------------------|------------------------------------|---------------------|
47+
| GetZosVersion | Get current zos version | None | (ZosVersion, error) |
48+
| SetZosVersion | Update zos version (admin-only) | version string, safeToUpgrade bool | error |
4949

5050
#### Account Management
5151

@@ -95,15 +95,20 @@ import (
9595
func main() {
9696
registrarURL := "https://registrar.dev4.grid.tf/v1"
9797

98-
s := make([]byte, 32)
99-
_, err := rand.Read(s)
100-
if err != nil {
101-
log.Fatal().Err(err).Send()
102-
}
103-
seed = hex.EncodeToString(s)
104-
fmt.Println("New Seed (Hex):", seed)
98+
// Generate 128-bit entropy (12-word mnemonic)
99+
entropy, err := bip39.NewEntropy(128)
100+
if err != nil {
101+
panic(err)
102+
}
103+
104+
// Generate mnemonic from entropy
105+
mnemonic, err = bip39.NewMnemonic(entropy)
106+
if err != nil {
107+
panic(err)
108+
}
109+
fmt.Println("New Mnemonic:", mnemonic)
105110

106-
cli, err := client.NewRegistrarClient(registrarURL, s)
111+
cli, err := client.NewRegistrarClient(registrarURL, mnemonic)
107112
if err != nil {
108113
panic(err)
109114
}

node-registrar/client/account.go

Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package client
22

33
import (
44
"bytes"
5-
"crypto/ed25519"
65
"encoding/base64"
76
"encoding/json"
87
"fmt"
@@ -11,44 +10,83 @@ import (
1110
"time"
1211

1312
"github.com/pkg/errors"
13+
"github.com/vedhavyas/go-subkey/v2"
1414
)
1515

1616
var ErrorAccountNotFround = fmt.Errorf("failed to get requested account from node regiatrar")
1717

18-
func (c RegistrarClient) CreateAccount(relays []string, rmbEncKey string) (account Account, err error) {
18+
func (c *RegistrarClient) CreateAccount(relays []string, rmbEncKey string) (account Account, mnemonic string, err error) {
1919
return c.createAccount(relays, rmbEncKey)
2020
}
2121

22-
func (c RegistrarClient) GetAccount(id uint64) (account Account, err error) {
22+
func (c *RegistrarClient) GetAccount(id uint64) (account Account, err error) {
2323
return c.getAccount(id)
2424
}
2525

26-
func (c RegistrarClient) GetAccountByPK(pk []byte) (account Account, err error) {
26+
func (c *RegistrarClient) GetAccountByPK(pk []byte) (account Account, err error) {
2727
return c.getAccountByPK(pk)
2828
}
2929

30-
func (c RegistrarClient) UpdateAccount(opts ...UpdateAccountOpts) (err error) {
30+
func (c *RegistrarClient) UpdateAccount(opts ...UpdateAccountOpts) (err error) {
3131
return c.updateAccount(opts)
3232
}
3333

34-
func (c RegistrarClient) EnsureAccount(relays []string, rmbEncKey string) (account Account, err error) {
34+
type accountCfg struct {
35+
relays []string
36+
rmbEncKey string
37+
}
38+
39+
type (
40+
UpdateAccountOpts func(*accountCfg)
41+
)
42+
43+
func UpdateAccountWithRelays(relays []string) UpdateAccountOpts {
44+
return func(n *accountCfg) {
45+
n.relays = relays
46+
}
47+
}
48+
49+
func UpdateAccountWithRMBEncKey(rmbEncKey string) UpdateAccountOpts {
50+
return func(n *accountCfg) {
51+
n.rmbEncKey = rmbEncKey
52+
}
53+
}
54+
55+
func (c *RegistrarClient) EnsureAccount(relays []string, rmbEncKey string) (account Account, err error) {
3556
return c.ensureAccount(relays, rmbEncKey)
3657
}
3758

38-
func (c *RegistrarClient) createAccount(relays []string, rmbEncKey string) (account Account, err error) {
59+
func (c *RegistrarClient) createAccount(relays []string, rmbEncKey string) (account Account, mnemonic string, err error) {
3960
url, err := url.JoinPath(c.baseURL, "accounts")
4061
if err != nil {
41-
return account, errors.Wrap(err, "failed to construct registrar url")
62+
return account, mnemonic, errors.Wrap(err, "failed to construct registrar url")
4263
}
4364

44-
timestamp := time.Now().Unix()
45-
publicKeyBase64 := base64.StdEncoding.EncodeToString(c.keyPair.publicKey)
65+
var keyPair subkey.KeyPair
66+
if len(c.mnemonic) != 0 {
67+
mnemonic = c.mnemonic
68+
keyPair, err = parseKeysFromMnemonicOrSeed(c.mnemonic)
69+
} else {
70+
mnemonic, keyPair, err = generateNewMnemonic()
71+
}
72+
if err != nil {
73+
return account, mnemonic, err
74+
}
4675

76+
c.keyPair = keyPair
77+
c.mnemonic = mnemonic
78+
79+
publicKeyBase64 := base64.StdEncoding.EncodeToString(c.keyPair.Public())
80+
81+
timestamp := time.Now().Unix()
4782
challenge := []byte(fmt.Sprintf("%d:%v", timestamp, publicKeyBase64))
48-
signature := ed25519.Sign(c.keyPair.privateKey, challenge)
83+
signature, err := keyPair.Sign(challenge)
84+
if err != nil {
85+
return account, mnemonic, errors.Wrap(err, "failed to sign account creation request")
86+
}
4987

5088
data := map[string]any{
51-
"public_key": c.keyPair.publicKey,
89+
"public_key": c.keyPair.Public(),
5290
"signature": signature,
5391
"timestamp": timestamp,
5492
"rmb_enc_key": rmbEncKey,
@@ -58,17 +96,17 @@ func (c *RegistrarClient) createAccount(relays []string, rmbEncKey string) (acco
5896
var body bytes.Buffer
5997
err = json.NewEncoder(&body).Encode(data)
6098
if err != nil {
61-
return account, errors.Wrap(err, "failed to parse request body")
99+
return account, mnemonic, errors.Wrap(err, "failed to parse request body")
62100
}
63101

64102
resp, err := c.httpClient.Post(url, "application/json", &body)
65103
if err != nil {
66-
return account, errors.Wrap(err, "failed to send request to the registrar")
104+
return account, mnemonic, errors.Wrap(err, "failed to send request to the registrar")
67105
}
68106

69107
if resp.StatusCode != http.StatusCreated {
70108
err = parseResponseError(resp.Body)
71-
return account, errors.Wrapf(err, "failed to create account with status %s", resp.Status)
109+
return account, mnemonic, errors.Wrapf(err, "failed to create account with status %s", resp.Status)
72110
}
73111
defer resp.Body.Close()
74112

@@ -78,7 +116,7 @@ func (c *RegistrarClient) createAccount(relays []string, rmbEncKey string) (acco
78116
return
79117
}
80118

81-
func (c RegistrarClient) getAccount(id uint64) (account Account, err error) {
119+
func (c *RegistrarClient) getAccount(id uint64) (account Account, err error) {
82120
url, err := url.JoinPath(c.baseURL, "accounts")
83121
if err != nil {
84122
return account, errors.Wrap(err, "failed to construct registrar url")
@@ -116,7 +154,7 @@ func (c RegistrarClient) getAccount(id uint64) (account Account, err error) {
116154
return
117155
}
118156

119-
func (c RegistrarClient) getAccountByPK(pk []byte) (account Account, err error) {
157+
func (c *RegistrarClient) getAccountByPK(pk []byte) (account Account, err error) {
120158
url, err := url.JoinPath(c.baseURL, "accounts")
121159
if err != nil {
122160
return account, errors.Wrap(err, "failed to construct registrar url")
@@ -157,7 +195,7 @@ func (c RegistrarClient) getAccountByPK(pk []byte) (account Account, err error)
157195
return account, err
158196
}
159197

160-
func (c RegistrarClient) updateAccount(opts []UpdateAccountOpts) (err error) {
198+
func (c *RegistrarClient) updateAccount(opts []UpdateAccountOpts) (err error) {
161199
err = c.ensureTwinID()
162200
if err != nil {
163201
return errors.Wrap(err, "failed to ensure twin id")
@@ -180,7 +218,11 @@ func (c RegistrarClient) updateAccount(opts []UpdateAccountOpts) (err error) {
180218
return
181219
}
182220

183-
req.Header.Set("X-Auth", c.signRequest(time.Now().Unix()))
221+
authHeader, err := c.signRequest(time.Now().Unix())
222+
if err != nil {
223+
return errors.Wrap(err, "failed to sign request")
224+
}
225+
req.Header.Set("X-Auth", authHeader)
184226
req.Header.Set("Content-Type", "application/json")
185227

186228
resp, err := c.httpClient.Do(req)
@@ -200,44 +242,20 @@ func (c RegistrarClient) updateAccount(opts []UpdateAccountOpts) (err error) {
200242
return
201243
}
202244

203-
type accountCfg struct {
204-
relays []string
205-
rmbEncKey string
206-
}
207-
208-
type (
209-
UpdateAccountOpts func(*accountCfg)
210-
)
211-
212-
func UpdateAccountWithRelays(relays []string) UpdateAccountOpts {
213-
return func(n *accountCfg) {
214-
n.relays = relays
215-
}
216-
}
217-
218-
func UpdateAccountWithRMBEncKey(rmbEncKey string) UpdateAccountOpts {
219-
return func(n *accountCfg) {
220-
n.rmbEncKey = rmbEncKey
221-
}
222-
}
223-
224-
func (c RegistrarClient) ensureAccount(relays []string, rmbEncKey string) (account Account, err error) {
225-
account, err = c.GetAccountByPK(c.keyPair.publicKey)
245+
func (c *RegistrarClient) ensureAccount(relays []string, rmbEncKey string) (account Account, err error) {
246+
account, err = c.GetAccountByPK(c.keyPair.Public())
226247
if errors.Is(err, ErrorAccountNotFround) {
227-
return c.CreateAccount(relays, rmbEncKey)
228-
} else if err != nil {
229-
return account, errors.Wrap(err, "failed to get account from the registrar")
248+
account, _, err = c.CreateAccount(relays, rmbEncKey)
230249
}
231-
232-
return
250+
return account, err
233251
}
234252

235253
func (c *RegistrarClient) ensureTwinID() error {
236254
if c.twinID != 0 {
237255
return nil
238256
}
239257

240-
twin, err := c.getAccountByPK(c.keyPair.publicKey)
258+
twin, err := c.getAccountByPK(c.keyPair.Public())
241259
if err != nil {
242260
return errors.Wrap(err, "failed to get the account of the node, registrar client was not set up properly")
243261
}

node-registrar/client/account_test.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ func TestCreateAccount(t *testing.T) {
1515
var count int
1616
require := require.New(t)
1717

18-
publicKey, privateKey, err := aliceKeys()
18+
keyPair, err := parseKeysFromMnemonicOrSeed(testMnemonic)
1919
require.NoError(err)
20-
account.PublicKey = base64.StdEncoding.EncodeToString(publicKey)
20+
account.PublicKey = base64.StdEncoding.EncodeToString(keyPair.Public())
2121

2222
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
2323
statusCode, body := serverHandler(r, request, count, require)
@@ -32,12 +32,12 @@ func TestCreateAccount(t *testing.T) {
3232
require.NoError(err)
3333

3434
request = newClientWithNoAccount
35-
c, err := NewRegistrarClient(baseURL, privateKey)
35+
c, err := NewRegistrarClient(baseURL, testMnemonic)
3636
require.NoError(err)
3737

3838
t.Run("test create account created successfully", func(t *testing.T) {
3939
request = createAccountStatusCreated
40-
result, err := c.CreateAccount(account.Relays, account.RMBEncKey)
40+
result, _, err := c.CreateAccount(account.Relays, account.RMBEncKey)
4141
require.NoError(err)
4242
require.Equal(account, result)
4343
})
@@ -48,9 +48,9 @@ func TestUpdateAccount(t *testing.T) {
4848
var count int
4949
require := require.New(t)
5050

51-
publicKey, privateKey, err := aliceKeys()
51+
keyPair, err := parseKeysFromMnemonicOrSeed(testMnemonic)
5252
require.NoError(err)
53-
account.PublicKey = base64.StdEncoding.EncodeToString(publicKey)
53+
account.PublicKey = base64.StdEncoding.EncodeToString(keyPair.Public())
5454

5555
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
5656
statusCode, body := serverHandler(r, request, count, require)
@@ -68,11 +68,11 @@ func TestUpdateAccount(t *testing.T) {
6868
t.Run("test update account updated successfully", func(t *testing.T) {
6969
count = 0
7070
request = newClientWithAccountNoNode
71-
c, err := NewRegistrarClient(baseURL, privateKey)
71+
c, err := NewRegistrarClient(baseURL, testMnemonic)
7272

7373
require.NoError(err)
7474
require.Equal(c.twinID, account.TwinID)
75-
require.Equal(c.keyPair.publicKey, publicKey)
75+
require.Equal(c.keyPair, keyPair)
7676

7777
request = updateAccountWithStatusOK
7878
relays := []string{"relay1"}
@@ -82,10 +82,10 @@ func TestUpdateAccount(t *testing.T) {
8282

8383
t.Run("test update account account not found", func(t *testing.T) {
8484
request = newClientWithNoAccount
85-
c, err := NewRegistrarClient(baseURL, privateKey)
85+
c, err := NewRegistrarClient(baseURL, testMnemonic)
8686

8787
require.NoError(err)
88-
require.Equal(c.keyPair.publicKey, publicKey)
88+
require.Equal(c.keyPair, keyPair)
8989

9090
request = updateAccountWithNoAccount
9191
relays := []string{"relay1"}
@@ -99,9 +99,9 @@ func TestGetAccount(t *testing.T) {
9999
var count int
100100
require := require.New(t)
101101

102-
publicKey, privateKey, err := aliceKeys()
102+
keyPair, err := parseKeysFromMnemonicOrSeed(testMnemonic)
103103
require.NoError(err)
104-
account.PublicKey = base64.StdEncoding.EncodeToString(publicKey)
104+
account.PublicKey = base64.StdEncoding.EncodeToString(keyPair.Public())
105105

106106
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
107107
statusCode, body := serverHandler(r, request, count, require)
@@ -117,10 +117,10 @@ func TestGetAccount(t *testing.T) {
117117

118118
count = 0
119119
request = newClientWithAccountNoNode
120-
c, err := NewRegistrarClient(baseURL, privateKey)
120+
c, err := NewRegistrarClient(baseURL, testMnemonic)
121121
require.NoError(err)
122122
require.Equal(account.TwinID, c.twinID)
123-
require.Equal(publicKey, c.keyPair.publicKey)
123+
require.Equal(keyPair, c.keyPair)
124124

125125
t.Run("test get account with id account not found", func(t *testing.T) {
126126
request = getAccountWithIDStatusNotFount

0 commit comments

Comments
 (0)