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 }