gitlab.com/jokerrs1/Sia@v1.3.2/node/api/wallet.go (about)

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