Skip to content

Commit afb1e6c

Browse files
committed
feature: add bl command to output bip39 labelled words
1 parent c023ac2 commit afb1e6c

File tree

5 files changed

+129
-0
lines changed

5 files changed

+129
-0
lines changed

main.go

+30
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ var cli struct {
4343
BipVal BipValCmd `cmd name:"bv" help:"Validate a BIP39 mnemonic seed phrase"`
4444
BipSlip BipSlipCmd `cmd name:"bs" help:"Convert a BIP39 mnemonic seed to a set of SLIP39 shares"`
4545
BipEntropy BipEntropyCmd `cmd name:"be" help:"Convert a BIP39 mnemonic seed to a hex-encoded entropy string"`
46+
BipLabel BipLabelCmd `cmd name:"bl" help:"Convert a full set of BIP39 mnemonic shares to labelled word format"`
4647
SlipVal SlipValCmd `cmd name:"sv" help:"Validate a full set of SLIP39 mnemonic shares"`
4748
SlipBip SlipBipCmd `cmd name:"sb" help:"Convert a minimal set of SLIP39 mnemonic shares to a BIP39 mnemonic seed"`
4849
SlipLabel SlipLabelCmd `cmd name:"sl" help:"Convert a full set of SLIP39 mnemonic shares to labelled word format"`
@@ -81,6 +82,12 @@ type BipSlipCmd struct {
8182
Seed []string `arg help:"BIP39 mnemonic seed phrase" optional`
8283
}
8384

85+
type BipLabelCmd struct {
86+
Upper bool `flag short:"u" help:"output words in uppercase"`
87+
88+
Seed []string `arg help:"BIP39 mnemonic seed phrase" optional`
89+
}
90+
8491
type SlipValCmd struct {
8592
Passphrase string `flag short:"p" help:"passphrase used with the SLIP39 shares"`
8693
CheckFile string `flag short:"c" aliases:"cf" help:"check file with the source BIP39 mnemonic seed"`
@@ -242,6 +249,29 @@ func (cmd BipSlipCmd) Run(ctx *Context) error {
242249
return nil
243250
}
244251

252+
func (cmd BipLabelCmd) Run(ctx *Context) error {
253+
mnemonic, err := readSeedMnemonic(ctx, cmd.Seed)
254+
if err != nil {
255+
return err
256+
}
257+
258+
words := strings.Fields(mnemonic)
259+
if len(words) < 12 || len(words) > 24 || len(words)%3 != 0 {
260+
return fmt.Errorf("invalid BIP39 mnemonic seed length %d (must be 12-24 words, multiple of 3)",
261+
len(words))
262+
}
263+
264+
for i := range len(words) {
265+
word := words[i]
266+
if cmd.Upper {
267+
word = strings.ToUpper(word)
268+
}
269+
fmt.Fprintf(ctx.writer, "%02d %s\n", i+1, word)
270+
}
271+
272+
return nil
273+
}
274+
245275
func (cmd SlipValCmd) Run(ctx *Context) error {
246276
mnemonics, err := readShareMnemonics(ctx, cmd.Shares)
247277
if err != nil {

main_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,57 @@ func TestBipSlip(t *testing.T) {
295295
}
296296
}
297297

298+
// Test converting BIP-39 seeds to labelled words
299+
func TestBipLabel_Success(t *testing.T) {
300+
t.Parallel()
301+
302+
// Load all testdata `bipMs.txt` files (good seeds)
303+
tests := make(map[string]string)
304+
testfiles, err := filepath.Glob("testdata/bip?s.txt")
305+
if err != nil {
306+
t.Fatal(err)
307+
}
308+
for _, tf := range testfiles {
309+
data, err := ioutil.ReadFile(tf)
310+
if err != nil {
311+
t.Fatal(err)
312+
}
313+
tests[tf] = string(data)
314+
}
315+
316+
reTestfile := regexp.MustCompile(`^testdata/bip`)
317+
for tf, seeds := range tests {
318+
lf := reTestfile.ReplaceAllString(tf, "testdata/blabels")
319+
ldata, err := ioutil.ReadFile(lf)
320+
if err != nil {
321+
t.Fatal(err)
322+
}
323+
//t.Logf("checking tf %q vs lf %q", tf, lf)
324+
325+
// Convert bip seeds to labelled words
326+
buf1 := bytes.NewBufferString(seeds)
327+
var buf2 bytes.Buffer
328+
// Test with uppercase flag on
329+
cmd := BipLabelCmd{Upper: true}
330+
ctx := Context{
331+
reader: buf1,
332+
writer: &buf2,
333+
}
334+
335+
err = cmd.Run(&ctx)
336+
if err != nil {
337+
t.Errorf("BipLabel error on %q: %s", tf, err.Error())
338+
continue
339+
}
340+
341+
words := buf2.String()
342+
if words != string(ldata) {
343+
t.Errorf("test file %q mismatch - got:\n%sexpected:\n%s",
344+
tf, words, string(ldata))
345+
}
346+
}
347+
}
348+
298349
// Test validation of good SLIP-39 shares
299350
func TestSlipVal_Success(t *testing.T) {
300351
t.Parallel()

testdata/blabels1s.txt

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
01 ALL
2+
02 HOUR
3+
03 MAKE
4+
04 FIRST
5+
05 LEADER
6+
06 EXTEND
7+
07 HOLE
8+
08 ALIEN
9+
09 BEHIND
10+
10 GUARD
11+
11 GOSPEL
12+
12 LAVA
13+
13 PATH
14+
14 OUTPUT
15+
15 CENSUS
16+
16 MUSEUM
17+
17 JUNIOR
18+
18 MASS
19+
19 REOPEN
20+
20 FAMOUS
21+
21 SING
22+
22 ADVANCE
23+
23 SALT
24+
24 REFORM

testdata/blabels2s.txt

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
01 ABANDON
2+
02 ABANDON
3+
03 ABANDON
4+
04 ABANDON
5+
05 ABANDON
6+
06 ABANDON
7+
07 ABANDON
8+
08 ABANDON
9+
09 ABANDON
10+
10 ABANDON
11+
11 ABANDON
12+
12 ABOUT

testdata/blabels3s.txt

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
01 LEGAL
2+
02 WINNER
3+
03 THANK
4+
04 YEAR
5+
05 WAVE
6+
06 SAUSAGE
7+
07 WORTH
8+
08 USEFUL
9+
09 LEGAL
10+
10 WINNER
11+
11 THANK
12+
12 YELLOW

0 commit comments

Comments
 (0)