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  }