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  }