gitlab.com/jokerrs1/Sia@v1.3.2/modules/wallet/seed_test.go (about) 1 package wallet 2 3 import ( 4 "bytes" 5 "path/filepath" 6 "testing" 7 8 "github.com/NebulousLabs/Sia/build" 9 "github.com/NebulousLabs/Sia/crypto" 10 "github.com/NebulousLabs/Sia/modules" 11 "github.com/NebulousLabs/Sia/modules/miner" 12 "github.com/NebulousLabs/Sia/types" 13 ) 14 15 // TestPrimarySeed checks that the correct seed is returned when calling 16 // PrimarySeed. 17 func TestPrimarySeed(t *testing.T) { 18 if testing.Short() { 19 t.SkipNow() 20 } 21 t.Parallel() 22 // Start with a blank wallet tester. 23 wt, err := createBlankWalletTester(t.Name()) 24 if err != nil { 25 t.Fatal(err) 26 } 27 defer wt.closeWt() 28 29 // Create a seed and unlock the wallet. 30 seed, err := wt.wallet.Encrypt(crypto.TwofishKey{}) 31 if err != nil { 32 t.Fatal(err) 33 } 34 err = wt.wallet.Unlock(crypto.TwofishKey(crypto.HashObject(seed))) 35 if err != nil { 36 t.Fatal(err) 37 } 38 39 // Try getting an address, see that the seed advances correctly. 40 primarySeed, remaining, err := wt.wallet.PrimarySeed() 41 if err != nil { 42 t.Fatal(err) 43 } 44 if !bytes.Equal(primarySeed[:], seed[:]) { 45 t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt") 46 } 47 if remaining != maxScanKeys { 48 t.Error("primary seed is returning the wrong number of remaining addresses") 49 } 50 _, err = wt.wallet.NextAddress() 51 if err != nil { 52 t.Fatal(err) 53 } 54 _, remaining, err = wt.wallet.PrimarySeed() 55 if err != nil { 56 t.Fatal(err) 57 } 58 if remaining != maxScanKeys-1 { 59 t.Error("primary seed is returning the wrong number of remaining addresses") 60 } 61 62 // Lock then unlock the wallet and check the responses. 63 err = wt.wallet.Lock() 64 if err != nil { 65 t.Fatal(err) 66 } 67 _, _, err = wt.wallet.PrimarySeed() 68 if err != modules.ErrLockedWallet { 69 t.Error("unexpected err:", err) 70 } 71 err = wt.wallet.Unlock(crypto.TwofishKey(crypto.HashObject(seed))) 72 if err != nil { 73 t.Fatal(err) 74 } 75 primarySeed, remaining, err = wt.wallet.PrimarySeed() 76 if err != nil { 77 t.Fatal(err) 78 } 79 if !bytes.Equal(primarySeed[:], seed[:]) { 80 t.Error("PrimarySeed is returning a value inconsitent with the seed returned by Encrypt") 81 } 82 if remaining != maxScanKeys-1 { 83 t.Error("primary seed is returning the wrong number of remaining addresses") 84 } 85 } 86 87 // TestLoadSeed checks that a seed can be successfully recovered from a wallet, 88 // and then remain available on subsequent loads of the wallet. 89 func TestLoadSeed(t *testing.T) { 90 if testing.Short() { 91 t.SkipNow() 92 } 93 t.Parallel() 94 wt, err := createWalletTester(t.Name(), &modules.ProductionDependencies{}) 95 if err != nil { 96 t.Fatal(err) 97 } 98 defer wt.closeWt() 99 seed, _, err := wt.wallet.PrimarySeed() 100 if err != nil { 101 t.Fatal(err) 102 } 103 allSeeds, err := wt.wallet.AllSeeds() 104 if err != nil { 105 t.Fatal(err) 106 } 107 if len(allSeeds) != 1 { 108 t.Fatal("AllSeeds should be returning the primary seed.") 109 } else if allSeeds[0] != seed { 110 t.Fatal("AllSeeds returned the wrong seed") 111 } 112 wt.wallet.Close() 113 114 dir := filepath.Join(build.TempDir(modules.WalletDir, t.Name()+"1"), modules.WalletDir) 115 w, err := New(wt.cs, wt.tpool, dir) 116 if err != nil { 117 t.Fatal(err) 118 } 119 newSeed, err := w.Encrypt(crypto.TwofishKey{}) 120 if err != nil { 121 t.Fatal(err) 122 } 123 err = w.Unlock(crypto.TwofishKey(crypto.HashObject(newSeed))) 124 if err != nil { 125 t.Fatal(err) 126 } 127 // Balance of wallet should be 0. 128 siacoinBal, _, _ := w.ConfirmedBalance() 129 if !siacoinBal.Equals64(0) { 130 t.Error("fresh wallet should not have a balance") 131 } 132 err = w.LoadSeed(crypto.TwofishKey(crypto.HashObject(newSeed)), seed) 133 if err != nil { 134 t.Fatal(err) 135 } 136 allSeeds, err = w.AllSeeds() 137 if err != nil { 138 t.Fatal(err) 139 } 140 if len(allSeeds) != 2 { 141 t.Error("AllSeeds should be returning the primary seed with the recovery seed.") 142 } 143 if allSeeds[0] != newSeed { 144 t.Error("AllSeeds returned the wrong seed") 145 } 146 if !bytes.Equal(allSeeds[1][:], seed[:]) { 147 t.Error("AllSeeds returned the wrong seed") 148 } 149 150 siacoinBal2, _, _ := w.ConfirmedBalance() 151 if siacoinBal2.Cmp64(0) <= 0 { 152 t.Error("wallet failed to load a seed with money in it") 153 } 154 allSeeds, err = w.AllSeeds() 155 if err != nil { 156 t.Fatal(err) 157 } 158 if len(allSeeds) != 2 { 159 t.Error("AllSeeds should be returning the primary seed with the recovery seed.") 160 } 161 if !bytes.Equal(allSeeds[0][:], newSeed[:]) { 162 t.Error("AllSeeds returned the wrong seed") 163 } 164 if !bytes.Equal(allSeeds[1][:], seed[:]) { 165 t.Error("AllSeeds returned the wrong seed") 166 } 167 } 168 169 // TestSweepSeedCoins tests that sweeping a seed results in the transfer of 170 // its siacoin outputs to the wallet. 171 func TestSweepSeedCoins(t *testing.T) { 172 if testing.Short() { 173 t.SkipNow() 174 } 175 t.Parallel() 176 // create a wallet with some money 177 wt, err := createWalletTester("TestSweepSeedCoins0", &modules.ProductionDependencies{}) 178 if err != nil { 179 t.Fatal(err) 180 } 181 defer wt.closeWt() 182 seed, _, err := wt.wallet.PrimarySeed() 183 if err != nil { 184 t.Fatal(err) 185 } 186 // send money to ourselves, so that we sweep a real output (instead of 187 // just a miner payout) 188 uc, err := wt.wallet.NextAddress() 189 if err != nil { 190 t.Fatal(err) 191 } 192 _, err = wt.wallet.SendSiacoins(types.SiacoinPrecision, uc.UnlockHash()) 193 if err != nil { 194 t.Fatal(err) 195 } 196 _, err = wt.miner.AddBlock() 197 if err != nil { 198 t.Fatal(err) 199 } 200 201 // create a blank wallet 202 dir := filepath.Join(build.TempDir(modules.WalletDir, "TestSweepSeedCoins1"), modules.WalletDir) 203 w, err := New(wt.cs, wt.tpool, dir) 204 if err != nil { 205 t.Fatal(err) 206 } 207 newSeed, err := w.Encrypt(crypto.TwofishKey{}) 208 if err != nil { 209 t.Fatal(err) 210 } 211 err = w.Unlock(crypto.TwofishKey(crypto.HashObject(newSeed))) 212 if err != nil { 213 t.Fatal(err) 214 } 215 // starting balance should be 0. 216 siacoinBal, _, _ := w.ConfirmedBalance() 217 if !siacoinBal.IsZero() { 218 t.Error("fresh wallet should not have a balance") 219 } 220 221 // sweep the seed of the first wallet into the second 222 sweptCoins, _, err := w.SweepSeed(seed) 223 if err != nil { 224 t.Fatal(err) 225 } 226 227 // new wallet should have exactly 'sweptCoins' coins 228 _, incoming := w.UnconfirmedBalance() 229 if incoming.Cmp(sweptCoins) != 0 { 230 t.Fatalf("wallet should have correct balance after sweeping seed: wanted %v, got %v", sweptCoins, incoming) 231 } 232 } 233 234 // TestSweepSeedFunds tests that sweeping a seed results in the transfer of 235 // its siafund outputs to the wallet. 236 func TestSweepSeedFunds(t *testing.T) { 237 if testing.Short() { 238 t.SkipNow() 239 } 240 t.Parallel() 241 wt, err := createWalletTester("TestSweepSeedFunds", &modules.ProductionDependencies{}) 242 if err != nil { 243 t.Fatal(err) 244 } 245 defer wt.closeWt() 246 247 // Load the key into the wallet. 248 err = wt.wallet.LoadSiagKeys(wt.walletMasterKey, []string{"../../types/siag0of1of1.siakey"}) 249 if err != nil { 250 t.Error(err) 251 } 252 253 _, siafundBal, _ := wt.wallet.ConfirmedBalance() 254 if siafundBal.Cmp(types.NewCurrency64(2000)) != 0 { 255 t.Error("expecting a siafund balance of 2000 from the 1of1 key") 256 } 257 // need to reset the miner as well, since it depends on the wallet 258 wt.miner, err = miner.New(wt.cs, wt.tpool, wt.wallet, wt.wallet.persistDir) 259 if err != nil { 260 t.Fatal(err) 261 } 262 263 // Create a seed and generate an address to send money to. 264 seed := modules.Seed{1, 2, 3} 265 sk := generateSpendableKey(seed, 1) 266 267 // Send some siafunds to the address. 268 _, err = wt.wallet.SendSiafunds(types.NewCurrency64(12), sk.UnlockConditions.UnlockHash()) 269 if err != nil { 270 t.Fatal(err) 271 } 272 // Send some siacoins to the address, but not enough to cover the 273 // transaction fee. 274 _, err = wt.wallet.SendSiacoins(types.NewCurrency64(1), sk.UnlockConditions.UnlockHash()) 275 if err != nil { 276 t.Fatal(err) 277 } 278 // mine blocks without earning payout until our balance is stable 279 for i := types.BlockHeight(0); i < types.MaturityDelay; i++ { 280 wt.addBlockNoPayout() 281 } 282 oldCoinBalance, siafundBal, _ := wt.wallet.ConfirmedBalance() 283 if siafundBal.Cmp(types.NewCurrency64(1988)) != 0 { 284 t.Errorf("expecting balance of %v after sending siafunds to the seed, got %v", 1988, siafundBal) 285 } 286 287 // Sweep the seed. 288 coins, funds, err := wt.wallet.SweepSeed(seed) 289 if err != nil { 290 t.Fatal(err) 291 } 292 if !coins.IsZero() { 293 t.Error("expected to sweep 0 coins, got", coins) 294 } 295 if funds.Cmp(types.NewCurrency64(12)) != 0 { 296 t.Errorf("expected to sweep %v funds, got %v", 12, funds) 297 } 298 // add a block without earning its payout 299 wt.addBlockNoPayout() 300 301 // Wallet balance should have decreased to pay for the sweep transaction. 302 newCoinBalance, _, _ := wt.wallet.ConfirmedBalance() 303 if newCoinBalance.Cmp(oldCoinBalance) >= 0 { 304 t.Error("expecting balance to go down; instead, increased by", newCoinBalance.Sub(oldCoinBalance)) 305 } 306 } 307 308 // TestSweepSeedSentFunds tests that sweeping a seed results in the transfer 309 // of its siafund outputs to the wallet, even after the funds have been 310 // transferred a few times. 311 func TestSweepSeedSentFunds(t *testing.T) { 312 if testing.Short() { 313 t.SkipNow() 314 } 315 t.Parallel() 316 wt, err := createWalletTester("TestSweepSeedSentFunds", &modules.ProductionDependencies{}) 317 if err != nil { 318 t.Fatal(err) 319 } 320 defer wt.closeWt() 321 322 // Load the key into the wallet. 323 err = wt.wallet.LoadSiagKeys(wt.walletMasterKey, []string{"../../types/siag0of1of1.siakey"}) 324 if err != nil { 325 t.Error(err) 326 } 327 328 _, siafundBal, _ := wt.wallet.ConfirmedBalance() 329 if siafundBal.Cmp(types.NewCurrency64(2000)) != 0 { 330 t.Error("expecting a siafund balance of 2000 from the 1of1 key") 331 } 332 // need to reset the miner as well, since it depends on the wallet 333 wt.miner, err = miner.New(wt.cs, wt.tpool, wt.wallet, wt.wallet.persistDir) 334 if err != nil { 335 t.Fatal(err) 336 } 337 338 // send funds to ourself a few times 339 for i := 0; i < 10; i++ { 340 uc, err := wt.wallet.NextAddress() 341 if err != nil { 342 t.Fatal(err) 343 } 344 _, err = wt.wallet.SendSiafunds(types.NewCurrency64(1), uc.UnlockHash()) 345 if err != nil { 346 t.Fatal(err) 347 } 348 wt.addBlockNoPayout() 349 } 350 // send some funds to the void 351 _, err = wt.wallet.SendSiafunds(types.NewCurrency64(10), types.UnlockHash{}) 352 if err != nil { 353 t.Fatal(err) 354 } 355 wt.addBlockNoPayout() 356 357 // Create a seed and generate an address to send money to. 358 seed := modules.Seed{1, 2, 3} 359 sk := generateSpendableKey(seed, 1) 360 361 // Send some siafunds to the address. 362 _, err = wt.wallet.SendSiafunds(types.NewCurrency64(12), sk.UnlockConditions.UnlockHash()) 363 if err != nil { 364 t.Fatal(err) 365 } 366 // mine blocks without earning payout until our balance is stable 367 for i := types.BlockHeight(0); i < types.MaturityDelay; i++ { 368 wt.addBlockNoPayout() 369 } 370 oldCoinBalance, siafundBal, _ := wt.wallet.ConfirmedBalance() 371 if expected := 2000 - 12 - 10; siafundBal.Cmp(types.NewCurrency64(uint64(expected))) != 0 { 372 t.Errorf("expecting balance of %v after sending siafunds to the seed, got %v", expected, siafundBal) 373 } 374 375 // Sweep the seed. 376 coins, funds, err := wt.wallet.SweepSeed(seed) 377 if err != nil { 378 t.Fatal(err) 379 } 380 if !coins.IsZero() { 381 t.Error("expected to sweep 0 coins, got", coins) 382 } 383 if funds.Cmp(types.NewCurrency64(12)) != 0 { 384 t.Errorf("expected to sweep %v funds, got %v", 12, funds) 385 } 386 // add a block without earning its payout 387 wt.addBlockNoPayout() 388 389 // Wallet balance should have decreased to pay for the sweep transaction. 390 newCoinBalance, _, _ := wt.wallet.ConfirmedBalance() 391 if newCoinBalance.Cmp(oldCoinBalance) >= 0 { 392 t.Error("expecting balance to go down; instead, increased by", newCoinBalance.Sub(oldCoinBalance)) 393 } 394 } 395 396 // TestSweepSeedCoinsAndFunds tests that sweeping a seed results in the 397 // transfer of its siacoin and siafund outputs to the wallet. 398 func TestSweepSeedCoinsAndFunds(t *testing.T) { 399 if testing.Short() || !build.VLONG { 400 t.SkipNow() 401 } 402 t.Parallel() 403 wt, err := createWalletTester("TestSweepSeedCoinsAndFunds", &modules.ProductionDependencies{}) 404 if err != nil { 405 t.Fatal(err) 406 } 407 defer wt.closeWt() 408 409 // Load the key into the wallet. 410 err = wt.wallet.LoadSiagKeys(wt.walletMasterKey, []string{"../../types/siag0of1of1.siakey"}) 411 if err != nil { 412 t.Error(err) 413 } 414 415 _, siafundBal, _ := wt.wallet.ConfirmedBalance() 416 if siafundBal.Cmp(types.NewCurrency64(2000)) != 0 { 417 t.Error("expecting a siafund balance of 2000 from the 1of1 key") 418 } 419 420 // Create a seed and generate an address to send money to. 421 seed := modules.Seed{1, 2, 3} 422 sk := generateSpendableKey(seed, 1) 423 424 // Send some siafunds to the address. 425 for i := 0; i < 12; i++ { 426 _, err = wt.wallet.SendSiafunds(types.NewCurrency64(1), sk.UnlockConditions.UnlockHash()) 427 if err != nil { 428 t.Fatal(err) 429 } 430 wt.addBlockNoPayout() 431 } 432 // Send some siacoins to the address -- must be more than the transaction 433 // fee. 434 for i := 0; i < 100; i++ { 435 _, err = wt.wallet.SendSiacoins(types.SiacoinPrecision.Mul64(10), sk.UnlockConditions.UnlockHash()) 436 if err != nil { 437 t.Fatal(err) 438 } 439 wt.addBlockNoPayout() 440 } 441 // mine blocks without earning payout until our balance is stable 442 for i := types.BlockHeight(0); i < types.MaturityDelay; i++ { 443 wt.addBlockNoPayout() 444 } 445 oldCoinBalance, siafundBal, _ := wt.wallet.ConfirmedBalance() 446 if siafundBal.Cmp(types.NewCurrency64(1988)) != 0 { 447 t.Errorf("expecting balance of %v after sending siafunds to the seed, got %v", 1988, siafundBal) 448 } 449 450 // Sweep the seed. 451 coins, funds, err := wt.wallet.SweepSeed(seed) 452 if err != nil { 453 t.Fatal(err) 454 } 455 if coins.IsZero() { 456 t.Error("expected to sweep coins, got 0") 457 } 458 if funds.Cmp(types.NewCurrency64(12)) != 0 { 459 t.Errorf("expected to sweep %v funds, got %v", 12, funds) 460 } 461 // add a block without earning its payout 462 wt.addBlockNoPayout() 463 464 // Wallet balance should have decreased to pay for the sweep transaction. 465 newCoinBalance, _, _ := wt.wallet.ConfirmedBalance() 466 if newCoinBalance.Cmp(oldCoinBalance) <= 0 { 467 t.Error("expecting balance to go up; instead, decreased by", oldCoinBalance.Sub(newCoinBalance)) 468 } 469 } 470 471 // TestGenerateKeys tests that the generateKeys function correctly generates a 472 // key for every index specified. 473 func TestGenerateKeys(t *testing.T) { 474 for i, k := range generateKeys(modules.Seed{}, 1000, 4000) { 475 if len(k.UnlockConditions.PublicKeys) == 0 { 476 t.Errorf("index %v was skipped", i) 477 } 478 } 479 }