github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/api/wallet_test.go (about)

     1  package api
     2  
     3  import (
     4  	"fmt"
     5  	"net/url"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/NebulousLabs/Sia/build"
    10  	"github.com/NebulousLabs/Sia/modules"
    11  	"github.com/NebulousLabs/Sia/modules/consensus"
    12  	"github.com/NebulousLabs/Sia/modules/gateway"
    13  	"github.com/NebulousLabs/Sia/modules/transactionpool"
    14  	"github.com/NebulousLabs/Sia/modules/wallet"
    15  	"github.com/NebulousLabs/Sia/types"
    16  )
    17  
    18  // TestIntegrationWalletGETEncrypted probes the GET call to /wallet when the
    19  // wallet has never been encrypted.
    20  func TestIntegrationWalletGETEncrypted(t *testing.T) {
    21  	if testing.Short() {
    22  		t.SkipNow()
    23  	}
    24  
    25  	// Check a wallet that has never been encrypted.
    26  	testdir := build.TempDir("api", "TestIntegrationWalletGETEncrypted")
    27  	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
    28  	if err != nil {
    29  		t.Fatal("Failed to create gateway:", err)
    30  	}
    31  	cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir))
    32  	if err != nil {
    33  		t.Fatal("Failed to create consensus set:", err)
    34  	}
    35  	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir))
    36  	if err != nil {
    37  		t.Fatal("Failed to create tpool:", err)
    38  	}
    39  	w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
    40  	if err != nil {
    41  		t.Fatal("Failed to create wallet:", err)
    42  	}
    43  	srv, err := NewServer("localhost:0", "Sia-Agent", cs, nil, g, nil, nil, nil, tp, w)
    44  	if err != nil {
    45  		t.Fatal(err)
    46  	}
    47  
    48  	// Assemble the serverTester and start listening for api requests.
    49  	st := &serverTester{
    50  		cs:      cs,
    51  		gateway: g,
    52  		tpool:   tp,
    53  		wallet:  w,
    54  
    55  		server: srv,
    56  	}
    57  	errChan := make(chan error)
    58  	go func() {
    59  		listenErr := srv.Serve()
    60  		errChan <- listenErr
    61  	}()
    62  	defer func() {
    63  		err := <-errChan
    64  		if err != nil {
    65  			t.Fatalf("API server quit: %v", err)
    66  		}
    67  	}()
    68  	defer st.server.Close()
    69  
    70  	var wg WalletGET
    71  	err = st.getAPI("/wallet", &wg)
    72  	if err != nil {
    73  		t.Fatal(err)
    74  	}
    75  	if wg.Encrypted {
    76  		t.Error("Wallet has never been encrypted")
    77  	}
    78  	if wg.Unlocked {
    79  		t.Error("Wallet has never been unlocked")
    80  	}
    81  }
    82  
    83  // TestIntegrationWalletBlankEncrypt tries to encrypt and unlock the wallet
    84  // through the api using a blank encryption key - meaning that the wallet seed
    85  // returned by the encryption call can be used as the encryption key.
    86  func TestIntegrationWalletBlankEncrypt(t *testing.T) {
    87  	if testing.Short() {
    88  		t.SkipNow()
    89  	}
    90  	// Create a server object without encrypting or unlocking the wallet.
    91  	testdir := build.TempDir("api", "TestIntegrationWalletBlankEncrypt")
    92  	g, err := gateway.New("localhost:0", filepath.Join(testdir, modules.GatewayDir))
    93  	if err != nil {
    94  		t.Fatal(err)
    95  	}
    96  	cs, err := consensus.New(g, filepath.Join(testdir, modules.ConsensusDir))
    97  	if err != nil {
    98  		t.Fatal(err)
    99  	}
   100  	tp, err := transactionpool.New(cs, g, filepath.Join(testdir, modules.TransactionPoolDir))
   101  	if err != nil {
   102  		t.Fatal(err)
   103  	}
   104  	w, err := wallet.New(cs, tp, filepath.Join(testdir, modules.WalletDir))
   105  	if err != nil {
   106  		t.Fatal(err)
   107  	}
   108  	srv, err := NewServer("localhost:0", "Sia-Agent", cs, nil, g, nil, nil, nil, tp, w)
   109  	if err != nil {
   110  		t.Fatal(err)
   111  	}
   112  	// Assemble the serverTester.
   113  	st := &serverTester{
   114  		cs:      cs,
   115  		gateway: g,
   116  		tpool:   tp,
   117  		wallet:  w,
   118  		server:  srv,
   119  	}
   120  	go func() {
   121  		listenErr := srv.Serve()
   122  		if listenErr != nil {
   123  			panic(listenErr)
   124  		}
   125  	}()
   126  	defer st.server.Close()
   127  
   128  	// Make a call to /wallet/encrypt and get the seed. Provide no encryption
   129  	// key so that the encryption key is the seed that gets returned.
   130  	var wep WalletEncryptPOST
   131  	err = st.postAPI("/wallet/encrypt", url.Values{}, &wep)
   132  	if err != nil {
   133  		t.Fatal(err)
   134  	}
   135  	// Use the seed to call /wallet/unlock.
   136  	unlockValues := url.Values{}
   137  	unlockValues.Set("encryptionpassword", wep.PrimarySeed)
   138  	err = st.stdPostAPI("/wallet/unlock", unlockValues)
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  	// Check that the wallet actually unlocked.
   143  	if !w.Unlocked() {
   144  		t.Error("wallet is not unlocked")
   145  	}
   146  }
   147  
   148  // TestIntegrationWalletGETSiacoins probes the GET call to /wallet when the
   149  // siacoin balance is being manipulated.
   150  func TestIntegrationWalletGETSiacoins(t *testing.T) {
   151  	if testing.Short() {
   152  		t.SkipNow()
   153  	}
   154  	st, err := createServerTester("TestIntegrationWalletGETSiacoins")
   155  	if err != nil {
   156  		t.Fatal(err)
   157  	}
   158  	defer st.server.Close()
   159  
   160  	// Check the initial wallet is encrypted, unlocked, and has the siacoins
   161  	// that got mined.
   162  	var wg WalletGET
   163  	err = st.getAPI("/wallet", &wg)
   164  	if err != nil {
   165  		t.Fatal(err)
   166  	}
   167  	if !wg.Encrypted {
   168  		t.Error("Wallet has been encrypted")
   169  	}
   170  	if !wg.Unlocked {
   171  		t.Error("Wallet has been unlocked")
   172  	}
   173  	if wg.ConfirmedSiacoinBalance.Cmp(types.CalculateCoinbase(1)) != 0 {
   174  		t.Error("reported wallet balance does not reflect the single block that has been mined")
   175  	}
   176  	if wg.UnconfirmedOutgoingSiacoins.Cmp(types.NewCurrency64(0)) != 0 {
   177  		t.Error("there should not be unconfirmed outgoing siacoins")
   178  	}
   179  	if wg.UnconfirmedIncomingSiacoins.Cmp(types.NewCurrency64(0)) != 0 {
   180  		t.Error("there should not be unconfirmed incoming siacoins")
   181  	}
   182  
   183  	// Send coins to a wallet address through the api.
   184  	var wag WalletAddressGET
   185  	err = st.getAPI("/wallet/address", &wag)
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	sendSiacoinsValues := url.Values{}
   190  	sendSiacoinsValues.Set("amount", "1234")
   191  	sendSiacoinsValues.Add("destination", wag.Address.String())
   192  	err = st.stdPostAPI("/wallet/siacoins", sendSiacoinsValues)
   193  	if err != nil {
   194  		t.Fatal(err)
   195  	}
   196  
   197  	// Check that the wallet is reporting unconfirmed siacoins.
   198  	err = st.getAPI("/wallet", &wg)
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  	if !wg.Encrypted {
   203  		t.Error("Wallet has been encrypted")
   204  	}
   205  	if !wg.Unlocked {
   206  		t.Error("Wallet has been unlocked")
   207  	}
   208  	if wg.ConfirmedSiacoinBalance.Cmp(types.CalculateCoinbase(1)) != 0 {
   209  		t.Error("reported wallet balance does not reflect the single block that has been mined")
   210  	}
   211  	if wg.UnconfirmedOutgoingSiacoins.Cmp(types.NewCurrency64(0)) <= 0 {
   212  		t.Error("there should be unconfirmed outgoing siacoins")
   213  	}
   214  	if wg.UnconfirmedIncomingSiacoins.Cmp(types.NewCurrency64(0)) <= 0 {
   215  		t.Error("there should be unconfirmed incoming siacoins")
   216  	}
   217  	if wg.UnconfirmedOutgoingSiacoins.Cmp(wg.UnconfirmedIncomingSiacoins) <= 0 {
   218  		t.Error("net movement of siacoins should be outgoing (miner fees)")
   219  	}
   220  
   221  	// Mine a block and see that the unconfirmed balances reduce back to
   222  	// nothing.
   223  	_, err = st.miner.AddBlock()
   224  	if err != nil {
   225  		t.Fatal(err)
   226  	}
   227  	err = st.getAPI("/wallet", &wg)
   228  	if err != nil {
   229  		t.Fatal(err)
   230  	}
   231  	if wg.ConfirmedSiacoinBalance.Cmp(types.CalculateCoinbase(1).Add(types.CalculateCoinbase(2))) >= 0 {
   232  		t.Error("reported wallet balance does not reflect mining two blocks and eating a miner fee")
   233  	}
   234  	if wg.UnconfirmedOutgoingSiacoins.Cmp(types.NewCurrency64(0)) != 0 {
   235  		t.Error("there should not be unconfirmed outgoing siacoins")
   236  	}
   237  	if wg.UnconfirmedIncomingSiacoins.Cmp(types.NewCurrency64(0)) != 0 {
   238  		t.Error("there should not be unconfirmed incoming siacoins")
   239  	}
   240  }
   241  
   242  // TestIntegrationWalletTransactionGETid queries the /wallet/transaction/$(id)
   243  // api call.
   244  func TestIntegrationWalletTransactionGETid(t *testing.T) {
   245  	if testing.Short() {
   246  		t.SkipNow()
   247  	}
   248  	st, err := createServerTester("TestIntegrationWalletTransactionGETid")
   249  	if err != nil {
   250  		t.Fatal(err)
   251  	}
   252  	defer st.server.Close()
   253  
   254  	// Mining blocks should have created transactions for the wallet containing
   255  	// miner payouts. Get the list of transactions.
   256  	var wtg WalletTransactionsGET
   257  	err = st.getAPI("/wallet/transactions?startheight=0&endheight=10", &wtg)
   258  	if err != nil {
   259  		t.Fatal(err)
   260  	}
   261  	if len(wtg.ConfirmedTransactions) == 0 {
   262  		t.Error("expecting a few wallet transactions, corresponding to miner payouts.")
   263  	}
   264  	if len(wtg.UnconfirmedTransactions) != 0 {
   265  		t.Error("expecting 0 unconfirmed transactions")
   266  	}
   267  
   268  	// Query the details of the first transaction using
   269  	// /wallet/transaction/$(id)
   270  	var wtgid WalletTransactionGETid
   271  	wtgidQuery := fmt.Sprintf("/wallet/transaction/%s", wtg.ConfirmedTransactions[0].TransactionID)
   272  	err = st.getAPI(wtgidQuery, &wtgid)
   273  	if err != nil {
   274  		t.Fatal(err)
   275  	}
   276  	if len(wtgid.Transaction.Inputs) != 0 {
   277  		t.Error("miner payout should appear as an output, not an input")
   278  	}
   279  	if len(wtgid.Transaction.Outputs) != 1 {
   280  		t.Fatal("a single miner payout output should have been created")
   281  	}
   282  	if wtgid.Transaction.Outputs[0].FundType != types.SpecifierMinerPayout {
   283  		t.Error("fund type should be a miner payout")
   284  	}
   285  }