github.com/ZuluSpl0it/Sia@v1.3.7/node/api/wallet.go (about)

     1  package api
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"math"
     7  	"net/http"
     8  	"path/filepath"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/NebulousLabs/Sia/crypto"
    13  	"github.com/NebulousLabs/Sia/modules"
    14  	"github.com/NebulousLabs/Sia/types"
    15  
    16  	"github.com/NebulousLabs/entropy-mnemonics"
    17  	"github.com/julienschmidt/httprouter"
    18  )
    19  
    20  type (
    21  	// WalletGET contains general information about the wallet.
    22  	WalletGET struct {
    23  		Encrypted  bool              `json:"encrypted"`
    24  		Height     types.BlockHeight `json:"height"`
    25  		Rescanning bool              `json:"rescanning"`
    26  		Unlocked   bool              `json:"unlocked"`
    27  
    28  		ConfirmedSiacoinBalance     types.Currency `json:"confirmedsiacoinbalance"`
    29  		UnconfirmedOutgoingSiacoins types.Currency `json:"unconfirmedoutgoingsiacoins"`
    30  		UnconfirmedIncomingSiacoins types.Currency `json:"unconfirmedincomingsiacoins"`
    31  
    32  		SiacoinClaimBalance types.Currency `json:"siacoinclaimbalance"`
    33  		SiafundBalance      types.Currency `json:"siafundbalance"`
    34  
    35  		DustThreshold types.Currency `json:"dustthreshold"`
    36  	}
    37  
    38  	// WalletAddressGET contains an address returned by a GET call to
    39  	// /wallet/address.
    40  	WalletAddressGET struct {
    41  		Address types.UnlockHash `json:"address"`
    42  	}
    43  
    44  	// WalletAddressesGET contains the list of wallet addresses returned by a
    45  	// GET call to /wallet/addresses.
    46  	WalletAddressesGET struct {
    47  		Addresses []types.UnlockHash `json:"addresses"`
    48  	}
    49  
    50  	// WalletInitPOST contains the primary seed that gets generated during a
    51  	// POST call to /wallet/init.
    52  	WalletInitPOST struct {
    53  		PrimarySeed string `json:"primaryseed"`
    54  	}
    55  
    56  	// WalletSiacoinsPOST contains the transaction sent in the POST call to
    57  	// /wallet/siacoins.
    58  	WalletSiacoinsPOST struct {
    59  		TransactionIDs []types.TransactionID `json:"transactionids"`
    60  	}
    61  
    62  	// WalletSiafundsPOST contains the transaction sent in the POST call to
    63  	// /wallet/siafunds.
    64  	WalletSiafundsPOST struct {
    65  		TransactionIDs []types.TransactionID `json:"transactionids"`
    66  	}
    67  
    68  	// WalletSeedsGET contains the seeds used by the wallet.
    69  	WalletSeedsGET struct {
    70  		PrimarySeed        string   `json:"primaryseed"`
    71  		AddressesRemaining int      `json:"addressesremaining"`
    72  		AllSeeds           []string `json:"allseeds"`
    73  	}
    74  
    75  	// WalletSweepPOST contains the coins and funds returned by a call to
    76  	// /wallet/sweep.
    77  	WalletSweepPOST struct {
    78  		Coins types.Currency `json:"coins"`
    79  		Funds types.Currency `json:"funds"`
    80  	}
    81  
    82  	// WalletTransactionGETid contains the transaction returned by a call to
    83  	// /wallet/transaction/:id
    84  	WalletTransactionGETid struct {
    85  		Transaction modules.ProcessedTransaction `json:"transaction"`
    86  	}
    87  
    88  	// WalletTransactionsGET contains the specified set of confirmed and
    89  	// unconfirmed transactions.
    90  	WalletTransactionsGET struct {
    91  		ConfirmedTransactions   []modules.ProcessedTransaction `json:"confirmedtransactions"`
    92  		UnconfirmedTransactions []modules.ProcessedTransaction `json:"unconfirmedtransactions"`
    93  	}
    94  
    95  	// WalletTransactionsGETaddr contains the set of wallet transactions
    96  	// relevant to the input address provided in the call to
    97  	// /wallet/transaction/:addr
    98  	WalletTransactionsGETaddr struct {
    99  		ConfirmedTransactions   []modules.ProcessedTransaction `json:"confirmedtransactions"`
   100  		UnconfirmedTransactions []modules.ProcessedTransaction `json:"unconfirmedtransactions"`
   101  	}
   102  
   103  	// WalletVerifyAddressGET contains a bool indicating if the address passed to
   104  	// /wallet/verify/address/:addr is a valid address.
   105  	WalletVerifyAddressGET struct {
   106  		Valid bool `json:"valid"`
   107  	}
   108  )
   109  
   110  // encryptionKeys enumerates the possible encryption keys that can be derived
   111  // from an input string.
   112  func encryptionKeys(seedStr string) (validKeys []crypto.TwofishKey) {
   113  	dicts := []mnemonics.DictionaryID{"english", "german", "japanese"}
   114  	for _, dict := range dicts {
   115  		seed, err := modules.StringToSeed(seedStr, dict)
   116  		if err != nil {
   117  			continue
   118  		}
   119  		validKeys = append(validKeys, crypto.TwofishKey(crypto.HashObject(seed)))
   120  	}
   121  	validKeys = append(validKeys, crypto.TwofishKey(crypto.HashObject(seedStr)))
   122  	return validKeys
   123  }
   124  
   125  // walletHander handles API calls to /wallet.
   126  func (api *API) walletHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   127  	siacoinBal, siafundBal, siaclaimBal, err := api.wallet.ConfirmedBalance()
   128  	if err != nil {
   129  		WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest)
   130  		return
   131  	}
   132  	siacoinsOut, siacoinsIn, err := api.wallet.UnconfirmedBalance()
   133  	if err != nil {
   134  		WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest)
   135  		return
   136  	}
   137  	dustThreshold, err := api.wallet.DustThreshold()
   138  	if err != nil {
   139  		WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest)
   140  		return
   141  	}
   142  	encrypted, err := api.wallet.Encrypted()
   143  	if err != nil {
   144  		WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest)
   145  		return
   146  	}
   147  	unlocked, err := api.wallet.Unlocked()
   148  	if err != nil {
   149  		WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest)
   150  		return
   151  	}
   152  	rescanning, err := api.wallet.Rescanning()
   153  	if err != nil {
   154  		WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest)
   155  		return
   156  	}
   157  	height, err := api.wallet.Height()
   158  	if err != nil {
   159  		WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest)
   160  		return
   161  	}
   162  	WriteJSON(w, WalletGET{
   163  		Encrypted:  encrypted,
   164  		Unlocked:   unlocked,
   165  		Rescanning: rescanning,
   166  		Height:     height,
   167  
   168  		ConfirmedSiacoinBalance:     siacoinBal,
   169  		UnconfirmedOutgoingSiacoins: siacoinsOut,
   170  		UnconfirmedIncomingSiacoins: siacoinsIn,
   171  
   172  		SiafundBalance:      siafundBal,
   173  		SiacoinClaimBalance: siaclaimBal,
   174  
   175  		DustThreshold: dustThreshold,
   176  	})
   177  }
   178  
   179  // wallet033xHandler handles API calls to /wallet/033x.
   180  func (api *API) wallet033xHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   181  	source := req.FormValue("source")
   182  	// Check that source is an absolute paths.
   183  	if !filepath.IsAbs(source) {
   184  		WriteError(w, Error{"error when calling /wallet/033x: source must be an absolute path"}, http.StatusBadRequest)
   185  		return
   186  	}
   187  	potentialKeys := encryptionKeys(req.FormValue("encryptionpassword"))
   188  	for _, key := range potentialKeys {
   189  		err := api.wallet.Load033xWallet(key, source)
   190  		if err == nil {
   191  			WriteSuccess(w)
   192  			return
   193  		}
   194  		if err != nil && err != modules.ErrBadEncryptionKey {
   195  			WriteError(w, Error{"error when calling /wallet/033x: " + err.Error()}, http.StatusBadRequest)
   196  			return
   197  		}
   198  	}
   199  	WriteError(w, Error{modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest)
   200  }
   201  
   202  // walletAddressHandler handles API calls to /wallet/address.
   203  func (api *API) walletAddressHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   204  	unlockConditions, err := api.wallet.NextAddress()
   205  	if err != nil {
   206  		WriteError(w, Error{"error when calling /wallet/addresses: " + err.Error()}, http.StatusBadRequest)
   207  		return
   208  	}
   209  	WriteJSON(w, WalletAddressGET{
   210  		Address: unlockConditions.UnlockHash(),
   211  	})
   212  }
   213  
   214  // walletAddressHandler handles API calls to /wallet/addresses.
   215  func (api *API) walletAddressesHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   216  	addresses, err := api.wallet.AllAddresses()
   217  	if err != nil {
   218  		WriteError(w, Error{fmt.Sprintf("Error when calling /wallet/addresses: %v", err)}, http.StatusBadRequest)
   219  		return
   220  	}
   221  	WriteJSON(w, WalletAddressesGET{
   222  		Addresses: addresses,
   223  	})
   224  }
   225  
   226  // walletBackupHandler handles API calls to /wallet/backup.
   227  func (api *API) walletBackupHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   228  	destination := req.FormValue("destination")
   229  	// Check that the destination is absolute.
   230  	if !filepath.IsAbs(destination) {
   231  		WriteError(w, Error{"error when calling /wallet/backup: destination must be an absolute path"}, http.StatusBadRequest)
   232  		return
   233  	}
   234  	err := api.wallet.CreateBackup(destination)
   235  	if err != nil {
   236  		WriteError(w, Error{"error when calling /wallet/backup: " + err.Error()}, http.StatusBadRequest)
   237  		return
   238  	}
   239  	WriteSuccess(w)
   240  }
   241  
   242  // walletInitHandler handles API calls to /wallet/init.
   243  func (api *API) walletInitHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   244  	var encryptionKey crypto.TwofishKey
   245  	if req.FormValue("encryptionpassword") != "" {
   246  		encryptionKey = crypto.TwofishKey(crypto.HashObject(req.FormValue("encryptionpassword")))
   247  	}
   248  
   249  	if req.FormValue("force") == "true" {
   250  		err := api.wallet.Reset()
   251  		if err != nil {
   252  			WriteError(w, Error{"error when calling /wallet/init: " + err.Error()}, http.StatusBadRequest)
   253  			return
   254  		}
   255  	}
   256  	seed, err := api.wallet.Encrypt(encryptionKey)
   257  	if err != nil {
   258  		WriteError(w, Error{"error when calling /wallet/init: " + err.Error()}, http.StatusBadRequest)
   259  		return
   260  	}
   261  
   262  	dictID := mnemonics.DictionaryID(req.FormValue("dictionary"))
   263  	if dictID == "" {
   264  		dictID = "english"
   265  	}
   266  	seedStr, err := modules.SeedToString(seed, dictID)
   267  	if err != nil {
   268  		WriteError(w, Error{"error when calling /wallet/init: " + err.Error()}, http.StatusBadRequest)
   269  		return
   270  	}
   271  	WriteJSON(w, WalletInitPOST{
   272  		PrimarySeed: seedStr,
   273  	})
   274  }
   275  
   276  // walletInitSeedHandler handles API calls to /wallet/init/seed.
   277  func (api *API) walletInitSeedHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   278  	var encryptionKey crypto.TwofishKey
   279  	if req.FormValue("encryptionpassword") != "" {
   280  		encryptionKey = crypto.TwofishKey(crypto.HashObject(req.FormValue("encryptionpassword")))
   281  	}
   282  	dictID := mnemonics.DictionaryID(req.FormValue("dictionary"))
   283  	if dictID == "" {
   284  		dictID = "english"
   285  	}
   286  	seed, err := modules.StringToSeed(req.FormValue("seed"), dictID)
   287  	if err != nil {
   288  		WriteError(w, Error{"error when calling /wallet/init/seed: " + err.Error()}, http.StatusBadRequest)
   289  		return
   290  	}
   291  
   292  	if req.FormValue("force") == "true" {
   293  		err = api.wallet.Reset()
   294  		if err != nil {
   295  			WriteError(w, Error{"error when calling /wallet/init/seed: " + err.Error()}, http.StatusBadRequest)
   296  			return
   297  		}
   298  	}
   299  
   300  	err = api.wallet.InitFromSeed(encryptionKey, seed)
   301  	if err != nil {
   302  		WriteError(w, Error{"error when calling /wallet/init/seed: " + err.Error()}, http.StatusBadRequest)
   303  		return
   304  	}
   305  	WriteSuccess(w)
   306  }
   307  
   308  // walletSeedHandler handles API calls to /wallet/seed.
   309  func (api *API) walletSeedHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   310  	// Get the seed using the ditionary + phrase
   311  	dictID := mnemonics.DictionaryID(req.FormValue("dictionary"))
   312  	if dictID == "" {
   313  		dictID = "english"
   314  	}
   315  	seed, err := modules.StringToSeed(req.FormValue("seed"), dictID)
   316  	if err != nil {
   317  		WriteError(w, Error{"error when calling /wallet/seed: " + err.Error()}, http.StatusBadRequest)
   318  		return
   319  	}
   320  
   321  	potentialKeys := encryptionKeys(req.FormValue("encryptionpassword"))
   322  	for _, key := range potentialKeys {
   323  		err := api.wallet.LoadSeed(key, seed)
   324  		if err == nil {
   325  			WriteSuccess(w)
   326  			return
   327  		}
   328  		if err != nil && err != modules.ErrBadEncryptionKey {
   329  			WriteError(w, Error{"error when calling /wallet/seed: " + err.Error()}, http.StatusBadRequest)
   330  			return
   331  		}
   332  	}
   333  	WriteError(w, Error{"error when calling /wallet/seed: " + modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest)
   334  }
   335  
   336  // walletSiagkeyHandler handles API calls to /wallet/siagkey.
   337  func (api *API) walletSiagkeyHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   338  	// Fetch the list of keyfiles from the post body.
   339  	keyfiles := strings.Split(req.FormValue("keyfiles"), ",")
   340  	potentialKeys := encryptionKeys(req.FormValue("encryptionpassword"))
   341  
   342  	for _, keypath := range keyfiles {
   343  		// Check that all key paths are absolute paths.
   344  		if !filepath.IsAbs(keypath) {
   345  			WriteError(w, Error{"error when calling /wallet/siagkey: keyfiles contains a non-absolute path"}, http.StatusBadRequest)
   346  			return
   347  		}
   348  	}
   349  
   350  	for _, key := range potentialKeys {
   351  		err := api.wallet.LoadSiagKeys(key, keyfiles)
   352  		if err == nil {
   353  			WriteSuccess(w)
   354  			return
   355  		}
   356  		if err != nil && err != modules.ErrBadEncryptionKey {
   357  			WriteError(w, Error{"error when calling /wallet/siagkey: " + err.Error()}, http.StatusBadRequest)
   358  			return
   359  		}
   360  	}
   361  	WriteError(w, Error{"error when calling /wallet/siagkey: " + modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest)
   362  }
   363  
   364  // walletLockHanlder handles API calls to /wallet/lock.
   365  func (api *API) walletLockHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   366  	err := api.wallet.Lock()
   367  	if err != nil {
   368  		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
   369  		return
   370  	}
   371  	WriteSuccess(w)
   372  }
   373  
   374  // walletSeedsHandler handles API calls to /wallet/seeds.
   375  func (api *API) walletSeedsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   376  	dictionary := mnemonics.DictionaryID(req.FormValue("dictionary"))
   377  	if dictionary == "" {
   378  		dictionary = mnemonics.English
   379  	}
   380  
   381  	// Get the primary seed information.
   382  	primarySeed, addrsRemaining, err := api.wallet.PrimarySeed()
   383  	if err != nil {
   384  		WriteError(w, Error{"error when calling /wallet/seeds: " + err.Error()}, http.StatusBadRequest)
   385  		return
   386  	}
   387  	primarySeedStr, err := modules.SeedToString(primarySeed, dictionary)
   388  	if err != nil {
   389  		WriteError(w, Error{"error when calling /wallet/seeds: " + err.Error()}, http.StatusBadRequest)
   390  		return
   391  	}
   392  
   393  	// Get the list of seeds known to the wallet.
   394  	allSeeds, err := api.wallet.AllSeeds()
   395  	if err != nil {
   396  		WriteError(w, Error{"error when calling /wallet/seeds: " + err.Error()}, http.StatusBadRequest)
   397  		return
   398  	}
   399  	var allSeedsStrs []string
   400  	for _, seed := range allSeeds {
   401  		str, err := modules.SeedToString(seed, dictionary)
   402  		if err != nil {
   403  			WriteError(w, Error{"error when calling /wallet/seeds: " + err.Error()}, http.StatusBadRequest)
   404  			return
   405  		}
   406  		allSeedsStrs = append(allSeedsStrs, str)
   407  	}
   408  	WriteJSON(w, WalletSeedsGET{
   409  		PrimarySeed:        primarySeedStr,
   410  		AddressesRemaining: int(addrsRemaining),
   411  		AllSeeds:           allSeedsStrs,
   412  	})
   413  }
   414  
   415  // walletSiacoinsHandler handles API calls to /wallet/siacoins.
   416  func (api *API) walletSiacoinsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   417  	var txns []types.Transaction
   418  	if req.FormValue("outputs") != "" {
   419  		// multiple amounts + destinations
   420  		if req.FormValue("amount") != "" || req.FormValue("destination") != "" {
   421  			WriteError(w, Error{"cannot supply both 'outputs' and single amount+destination pair"}, http.StatusInternalServerError)
   422  			return
   423  		}
   424  
   425  		var outputs []types.SiacoinOutput
   426  		err := json.Unmarshal([]byte(req.FormValue("outputs")), &outputs)
   427  		if err != nil {
   428  			WriteError(w, Error{"could not decode outputs: " + err.Error()}, http.StatusInternalServerError)
   429  			return
   430  		}
   431  		txns, err = api.wallet.SendSiacoinsMulti(outputs)
   432  		if err != nil {
   433  			WriteError(w, Error{"error when calling /wallet/siacoins: " + err.Error()}, http.StatusInternalServerError)
   434  			return
   435  		}
   436  	} else {
   437  		// single amount + destination
   438  		amount, ok := scanAmount(req.FormValue("amount"))
   439  		if !ok {
   440  			WriteError(w, Error{"could not read amount from POST call to /wallet/siacoins"}, http.StatusBadRequest)
   441  			return
   442  		}
   443  		dest, err := scanAddress(req.FormValue("destination"))
   444  		if err != nil {
   445  			WriteError(w, Error{"could not read address from POST call to /wallet/siacoins"}, http.StatusBadRequest)
   446  			return
   447  		}
   448  
   449  		txns, err = api.wallet.SendSiacoins(amount, dest)
   450  		if err != nil {
   451  			WriteError(w, Error{"error when calling /wallet/siacoins: " + err.Error()}, http.StatusInternalServerError)
   452  			return
   453  		}
   454  
   455  	}
   456  
   457  	var txids []types.TransactionID
   458  	for _, txn := range txns {
   459  		txids = append(txids, txn.ID())
   460  	}
   461  	WriteJSON(w, WalletSiacoinsPOST{
   462  		TransactionIDs: txids,
   463  	})
   464  }
   465  
   466  // walletSiafundsHandler handles API calls to /wallet/siafunds.
   467  func (api *API) walletSiafundsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   468  	amount, ok := scanAmount(req.FormValue("amount"))
   469  	if !ok {
   470  		WriteError(w, Error{"could not read 'amount' from POST call to /wallet/siafunds"}, http.StatusBadRequest)
   471  		return
   472  	}
   473  	dest, err := scanAddress(req.FormValue("destination"))
   474  	if err != nil {
   475  		WriteError(w, Error{"error when calling /wallet/siafunds: " + err.Error()}, http.StatusBadRequest)
   476  		return
   477  	}
   478  
   479  	txns, err := api.wallet.SendSiafunds(amount, dest)
   480  	if err != nil {
   481  		WriteError(w, Error{"error when calling /wallet/siafunds: " + err.Error()}, http.StatusInternalServerError)
   482  		return
   483  	}
   484  	var txids []types.TransactionID
   485  	for _, txn := range txns {
   486  		txids = append(txids, txn.ID())
   487  	}
   488  	WriteJSON(w, WalletSiafundsPOST{
   489  		TransactionIDs: txids,
   490  	})
   491  }
   492  
   493  // walletSweepSeedHandler handles API calls to /wallet/sweep/seed.
   494  func (api *API) walletSweepSeedHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   495  	// Get the seed using the ditionary + phrase
   496  	dictID := mnemonics.DictionaryID(req.FormValue("dictionary"))
   497  	if dictID == "" {
   498  		dictID = "english"
   499  	}
   500  	seed, err := modules.StringToSeed(req.FormValue("seed"), dictID)
   501  	if err != nil {
   502  		WriteError(w, Error{"error when calling /wallet/sweep/seed: " + err.Error()}, http.StatusBadRequest)
   503  		return
   504  	}
   505  
   506  	coins, funds, err := api.wallet.SweepSeed(seed)
   507  	if err != nil {
   508  		WriteError(w, Error{"error when calling /wallet/sweep/seed: " + err.Error()}, http.StatusBadRequest)
   509  		return
   510  	}
   511  	WriteJSON(w, WalletSweepPOST{
   512  		Coins: coins,
   513  		Funds: funds,
   514  	})
   515  }
   516  
   517  // walletTransactionHandler handles API calls to /wallet/transaction/:id.
   518  func (api *API) walletTransactionHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
   519  	// Parse the id from the url.
   520  	var id types.TransactionID
   521  	jsonID := "\"" + ps.ByName("id") + "\""
   522  	err := id.UnmarshalJSON([]byte(jsonID))
   523  	if err != nil {
   524  		WriteError(w, Error{"error when calling /wallet/transaction/id:" + err.Error()}, http.StatusBadRequest)
   525  		return
   526  	}
   527  
   528  	txn, ok, err := api.wallet.Transaction(id)
   529  	if err != nil {
   530  		WriteError(w, Error{"error when calling /wallet/transaction/id:" + err.Error()}, http.StatusBadRequest)
   531  		return
   532  	}
   533  	if !ok {
   534  		WriteError(w, Error{"error when calling /wallet/transaction/:id  :  transaction not found"}, http.StatusBadRequest)
   535  		return
   536  	}
   537  	WriteJSON(w, WalletTransactionGETid{
   538  		Transaction: txn,
   539  	})
   540  }
   541  
   542  // walletTransactionsHandler handles API calls to /wallet/transactions.
   543  func (api *API) walletTransactionsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   544  	startheightStr, endheightStr := req.FormValue("startheight"), req.FormValue("endheight")
   545  	if startheightStr == "" || endheightStr == "" {
   546  		WriteError(w, Error{"startheight and endheight must be provided to a /wallet/transactions call."}, http.StatusBadRequest)
   547  		return
   548  	}
   549  	// Get the start and end blocks.
   550  	start, err := strconv.ParseUint(startheightStr, 10, 64)
   551  	if err != nil {
   552  		WriteError(w, Error{"parsing integer value for parameter `startheight` failed: " + err.Error()}, http.StatusBadRequest)
   553  		return
   554  	}
   555  	// Check if endheightStr is set to -1. If it is, we use MaxUint64 as the
   556  	// end. Otherwise we parse the argument as an unsigned integer.
   557  	var end uint64
   558  	if endheightStr == "-1" {
   559  		end = math.MaxUint64
   560  	} else {
   561  		end, err = strconv.ParseUint(endheightStr, 10, 64)
   562  	}
   563  	if err != nil {
   564  		WriteError(w, Error{"parsing integer value for parameter `endheight` failed: " + err.Error()}, http.StatusBadRequest)
   565  		return
   566  	}
   567  	confirmedTxns, err := api.wallet.Transactions(types.BlockHeight(start), types.BlockHeight(end))
   568  	if err != nil {
   569  		WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest)
   570  		return
   571  	}
   572  	unconfirmedTxns, err := api.wallet.UnconfirmedTransactions()
   573  	if err != nil {
   574  		WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest)
   575  		return
   576  	}
   577  
   578  	WriteJSON(w, WalletTransactionsGET{
   579  		ConfirmedTransactions:   confirmedTxns,
   580  		UnconfirmedTransactions: unconfirmedTxns,
   581  	})
   582  }
   583  
   584  // walletTransactionsAddrHandler handles API calls to
   585  // /wallet/transactions/:addr.
   586  func (api *API) walletTransactionsAddrHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
   587  	// Parse the address being input.
   588  	jsonAddr := "\"" + ps.ByName("addr") + "\""
   589  	var addr types.UnlockHash
   590  	err := addr.UnmarshalJSON([]byte(jsonAddr))
   591  	if err != nil {
   592  		WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest)
   593  		return
   594  	}
   595  
   596  	confirmedATs, err := api.wallet.AddressTransactions(addr)
   597  	if err != nil {
   598  		WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest)
   599  		return
   600  	}
   601  	unconfirmedATs, err := api.wallet.AddressUnconfirmedTransactions(addr)
   602  	if err != nil {
   603  		WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest)
   604  		return
   605  	}
   606  	WriteJSON(w, WalletTransactionsGETaddr{
   607  		ConfirmedTransactions:   confirmedATs,
   608  		UnconfirmedTransactions: unconfirmedATs,
   609  	})
   610  }
   611  
   612  // walletUnlockHandler handles API calls to /wallet/unlock.
   613  func (api *API) walletUnlockHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   614  	potentialKeys := encryptionKeys(req.FormValue("encryptionpassword"))
   615  	for _, key := range potentialKeys {
   616  		err := api.wallet.Unlock(key)
   617  		if err == nil {
   618  			WriteSuccess(w)
   619  			return
   620  		}
   621  		if err != nil && err != modules.ErrBadEncryptionKey {
   622  			WriteError(w, Error{"error when calling /wallet/unlock: " + err.Error()}, http.StatusBadRequest)
   623  			return
   624  		}
   625  	}
   626  	WriteError(w, Error{"error when calling /wallet/unlock: " + modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest)
   627  }
   628  
   629  // walletChangePasswordHandler handles API calls to /wallet/changepassword
   630  func (api *API) walletChangePasswordHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
   631  	var newKey crypto.TwofishKey
   632  	newPassword := req.FormValue("newpassword")
   633  	if newPassword == "" {
   634  		WriteError(w, Error{"a password must be provided to newpassword"}, http.StatusBadRequest)
   635  		return
   636  	}
   637  	newKey = crypto.TwofishKey(crypto.HashObject(newPassword))
   638  
   639  	originalKeys := encryptionKeys(req.FormValue("encryptionpassword"))
   640  	for _, key := range originalKeys {
   641  		err := api.wallet.ChangeKey(key, newKey)
   642  		if err == nil {
   643  			WriteSuccess(w)
   644  			return
   645  		}
   646  		if err != nil && err != modules.ErrBadEncryptionKey {
   647  			WriteError(w, Error{"error when calling /wallet/changepassword: " + err.Error()}, http.StatusBadRequest)
   648  			return
   649  		}
   650  	}
   651  	WriteError(w, Error{"error when calling /wallet/changepassword: " + modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest)
   652  }
   653  
   654  // walletVerifyAddressHandler handles API calls to /wallet/verify/address/:addr.
   655  func (api *API) walletVerifyAddressHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
   656  	addrString := ps.ByName("addr")
   657  
   658  	err := new(types.UnlockHash).LoadString(addrString)
   659  	WriteJSON(w, WalletVerifyAddressGET{Valid: err == nil})
   660  }