Skip to content

Commit f06463f

Browse files
authored
Merge pull request #9 from threefoldtech/development_mnemonics_registrar
Development support mnemonics in registrar client
2 parents 84ee6df + 94ac63d commit f06463f

24 files changed

+417
-109
lines changed

packages/registrar_client/README.md

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,37 @@ This package provides a client for interacting with the TFGrid v4 Node Registrar
3535

3636
## Getting Started
3737

38-
To initialize the Registrar Client, you need to provide the base url of registrar and Base64-encoded, 64-byte raw Ed25519 private key (nacl format).
38+
To initialize the Registrar Client, you need to provide the base URL of the registrar and either your mnemonic phrase or a 64-character hex seed. You also need to specify the keypair type.
3939

40-
To generate a 64-byte ed25519 private key, you can use tweetnacl library to generate key:
40+
The supported keypair types are:
41+
42+
- `ed25519`
43+
- `sr25519` (default)
44+
45+
Here's how to initialize the client:
4146

4247
```typescript
43-
import nacl from "tweetnacl";
44-
import base64 from "base64-js";
48+
const client = new RegistrarClient({
49+
baseUrl: "https://registrar.dev4.grid.tf/v1",
50+
mnemonicOrSeed: "your_mnemonic_or_seed",
51+
keypairType: "sr25519", // Optional, defaults to "sr25519"
52+
});
53+
```
4554

46-
const keyPair = nacl.sign.keyPair();
47-
const privateKey = base64.fromByteArray(keyPair.secretKey);
55+
To generate 64 character hex seed:
4856

49-
console.log("Your 64-byte ed25519 private key:", privateKey);
57+
```bash
58+
openssl rand -hex 32
5059
```
5160

5261
Here is an example:
5362

5463
```typescript
55-
const client = new RegistrarClient({ baseURl: "https://registrar.dev4.grid.tf/v1", privateKey: your_private_key });
64+
const client = new RegistrarClient({
65+
baseURl: "https://registrar.dev4.grid.tf/v1",
66+
mnemonicOrSeed: "your_mnemonic_or_seed",
67+
keypairType: "ed21559"
68+
});
5669
```
5770

5871
To be able to create a farm you need to have a Stellar wallet and provide your Stellar address. For more details on how to create a Stellar wallet and generate a Stellar address, please refer to the [Stellar Account Viewer](https://www.stellar.org/account-viewer/#!/) or the [Stellar Documentation](https://developers.stellar.org/docs/tutorials/create-account/).
@@ -62,7 +75,7 @@ To be able to create a farm you need to have a Stellar wallet and provide your S
6275
Here is an example of how to use the Registrar Client:
6376

6477
```typescript
65-
const client = new RegistrarClient({ baseUrl: URl, privateKey: your_private_key });
78+
const client = new RegistrarClient({ baseUrl: URl, mnemonicOrSeed: your_mnemonic_or_seed, keypairType: "ed21559" });
6679

6780
// Example: Create an account
6881
const accountRequest: CreateAccountRequest = {

packages/registrar_client/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,10 @@
5454
"typescript": "^5.7.3"
5555
},
5656
"dependencies": {
57+
"@polkadot/keyring": "^13.4.4",
5758
"@stellar/stellar-base": "^12.1.1",
5859
"@types/jest": "^29.5.14",
60+
"bip39": "^3.1.0",
5961
"dotenv": "^16.4.7",
6062
"jest": "^29.7.0",
6163
"ts-jest": "^29.2.5",

packages/registrar_client/scripts/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ yarn install
1616

1717
## Getting Started
1818

19-
- Set base URL, your private key, and Stellar address (if needed) in `scripts/config.json`.
19+
- Set base URL, your mnemonic or seed, keypair type and Stellar address (if needed) in `scripts/config.json`.
2020

2121
## Usage
2222

2323
To run any of the scripts, use the following command format:
2424

2525
```bash
26-
yarn ts-node --project tsconfig-node.json scripts/<script_name>.ts
26+
yarn ts-node --project tsconfig-node.json scripts/<script_name>.ts
2727
```
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"baseUrl": "http://localhost:8080/v1", "privateKey": "", "stellarAddress": ""}
1+
{"baseUrl": "https://registrar.dev4.grid.tf/v1", "mnemonicOrSeed": "", "stellarAddress": "", "keypairType": "ed25519"}

packages/registrar_client/scripts/create_account.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { log } from "console";
22
import { RegistrarClient } from "../src/";
33
import config from "./config.json";
4+
import { KeypairType } from "@polkadot/util-crypto/types";
45

56
async function createAccount(client: RegistrarClient) {
67
const account = await client.accounts.createAccount({});
@@ -10,7 +11,7 @@ async function createAccount(client: RegistrarClient) {
1011
}
1112

1213
async function main() {
13-
const client = new RegistrarClient({ baseURL: config.baseUrl, privateKey: config.privateKey });
14+
const client = new RegistrarClient({ baseURL: config.baseUrl, mnemonicOrSeed: config.mnemonicOrSeed, keypairType: config.keypairType as KeypairType });
1415
await createAccount(client);
1516
}
1617

packages/registrar_client/scripts/create_farm.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { log } from "console";
22
import { Account, RegistrarClient } from "../src/";
33
import config from "./config.json";
4-
import * as tweetnacl from "tweetnacl";
54
import * as base64 from "base64-js";
5+
import { deriveKeyPair } from "../src/utils";
6+
import { KeypairType } from "@polkadot/util-crypto/types";
67

78
async function getAccount(client: RegistrarClient): Promise<Account> {
8-
const publicKey = tweetnacl.sign.keyPair.fromSecretKey(base64.toByteArray(config.privateKey)).publicKey;
9-
const account = await client.accounts.getAccountByPublicKey(base64.fromByteArray(publicKey));
9+
const keyPair = await deriveKeyPair(config.mnemonicOrSeed, config.keypairType as KeypairType);
10+
const account = await client.accounts.getAccountByPublicKey(base64.fromByteArray(keyPair.publicKey));
1011
log("================= Getting Account =================");
1112
log(account);
1213
log("================= Getting Account =================");
@@ -29,7 +30,7 @@ async function getFarm(client: RegistrarClient, farmID: number) {
2930
}
3031

3132
async function main() {
32-
const client = new RegistrarClient({ baseURL: config.baseUrl, privateKey: config.privateKey });
33+
const client = new RegistrarClient({ baseURL: config.baseUrl, mnemonicOrSeed: config.mnemonicOrSeed });
3334
const account = await getAccount(client);
3435
const twinID = account.twin_id;
3536
const farmID = await createFarm(client, twinID, config.stellarAddress);

packages/registrar_client/scripts/create_node.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { log } from "console";
22
import { Account, RegistrarClient, NodeRegistrationRequest } from "../src/";
33
import config from "./config.json";
4-
import * as tweetnacl from "tweetnacl";
54
import * as base64 from "base64-js";
5+
import { deriveKeyPair } from "../src/utils";
6+
import { KeypairType } from "@polkadot/util-crypto/types";
67

78
async function getAccount(client: RegistrarClient): Promise<Account> {
8-
const publicKey = tweetnacl.sign.keyPair.fromSecretKey(base64.toByteArray(config.privateKey)).publicKey;
9-
const account = await client.accounts.getAccountByPublicKey(base64.fromByteArray(publicKey));
9+
const keyPair = await deriveKeyPair(config.mnemonicOrSeed, config.keypairType as KeypairType);
10+
const account = await client.accounts.getAccountByPublicKey(base64.fromByteArray(keyPair.publicKey));
1011
log("================= Getting Account =================");
1112
log(account);
1213
log("================= Getting Account =================");
@@ -29,7 +30,7 @@ async function getNode(client: RegistrarClient, nodeID: number) {
2930
}
3031

3132
async function main() {
32-
const client = new RegistrarClient({ baseURL: config.baseUrl, privateKey: config.privateKey });
33+
const client = new RegistrarClient({ baseURL: config.baseUrl, mnemonicOrSeed: config.mnemonicOrSeed, keypairType: config.keypairType as KeypairType });
3334
const account = await getAccount(client);
3435
const twinID = account.twin_id;
3536
const node: NodeRegistrationRequest = {

packages/registrar_client/scripts/list_farms.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ async function listFarms(client: RegistrarClient, filter: NodesFilter) {
99
}
1010

1111
async function main() {
12-
const client = new RegistrarClient({ baseURL: config.baseUrl, privateKey: config.privateKey });
12+
const client = new RegistrarClient({ baseURL: config.baseUrl, mnemonicOrSeed: config.mnemonicOrSeed });
1313
await listFarms(client, {});
1414
}
1515

packages/registrar_client/scripts/list_nodes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ async function listNodes(client: RegistrarClient, filter: NodesFilter) {
99
}
1010

1111
async function main() {
12-
const client = new RegistrarClient({ baseURL: config.baseUrl, privateKey: config.privateKey });
12+
const client = new RegistrarClient({ baseURL: config.baseUrl, mnemonicOrSeed: config.mnemonicOrSeed });
1313
const filter: NodesFilter = {
1414
farm_id: 70,
1515
};

packages/registrar_client/scripts/update_account.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { log } from "console";
22
import { RegistrarClient, UpdateAccountRequest } from "../src/";
33
import config from "./config.json";
4+
import { KeypairType } from "@polkadot/util-crypto/types";
45

56
async function updateAccount(client: RegistrarClient, twinID: number, update: UpdateAccountRequest) {
67
const account = await client.accounts.updateAccount(twinID, update);
@@ -17,7 +18,7 @@ async function getAccount(client: RegistrarClient, twinID: number) {
1718
}
1819

1920
async function main() {
20-
const client = new RegistrarClient({ baseURL: config.baseUrl, privateKey: config.privateKey });
21+
const client = new RegistrarClient({ baseURL: config.baseUrl, mnemonicOrSeed: config.mnemonicOrSeed, keypairType: config.keypairType as KeypairType });
2122
const update: UpdateAccountRequest = {
2223
relays: ["relay1", "relay2"],
2324
rmb_enc_key: "rmb_enc_key",

packages/registrar_client/scripts/update_farm.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { log } from "console";
22
import { RegistrarClient } from "../src/";
33
import config from "./config.json";
4+
import { KeypairType } from "@polkadot/util-crypto/types";
45

56
async function updateFarm(client: RegistrarClient, twinID: number, farmID: number, farmName: string) {
67
const farm = await client.farms.updateFarm(farmID, twinID, farmName, config.stellarAddress);
@@ -17,7 +18,7 @@ async function getFarm(client: RegistrarClient, farmID: number) {
1718
}
1819

1920
async function main() {
20-
const client = new RegistrarClient({ baseURL: config.baseUrl, privateKey: config.privateKey });
21+
const client = new RegistrarClient({ baseURL: config.baseUrl, mnemonicOrSeed: config.mnemonicOrSeed, keypairType: config.keypairType as KeypairType });
2122
const twinID = 143;
2223
const farmID = 46;
2324
const farmName = "testfarm";

packages/registrar_client/scripts/update_node.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { log } from "console";
22
import { UpdateNodeRequest, RegistrarClient } from "../src/";
33
import config from "./config.json";
4+
import { KeypairType } from "@polkadot/util-crypto/types";
45

56
async function updateNode(client: RegistrarClient, twinID: number, nodeID: number, update: UpdateNodeRequest) {
67
const account = await client.nodes.updateNode(nodeID, twinID, update);
@@ -17,7 +18,7 @@ async function getNode(client: RegistrarClient, nodeID: number) {
1718
}
1819

1920
async function main() {
20-
const client = new RegistrarClient({ baseURL: config.baseUrl, privateKey: config.privateKey });
21+
const client = new RegistrarClient({ baseURL: config.baseUrl, mnemonicOrSeed: config.mnemonicOrSeed, keypairType: config.keypairType as KeypairType });
2122
const update: UpdateNodeRequest = {
2223
farm_id: 46,
2324
interfaces: [

packages/registrar_client/src/client/client.ts

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { Accounts } from "../modules/accounts";
33
import { Farms } from "../modules/farms";
44
import { Nodes } from "../modules/nodes";
55
import { Zos } from "../modules/zos";
6-
6+
import { validateMnemonic } from "bip39";
7+
import { KeypairType } from "@polkadot/util-crypto/types";
8+
import { SUPPORTED_KEYPAIR_TYPES } from "../utils";
79
export abstract class BaseClient {
810
private client: AxiosInstance;
911

@@ -35,25 +37,46 @@ export abstract class BaseClient {
3537
}
3638
interface Config {
3739
baseURL: string;
38-
privateKey: string;
40+
mnemonicOrSeed: string;
41+
keypairType?: KeypairType;
3942
}
4043

4144
export class RegistrarClient extends BaseClient {
42-
public readonly privateKey: string;
45+
public readonly mnemonicOrSeed: string;
46+
public readonly keypairType: KeypairType;
4347
accounts: Accounts;
4448
farms: Farms;
4549
nodes: Nodes;
4650
zos: Zos;
4751

48-
constructor({ baseURL, privateKey }: Config) {
52+
_validateSeed(seed: string): string {
53+
if (!seed.startsWith("0x")) {
54+
seed = `0x${seed}`;
55+
}
56+
if (!seed.match(/^0x[a-fA-F0-9]{64}$/)) {
57+
return "";
58+
}
59+
return seed;
60+
}
61+
62+
constructor({ baseURL, mnemonicOrSeed, keypairType = "sr25519" }: Config) {
63+
if (!SUPPORTED_KEYPAIR_TYPES.includes(keypairType)) {
64+
throw new Error(`Unsupported keypair type: ${keypairType}`);
65+
}
4966
if (!baseURL) {
5067
throw new Error("Base URL is required");
5168
}
52-
if (!privateKey) {
53-
throw new Error("Private key is required");
54-
}
5569
super(baseURL);
56-
this.privateKey = privateKey;
70+
71+
if (!validateMnemonic(mnemonicOrSeed)) {
72+
mnemonicOrSeed = this._validateSeed(mnemonicOrSeed);
73+
}
74+
if (!mnemonicOrSeed) {
75+
throw new Error("Invalid mnemonic or seed");
76+
}
77+
78+
this.mnemonicOrSeed = mnemonicOrSeed;
79+
this.keypairType = keypairType;
5780
this.accounts = new Accounts(this);
5881
this.farms = new Farms(this);
5982
this.nodes = new Nodes(this);

packages/registrar_client/src/modules/accounts.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { RegistrarClient } from "../client/client";
22
import { Account, CreateAccountRequest, UpdateAccountRequest } from "../types/account";
3-
import * as tweetnacl from "tweetnacl";
4-
import * as base64 from "base64-js";
53
import { createSignatureWithPublicKey, createAuthHeader } from "../utils";
64

75
export class Accounts {
@@ -14,11 +12,7 @@ export class Accounts {
1412
}
1513

1614
async createAccount(request: Partial<CreateAccountRequest>): Promise<Account | null> {
17-
const privateKey = this.client.privateKey;
18-
const keyPair = tweetnacl.sign.keyPair.fromSecretKey(base64.toByteArray(privateKey));
19-
20-
const publicKey = base64.fromByteArray(keyPair.publicKey);
21-
const { signature, timestamp } = createSignatureWithPublicKey(publicKey, privateKey);
15+
const { signature, publicKey, timestamp } = await createSignatureWithPublicKey(this.client.mnemonicOrSeed, this.client.keypairType);
2216

2317
request.public_key = publicKey;
2418
request.signature = signature;
@@ -59,7 +53,7 @@ export class Accounts {
5953

6054
async updateAccount(twinID: number, body: UpdateAccountRequest): Promise<any> {
6155
try {
62-
const headers = createAuthHeader(twinID, this.client.privateKey);
56+
const headers = await createAuthHeader(twinID, this.client.mnemonicOrSeed, this.client.keypairType);
6357

6458
const data = await this.client.patch<any>(`${this.accountUri}/${twinID}`, body, { headers });
6559
return data;

packages/registrar_client/src/modules/farms.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class Farms {
5252
}
5353

5454
const farm = { farm_name: farmName, dedicated, twin_id: twinID, stellar_address: stellarAddress };
55-
const headers = createAuthHeader(twinID, this.client.privateKey);
55+
const headers = await createAuthHeader(twinID, this.client.mnemonicOrSeed, this.client.keypairType);
5656
try {
5757
const data = await this.client.post<FarmCreationResponse>(`${this.farmUri}/`, farm, { headers });
5858
return data;
@@ -94,7 +94,7 @@ export class Farms {
9494
throw new Error("Invalid stellar address");
9595
}
9696

97-
const headers = createAuthHeader(twinID, this.client.privateKey);
97+
const headers = await createAuthHeader(twinID, this.client.mnemonicOrSeed, this.client.keypairType);
9898
const farm : FarmUpdateRequest = { farm_name: name, stellar_address: stellarAddress };
9999
try {
100100
const data = await this.client.patch<any>(`${this.farmUri}/${farmID}`,farm , { headers });

packages/registrar_client/src/modules/nodes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class Nodes {
1919

2020
async registerNode(node: NodeRegistrationRequest): Promise<NodeRegistrationResponse> {
2121
this._validateNodeData(node);
22-
const headers = createAuthHeader(node.twin_id, this.client.privateKey);
22+
const headers = await createAuthHeader(node.twin_id, this.client.mnemonicOrSeed, this.client.keypairType);
2323
try {
2424
const data = await this.client.post<NodeRegistrationResponse>(`${this.nodeUri}/`, node, { headers });
2525
return data;
@@ -50,7 +50,7 @@ export class Nodes {
5050

5151
async updateNode(nodeID: number, twinID: number, node: UpdateNodeRequest): Promise<any> {
5252
this._validateNodeData(node);
53-
const headers = createAuthHeader(twinID, this.client.privateKey);
53+
const headers = await createAuthHeader(twinID, this.client.mnemonicOrSeed, this.client.keypairType);
5454
try {
5555
const data = await this.client.patch<Node>(`${this.nodeUri}/${nodeID}`, node, { headers });
5656
return data;
@@ -60,7 +60,7 @@ export class Nodes {
6060
}
6161

6262
async reportNodeUptime(nodeID: number, twinID: number, uptime: UptimeReportRequest): Promise<any> {
63-
const headers = createAuthHeader(twinID, this.client.privateKey);
63+
const headers = await createAuthHeader(twinID, this.client.mnemonicOrSeed, this.client.keypairType);
6464
try {
6565
const data = await this.client.post<any>(`${this.nodeUri}/${nodeID}/uptime`, uptime, { headers });
6666
return data;

0 commit comments

Comments
 (0)