hd_test.go raw

   1  package hd
   2  
   3  import (
   4  	"bytes"
   5  	"encoding/hex"
   6  	"testing"
   7  )
   8  
   9  // seed is the BIP32 test vector 1 seed.
  10  var seed = mustHex("000102030405060708090a0b0c0d0e0f")
  11  
  12  // mustHex decodes a hex string or fails the test.
  13  func mustHex(text string) []byte {
  14  	out, err := hex.DecodeString(text)
  15  
  16  	if err != nil {
  17  		panic(err)
  18  	}
  19  
  20  	return out
  21  }
  22  
  23  // TestMasterIsDeterministic: the same seed is the same tree, which is the whole promise of
  24  // a backup.
  25  func TestMasterIsDeterministic(t *testing.T) {
  26  	first, err := NewMaster(seed)
  27  
  28  	if err != nil {
  29  		t.Fatal(err)
  30  	}
  31  
  32  	second, err := NewMaster(seed)
  33  
  34  	if err != nil {
  35  		t.Fatal(err)
  36  	}
  37  
  38  	if !bytes.Equal(first.Private[:], second.Private[:]) ||
  39  		!bytes.Equal(first.ChainCode[:], second.ChainCode[:]) {
  40  		t.Fatal("the same seed gave two master keys")
  41  	}
  42  
  43  	if first.Depth != 0 || len(first.Path) != 0 {
  44  		t.Fatalf("the master key is at depth %d, path %v", first.Depth, first.Path)
  45  	}
  46  
  47  	// A different seed is a different tree.
  48  	other, err := NewMaster(mustHex("0f0e0d0c0b0a09080706050403020100"))
  49  
  50  	if err != nil {
  51  		t.Fatal(err)
  52  	}
  53  
  54  	if bytes.Equal(first.Private[:], other.Private[:]) {
  55  		t.Fatal("two different seeds gave the same master key")
  56  	}
  57  }
  58  
  59  // TestHardenedAndNormalDiffer: the same index on the two branches is two keys, which is
  60  // what a hardened level buys.
  61  func TestHardenedAndNormalDiffer(t *testing.T) {
  62  	master, err := NewMaster(seed)
  63  
  64  	if err != nil {
  65  		t.Fatal(err)
  66  	}
  67  
  68  	normal, err := master.ChildKey(0)
  69  
  70  	if err != nil {
  71  		t.Fatal(err)
  72  	}
  73  
  74  	hardened, err := master.ChildKey(Hardened)
  75  
  76  	if err != nil {
  77  		t.Fatal(err)
  78  	}
  79  
  80  	if bytes.Equal(normal.Private[:], hardened.Private[:]) {
  81  		t.Fatal("the normal and hardened children of one index are the same key")
  82  	}
  83  
  84  	if normal.Depth != 1 || hardened.Depth != 1 {
  85  		t.Fatalf("depths are %d and %d", normal.Depth, hardened.Depth)
  86  	}
  87  }
  88  
  89  // TestPathIsCarried: a key says where it came from, because a wallet's address is recorded
  90  // as a path.
  91  func TestPathIsCarried(t *testing.T) {
  92  	master, err := NewMaster(seed)
  93  
  94  	if err != nil {
  95  		t.Fatal(err)
  96  	}
  97  
  98  	path, err := ParsePath("m/84'/0'/0'/1/7")
  99  
 100  	if err != nil {
 101  		t.Fatal(err)
 102  	}
 103  
 104  	key, err := master.Derive(path)
 105  
 106  	if err != nil {
 107  		t.Fatal(err)
 108  	}
 109  
 110  	if got := key.ChildPath(); got != "m/84'/0'/0'/1/7" {
 111  		t.Fatalf("the key's path is %q", got)
 112  	}
 113  
 114  	if key.Depth != 5 {
 115  		t.Fatalf("the depth is %d", key.Depth)
 116  	}
 117  
 118  	// The same path twice is the same key: derivation is a function, not a walk.
 119  	again, err := master.Derive(path)
 120  
 121  	if err != nil {
 122  		t.Fatal(err)
 123  	}
 124  
 125  	if !bytes.Equal(key.Private[:], again.Private[:]) {
 126  		t.Fatal("the same path gave two keys")
 127  	}
 128  }
 129  
 130  // TestParsePath: the notation a person copies, and what it refuses.
 131  func TestParsePath(t *testing.T) {
 132  	good := map[string]string{
 133  		"m/84'/0'/0'/0/0": "m/84'/0'/0'/0/0",
 134  		"m/84h/0h/0h/0/0": "m/84'/0'/0'/0/0",
 135  		"m":               "m",
 136  		" m/0 ":           "m/0",
 137  	}
 138  
 139  	for input, want := range good {
 140  		path, err := ParsePath(input)
 141  
 142  		if err != nil {
 143  			t.Fatalf("%q: %v", input, err)
 144  		}
 145  
 146  		if got := path.String(); got != want {
 147  			t.Fatalf("%q parsed to %q, want %q", input, got, want)
 148  		}
 149  	}
 150  
 151  	for _, bad := range []string{
 152  		"", "84'/0'", "m//0", "m/x", "m/84''", "m/2147483648", "/0", "m/-1",
 153  	} {
 154  		if _, err := ParsePath(bad); err == nil {
 155  			t.Errorf("%q was accepted as a path", bad)
 156  		}
 157  	}
 158  }
 159  
 160  // TestPublicIsStable: the same private key is the same public key, and a child's is not
 161  // its parent's.
 162  func TestPublicIsStable(t *testing.T) {
 163  	master, err := NewMaster(seed)
 164  
 165  	if err != nil {
 166  		t.Fatal(err)
 167  	}
 168  
 169  	first := master.Public()
 170  	second := master.Public()
 171  
 172  	if !bytes.Equal(first, second) || len(first) != 33 {
 173  		t.Fatalf("the public key is %d bytes and %v", len(first),
 174  			bytes.Equal(first, second))
 175  	}
 176  
 177  	child, err := master.ChildKey(Hardened)
 178  
 179  	if err != nil {
 180  		t.Fatal(err)
 181  	}
 182  
 183  	if bytes.Equal(child.Public(), first) {
 184  		t.Fatal("a child has its parent's public key")
 185  	}
 186  
 187  	// A public key is a compressed point, so its first byte is a parity marker.
 188  	if first[0] != 0x02 && first[0] != 0x03 {
 189  		t.Fatalf("the public key starts with %#x", first[0])
 190  	}
 191  }
 192  
 193  // TestSeedLength: BIP32 defines 16 to 64 bytes.
 194  func TestSeedLength(t *testing.T) {
 195  	for _, size := range []int{16, 32, 64} {
 196  		if _, err := NewMaster(make([]byte, size)); err != nil {
 197  			t.Errorf("%d bytes: %v", size, err)
 198  		}
 199  	}
 200  
 201  	for _, size := range []int{0, 15, 65} {
 202  		if _, err := NewMaster(make([]byte, size)); err == nil {
 203  			t.Errorf("%d bytes was accepted", size)
 204  		}
 205  	}
 206  
 207  	// An all-zero seed is still a seed: it is a bad one for a person to pick, but it is
 208  	// representable, and refusing it would refuse a valid derivation.
 209  	if _, err := NewMaster(make([]byte, 32)); err != nil {
 210  		t.Fatalf("the zero seed was refused: %v", err)
 211  	}
 212  }
 213