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