Skip to content

Commit

Permalink
feat: use seedutil to generate mnemonic
Browse files Browse the repository at this point in the history
This changes adds the functionality to seedutil to generate a random
24 word mnemonic, which then replaces lnd for the purposes of generating
the seed and mnemonic for a new xud node. This removes a dependency and
allows xud to create a node without the help of lnd.

Closes #1253.
  • Loading branch information
sangaman committed Jan 29, 2020
1 parent 81a8e65 commit 663e4be
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 31 deletions.
4 changes: 2 additions & 2 deletions lib/service/InitService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { promises as fs } from 'fs';
import NodeKey from '../nodekey/NodeKey';
import swapErrors from '../swaps/errors';
import SwapClientManager from '../swaps/SwapClientManager';
import { decipher } from '../utils/seedutil';
import { decipher, generate } from '../utils/seedutil';
import errors from './errors';

interface InitService {
Expand Down Expand Up @@ -32,7 +32,7 @@ class InitService extends EventEmitter {
await this.prepareCall();

try {
const seedMnemonic = await this.swapClientManager.genSeed();
const seedMnemonic = await generate();

// we use the deciphered seed (without the salt and extra fields that make up the enciphered seed)
// to generate an xud nodekey from the same seed used for wallets
Expand Down
24 changes: 1 addition & 23 deletions lib/swaps/SwapClientManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { promises as fs } from 'fs';
import { EventEmitter } from 'events';
import { promises as fs } from 'fs';
import Config from '../Config';
import { SwapClientType } from '../constants/enums';
import { Models } from '../db/DB';
Expand Down Expand Up @@ -151,28 +151,6 @@ class SwapClientManager extends EventEmitter {
}
}

/**
* Generates a cryptographically random 24 word seed mnemonic from an lnd client.
*/
public genSeed = async () => {
const lndClients = this.getLndClientsMap().values();
// loop through swap clients until we find an lnd client awaiting unlock
for (const lndClient of lndClients) {
if (lndClient.isWaitingUnlock()) {
try {
const seed = await lndClient.genSeed();
return seed;
} catch (err) {
lndClient.logger.error('could not generate seed', err);
}
}
}

// TODO: use seedutil tool to generate a seed instead of throwing error
// when we can't generate one with lnd
throw errors.SWAP_CLIENT_WALLET_NOT_CREATED('could not generate aezeed');
}

/**
* Initializes wallets with seed and password.
*/
Expand Down
16 changes: 15 additions & 1 deletion lib/utils/seedutil.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import assert from 'assert';
import { exec as childProcessExec } from 'child_process';
import { promisify } from 'util';

Expand Down Expand Up @@ -49,4 +50,17 @@ async function decipher(mnemonic: string[]) {
return Buffer.from(decipheredSeed, 'hex');
}

export { keystore, encipher, decipher };
async function generate() {
const { stdout, stderr } = await exec('./seedutil/seedutil generate');

if (stderr) {
throw new Error(stderr);
}

const mnemonic = stdout.trim().split(' ');
assert.equal(mnemonic.length, 24, 'seedutil did not generate mnemonic of exactly 24 words');
return mnemonic;
}

export { keystore, encipher, decipher, generate };

11 changes: 11 additions & 0 deletions seedutil/SeedUtil.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ describe('SeedUtil decipher', () => {
});
});

describe('SeedUtil generate', () => {
test('it prints a 24 word mnemonic which can be deciphered', async () => {
const cmd = './seedutil/seedutil generate';
const mnemonic = await executeCommand(cmd);
expect(mnemonic.split(' ')).toHaveLength(24);

const cmd2 = `./seedutil/seedutil decipher ${mnemonic}`;
await executeCommand(cmd2);
});
});

describe('SeedUtil keystore', () => {
beforeEach(async () => {
await deleteDir(DEFAULT_KEYSTORE_PATH);
Expand Down
30 changes: 25 additions & 5 deletions seedutil/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"time"

"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/crypto"
Expand All @@ -26,7 +28,7 @@ var (
defaultKeyStorePath = filepath.Join(filepath.Dir(os.Args[0]))
)

func parseMnemonic(words []string) aezeed.Mnemonic{
func parseMnemonic(words []string) aezeed.Mnemonic {
if len(words) < aezeed.NummnemonicWords {
fmt.Fprintf(os.Stderr, "\nerror: expecting %v-word mnemonic seed separated by spaces\n", aezeed.NummnemonicWords)
os.Exit(1)
Expand Down Expand Up @@ -54,7 +56,8 @@ func mnemonicToCipherSeed(mnemonic aezeed.Mnemonic, aezeedPassphrase *string) *a
func main() {
keystoreCommand := flag.NewFlagSet("keystore", flag.ExitOnError)
encipherCommand := flag.NewFlagSet("encipher", flag.ExitOnError)
mnemonicCommand := flag.NewFlagSet("mnemonic", flag.ExitOnError)
decipherCommand := flag.NewFlagSet("decipher", flag.ExitOnError)
generateCommand := flag.NewFlagSet("generate", flag.ExitOnError)

if len(os.Args) < 2 {
fmt.Println("subcommand is required")
Expand Down Expand Up @@ -117,9 +120,9 @@ func main() {
encipheredSeed, _ := cipherSeed.Encipher([]byte(*aezeedPassphrase))
fmt.Println(hex.EncodeToString(encipheredSeed[:]))
case "decipher":
aezeedPassphrase := mnemonicCommand.String("aezeedpass", defaultAezeedPassphrase, "aezeed passphrase")
mnemonicCommand.Parse(os.Args[2:])
args = mnemonicCommand.Args()
aezeedPassphrase := decipherCommand.String("aezeedpass", defaultAezeedPassphrase, "aezeed passphrase")
decipherCommand.Parse(os.Args[2:])
args = decipherCommand.Args()

mnemonic := parseMnemonic(args)
decipheredSeed, err := mnemonic.Decipher([]byte(*aezeedPassphrase))
Expand All @@ -129,6 +132,23 @@ func main() {
}

fmt.Println(hex.EncodeToString(decipheredSeed[:]))
case "generate":
aezeedPassphrase := generateCommand.String("aezeedpass", defaultAezeedPassphrase, "aezeed passphrase")
generateCommand.Parse(os.Args[2:])

cipherSeed, err := aezeed.New(0, nil, time.Now())
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

mnemonic, err := cipherSeed.ToMnemonic([]byte(*aezeedPassphrase))
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}

fmt.Println(strings.Join([]string(mnemonic[:]), " "))
default:
flag.PrintDefaults()
os.Exit(1)
Expand Down

0 comments on commit 663e4be

Please sign in to comment.