decred.org/dcrwallet/v3@v3.1.0/wallet/udb/cointype_test.go (about) 1 // Copyright (c) 2017 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package udb 6 7 import ( 8 "context" 9 "testing" 10 11 "decred.org/dcrwallet/v3/errors" 12 "decred.org/dcrwallet/v3/internal/compat" 13 "decred.org/dcrwallet/v3/wallet/walletdb" 14 "github.com/decred/dcrd/chaincfg/v3" 15 "github.com/decred/dcrd/hdkeychain/v3" 16 "github.com/decred/dcrd/txscript/v4/stdaddr" 17 ) 18 19 func TestCoinTypes(t *testing.T) { 20 t.Parallel() 21 22 tests := []struct { 23 params *chaincfg.Params 24 legacyCoinType, slip0044CoinType uint32 25 }{ 26 {chaincfg.MainNetParams(), 20, 42}, 27 {chaincfg.TestNet3Params(), 11, 1}, 28 {chaincfg.SimNetParams(), 115, 1}, 29 } 30 for _, test := range tests { 31 legacyCoinType, slip0044CoinType := CoinTypes(test.params) 32 if legacyCoinType != test.legacyCoinType { 33 t.Errorf("%s: got legacy coin type %d, expected %d", test.params.Name, 34 legacyCoinType, test.legacyCoinType) 35 } 36 if slip0044CoinType != test.slip0044CoinType { 37 t.Errorf("%s: got SLIP0044 coin type %d, expected %d", test.params.Name, 38 slip0044CoinType, test.slip0044CoinType) 39 } 40 } 41 } 42 43 func deriveChildAddress(accountExtKey *hdkeychain.ExtendedKey, branch, child uint32, params *chaincfg.Params) (stdaddr.Address, error) { 44 branchKey, err := accountExtKey.Child(branch) 45 if err != nil { 46 return nil, err 47 } 48 addressKey, err := branchKey.Child(child) 49 if err != nil { 50 return nil, err 51 } 52 return compat.HD2Address(addressKey, params) 53 } 54 55 func equalExtKeys(k0, k1 *hdkeychain.ExtendedKey) bool { 56 return k0.String() == k1.String() 57 } 58 59 func TestCoinTypeUpgrade(t *testing.T) { 60 t.Parallel() 61 62 ctx := context.Background() 63 db, teardown := tempDB(t) 64 defer teardown() 65 66 params := chaincfg.TestNet3Params() 67 68 err := Initialize(ctx, db, params, seed, pubPass, privPassphrase) 69 if err != nil { 70 t.Fatal(err) 71 } 72 73 m, _, _, err := Open(ctx, db, params, pubPass) 74 if err != nil { 75 t.Fatal(err) 76 } 77 78 legacyCoinType, slip0044CoinType := CoinTypes(params) 79 80 masterExtKey, err := hdkeychain.NewMaster(seed, params) 81 if err != nil { 82 t.Fatal(err) 83 } 84 legacyCoinTypeExtKey, err := deriveCoinTypeKey(masterExtKey, legacyCoinType) 85 if err != nil { 86 t.Fatal(err) 87 } 88 slip0044CoinTypeExtKey, err := deriveCoinTypeKey(masterExtKey, slip0044CoinType) 89 if err != nil { 90 t.Fatal(err) 91 } 92 slip0044Account0ExtKey, err := deriveAccountKey(slip0044CoinTypeExtKey, 0) 93 if err != nil { 94 t.Fatal(err) 95 } 96 slip0044Account0ExtKey = slip0044Account0ExtKey.Neuter() 97 slip0044Account1ExtKey, err := deriveAccountKey(slip0044CoinTypeExtKey, 1) 98 if err != nil { 99 t.Fatal(err) 100 } 101 slip0044Account1ExtKey = slip0044Account1ExtKey.Neuter() 102 slip0044Account0Address0, err := deriveChildAddress(slip0044Account0ExtKey, 0, 0, params) 103 if err != nil { 104 t.Fatal(err) 105 } 106 slip0044Account1Address0, err := deriveChildAddress(slip0044Account1ExtKey, 0, 0, params) 107 if err != nil { 108 t.Fatal(err) 109 } 110 slip0044Account0Address0Hash160 := slip0044Account0Address0.(stdaddr.Hash160er).Hash160() 111 slip0044Account1Address0Hash160 := slip0044Account1Address0.(stdaddr.Hash160er).Hash160() 112 113 err = walletdb.Update(ctx, db, func(dbtx walletdb.ReadWriteTx) error { 114 ns := dbtx.ReadWriteBucket(waddrmgrBucketKey) 115 err := m.Unlock(ns, privPassphrase) 116 if err != nil { 117 t.Fatal(err) 118 } 119 120 // Check reported initial coin type and compare the key itself against 121 // the expected value. 122 coinType, err := m.CoinType(dbtx) 123 if err != nil { 124 t.Fatal(err) 125 } 126 if coinType != legacyCoinType { 127 t.Fatalf("initialized database has wrong coin type %d", coinType) 128 } 129 coinTypeExtKey, err := m.CoinTypePrivKey(dbtx) 130 if err != nil { 131 t.Fatal(err) 132 } 133 if !equalExtKeys(coinTypeExtKey, legacyCoinTypeExtKey) { 134 t.Fatalf("initialized database has wrong coin type key") 135 } 136 137 // Perform the upgrade 138 err = m.UpgradeToSLIP0044CoinType(dbtx) 139 if err != nil { 140 t.Fatal(err) 141 } 142 143 // Check upgraded coin type and keys. 144 coinType, err = m.CoinType(dbtx) 145 if err != nil { 146 t.Fatal(err) 147 } 148 if coinType != slip0044CoinType { 149 t.Fatalf("upgraded database has wrong coin type %d", coinType) 150 } 151 coinTypeExtKey, err = m.CoinTypePrivKey(dbtx) 152 if err != nil { 153 t.Fatal(err) 154 } 155 if !equalExtKeys(coinTypeExtKey, slip0044CoinTypeExtKey) { 156 t.Fatalf("upgraded database has wrong coin type key") 157 } 158 159 // Check the account 0 xpub matches the one derived from the SLIP0044 160 // coin type. 161 accountExtKey, err := m.AccountExtendedPubKey(dbtx, 0) 162 if err != nil { 163 t.Fatal(err) 164 } 165 if !equalExtKeys(accountExtKey, slip0044Account0ExtKey) { 166 t.Fatalf("upgraded database has wrong account xpub") 167 } 168 169 // Check that the SLIP0044-derived account 0's first address can be 170 // created and is indexed. 171 err = m.SyncAccountToAddrIndex(ns, 0, 1, 0) 172 if err != nil { 173 t.Fatal(err) 174 } 175 if !m.ExistsHash160(ns, slip0044Account0Address0Hash160[:]) { 176 t.Fatalf("upgraded database does not record SLIP0044-derived account 0 branch 0 address 0") 177 } 178 179 // Create the next account, and perform all of the same checks on it as 180 // the first account. 181 _, err = m.NewAccount(ns, "account-1") 182 if err != nil { 183 t.Fatal(err) 184 } 185 accountExtKey, err = m.AccountExtendedPubKey(dbtx, 1) 186 if err != nil { 187 t.Fatal(err) 188 } 189 if !equalExtKeys(accountExtKey, slip0044Account1ExtKey) { 190 t.Fatal("upgraded database derived wrong account xpub") 191 } 192 err = m.SyncAccountToAddrIndex(ns, 1, 1, 0) 193 if err != nil { 194 t.Fatal(err) 195 } 196 if !m.ExistsHash160(ns, slip0044Account1Address0Hash160[:]) { 197 t.Fatalf("upgraded database does not record SLIP0044-derived account 1 branch 0 address 0") 198 } 199 200 // Check that the upgrade can not be performed a second time. 201 err = m.UpgradeToSLIP0044CoinType(dbtx) 202 if !errors.Is(err, errors.Invalid) { 203 t.Fatalf("upgrade database did not refuse second upgrade with errors.Invalid") 204 } 205 206 return nil 207 }) 208 if err != nil { 209 t.Error(err) 210 } 211 }