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