github.com/fozzysec/SiaPrime@v0.0.0-20190612043147-66c8e8d11fe3/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 "SiaPrime/crypto" 13 "SiaPrime/modules" 14 "SiaPrime/types" 15 16 "github.com/julienschmidt/httprouter" 17 "gitlab.com/NebulousLabs/entropy-mnemonics" 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 // WalletSignPOSTParams contains the unsigned transaction and a set of 69 // inputs to sign. 70 WalletSignPOSTParams struct { 71 Transaction types.Transaction `json:"transaction"` 72 ToSign []crypto.Hash `json:"tosign"` 73 } 74 75 // WalletSignPOSTResp contains the signed transaction. 76 WalletSignPOSTResp struct { 77 Transaction types.Transaction `json:"transaction"` 78 } 79 80 // WalletSeedsGET contains the seeds used by the wallet. 81 WalletSeedsGET struct { 82 PrimarySeed string `json:"primaryseed"` 83 AddressesRemaining int `json:"addressesremaining"` 84 AllSeeds []string `json:"allseeds"` 85 } 86 87 // WalletSweepPOST contains the coins and funds returned by a call to 88 // /wallet/sweep. 89 WalletSweepPOST struct { 90 Coins types.Currency `json:"coins"` 91 Funds types.Currency `json:"funds"` 92 } 93 94 // WalletTransactionGETid contains the transaction returned by a call to 95 // /wallet/transaction/:id 96 WalletTransactionGETid struct { 97 Transaction modules.ProcessedTransaction `json:"transaction"` 98 } 99 100 // WalletTransactionsGET contains the specified set of confirmed and 101 // unconfirmed transactions. 102 WalletTransactionsGET struct { 103 ConfirmedTransactions []modules.ProcessedTransaction `json:"confirmedtransactions"` 104 UnconfirmedTransactions []modules.ProcessedTransaction `json:"unconfirmedtransactions"` 105 } 106 107 // WalletTransactionsGETaddr contains the set of wallet transactions 108 // relevant to the input address provided in the call to 109 // /wallet/transaction/:addr 110 WalletTransactionsGETaddr struct { 111 ConfirmedTransactions []modules.ProcessedTransaction `json:"confirmedtransactions"` 112 UnconfirmedTransactions []modules.ProcessedTransaction `json:"unconfirmedtransactions"` 113 } 114 115 // WalletUnlockConditionsGET contains a set of unlock conditions. 116 WalletUnlockConditionsGET struct { 117 UnlockConditions types.UnlockConditions `json:"unlockconditions"` 118 } 119 120 // WalletUnlockConditionsPOSTParams contains a set of unlock conditions. 121 WalletUnlockConditionsPOSTParams struct { 122 UnlockConditions types.UnlockConditions `json:"unlockconditions"` 123 } 124 125 // WalletUnspentGET contains the unspent outputs tracked by the wallet. 126 // The MaturityHeight field of each output indicates the height of the 127 // block that the output appeared in. 128 WalletUnspentGET struct { 129 Outputs []modules.UnspentOutput `json:"outputs"` 130 } 131 132 // WalletVerifyAddressGET contains a bool indicating if the address passed to 133 // /wallet/verify/address/:addr is a valid address. 134 WalletVerifyAddressGET struct { 135 Valid bool `json:"valid"` 136 } 137 138 // WalletWatchPOST contains the set of addresses to add or remove from the 139 // watch set. 140 WalletWatchPOST struct { 141 Addresses []types.UnlockHash `json:"addresses"` 142 Remove bool `json:"remove"` 143 Unused bool `json:"unused"` 144 } 145 146 // WalletWatchGET contains the set of addresses that the wallet is 147 // currently watching. 148 WalletWatchGET struct { 149 Addresses []types.UnlockHash `json:"addresses"` 150 } 151 ) 152 153 // encryptionKeys enumerates the possible encryption keys that can be derived 154 // from an input string. 155 func encryptionKeys(seedStr string) (validKeys []crypto.TwofishKey) { 156 dicts := []mnemonics.DictionaryID{"english", "german", "japanese"} 157 for _, dict := range dicts { 158 seed, err := modules.StringToSeed(seedStr, dict) 159 if err != nil { 160 continue 161 } 162 validKeys = append(validKeys, crypto.TwofishKey(crypto.HashObject(seed))) 163 } 164 validKeys = append(validKeys, crypto.TwofishKey(crypto.HashObject(seedStr))) 165 return validKeys 166 } 167 168 // walletHander handles API calls to /wallet. 169 func (api *API) walletHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 170 siacoinBal, siafundBal, siaclaimBal, err := api.wallet.ConfirmedBalance() 171 if err != nil { 172 WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest) 173 return 174 } 175 siacoinsOut, siacoinsIn, err := api.wallet.UnconfirmedBalance() 176 if err != nil { 177 WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest) 178 return 179 } 180 dustThreshold, err := api.wallet.DustThreshold() 181 if err != nil { 182 WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest) 183 return 184 } 185 encrypted, err := api.wallet.Encrypted() 186 if err != nil { 187 WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest) 188 return 189 } 190 unlocked, err := api.wallet.Unlocked() 191 if err != nil { 192 WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest) 193 return 194 } 195 rescanning, err := api.wallet.Rescanning() 196 if err != nil { 197 WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest) 198 return 199 } 200 height, err := api.wallet.Height() 201 if err != nil { 202 WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest) 203 return 204 } 205 WriteJSON(w, WalletGET{ 206 Encrypted: encrypted, 207 Unlocked: unlocked, 208 Rescanning: rescanning, 209 Height: height, 210 211 ConfirmedSiacoinBalance: siacoinBal, 212 UnconfirmedOutgoingSiacoins: siacoinsOut, 213 UnconfirmedIncomingSiacoins: siacoinsIn, 214 215 SiafundBalance: siafundBal, 216 SiacoinClaimBalance: siaclaimBal, 217 218 DustThreshold: dustThreshold, 219 }) 220 } 221 222 // wallet033xHandler handles API calls to /wallet/033x. 223 func (api *API) wallet033xHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 224 source := req.FormValue("source") 225 // Check that source is an absolute paths. 226 if !filepath.IsAbs(source) { 227 WriteError(w, Error{"error when calling /wallet/033x: source must be an absolute path"}, http.StatusBadRequest) 228 return 229 } 230 potentialKeys := encryptionKeys(req.FormValue("encryptionpassword")) 231 for _, key := range potentialKeys { 232 err := api.wallet.Load033xWallet(key, source) 233 if err == nil { 234 WriteSuccess(w) 235 return 236 } 237 if err != nil && err != modules.ErrBadEncryptionKey { 238 WriteError(w, Error{"error when calling /wallet/033x: " + err.Error()}, http.StatusBadRequest) 239 return 240 } 241 } 242 WriteError(w, Error{modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest) 243 } 244 245 // walletAddressHandler handles API calls to /wallet/address. 246 func (api *API) walletAddressHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 247 unlockConditions, err := api.wallet.NextAddress() 248 if err != nil { 249 WriteError(w, Error{"error when calling /wallet/addresses: " + err.Error()}, http.StatusBadRequest) 250 return 251 } 252 WriteJSON(w, WalletAddressGET{ 253 Address: unlockConditions.UnlockHash(), 254 }) 255 } 256 257 // walletAddressHandler handles API calls to /wallet/addresses. 258 func (api *API) walletAddressesHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 259 addresses, err := api.wallet.AllAddresses() 260 if err != nil { 261 WriteError(w, Error{fmt.Sprintf("Error when calling /wallet/addresses: %v", err)}, http.StatusBadRequest) 262 return 263 } 264 WriteJSON(w, WalletAddressesGET{ 265 Addresses: addresses, 266 }) 267 } 268 269 // walletBackupHandler handles API calls to /wallet/backup. 270 func (api *API) walletBackupHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 271 destination := req.FormValue("destination") 272 // Check that the destination is absolute. 273 if !filepath.IsAbs(destination) { 274 WriteError(w, Error{"error when calling /wallet/backup: destination must be an absolute path"}, http.StatusBadRequest) 275 return 276 } 277 err := api.wallet.CreateBackup(destination) 278 if err != nil { 279 WriteError(w, Error{"error when calling /wallet/backup: " + err.Error()}, http.StatusBadRequest) 280 return 281 } 282 WriteSuccess(w) 283 } 284 285 // walletInitHandler handles API calls to /wallet/init. 286 func (api *API) walletInitHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 287 var encryptionKey crypto.TwofishKey 288 if req.FormValue("encryptionpassword") != "" { 289 encryptionKey = crypto.TwofishKey(crypto.HashObject(req.FormValue("encryptionpassword"))) 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: " + err.Error()}, http.StatusBadRequest) 296 return 297 } 298 } 299 seed, err := api.wallet.Encrypt(encryptionKey) 300 if err != nil { 301 WriteError(w, Error{"error when calling /wallet/init: " + err.Error()}, http.StatusBadRequest) 302 return 303 } 304 305 dictID := mnemonics.DictionaryID(req.FormValue("dictionary")) 306 if dictID == "" { 307 dictID = "english" 308 } 309 seedStr, err := modules.SeedToString(seed, dictID) 310 if err != nil { 311 WriteError(w, Error{"error when calling /wallet/init: " + err.Error()}, http.StatusBadRequest) 312 return 313 } 314 WriteJSON(w, WalletInitPOST{ 315 PrimarySeed: seedStr, 316 }) 317 } 318 319 // walletInitSeedHandler handles API calls to /wallet/init/seed. 320 func (api *API) walletInitSeedHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 321 var encryptionKey crypto.TwofishKey 322 if req.FormValue("encryptionpassword") != "" { 323 encryptionKey = crypto.TwofishKey(crypto.HashObject(req.FormValue("encryptionpassword"))) 324 } 325 dictID := mnemonics.DictionaryID(req.FormValue("dictionary")) 326 if dictID == "" { 327 dictID = "english" 328 } 329 seed, err := modules.StringToSeed(req.FormValue("seed"), dictID) 330 if err != nil { 331 WriteError(w, Error{"error when calling /wallet/init/seed: " + err.Error()}, http.StatusBadRequest) 332 return 333 } 334 335 if req.FormValue("force") == "true" { 336 err = api.wallet.Reset() 337 if err != nil { 338 WriteError(w, Error{"error when calling /wallet/init/seed: " + err.Error()}, http.StatusBadRequest) 339 return 340 } 341 } 342 343 err = api.wallet.InitFromSeed(encryptionKey, seed) 344 if err != nil { 345 WriteError(w, Error{"error when calling /wallet/init/seed: " + err.Error()}, http.StatusBadRequest) 346 return 347 } 348 WriteSuccess(w) 349 } 350 351 // walletSeedHandler handles API calls to /wallet/seed. 352 func (api *API) walletSeedHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 353 // Get the seed using the ditionary + phrase 354 dictID := mnemonics.DictionaryID(req.FormValue("dictionary")) 355 if dictID == "" { 356 dictID = "english" 357 } 358 seed, err := modules.StringToSeed(req.FormValue("seed"), dictID) 359 if err != nil { 360 WriteError(w, Error{"error when calling /wallet/seed: " + err.Error()}, http.StatusBadRequest) 361 return 362 } 363 364 potentialKeys := encryptionKeys(req.FormValue("encryptionpassword")) 365 for _, key := range potentialKeys { 366 err := api.wallet.LoadSeed(key, seed) 367 if err == nil { 368 WriteSuccess(w) 369 return 370 } 371 if err != nil && err != modules.ErrBadEncryptionKey { 372 WriteError(w, Error{"error when calling /wallet/seed: " + err.Error()}, http.StatusBadRequest) 373 return 374 } 375 } 376 WriteError(w, Error{"error when calling /wallet/seed: " + modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest) 377 } 378 379 // walletSiagkeyHandler handles API calls to /wallet/siagkey. 380 func (api *API) walletSiagkeyHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 381 // Fetch the list of keyfiles from the post body. 382 keyfiles := strings.Split(req.FormValue("keyfiles"), ",") 383 potentialKeys := encryptionKeys(req.FormValue("encryptionpassword")) 384 385 for _, keypath := range keyfiles { 386 // Check that all key paths are absolute paths. 387 if !filepath.IsAbs(keypath) { 388 WriteError(w, Error{"error when calling /wallet/siagkey: keyfiles contains a non-absolute path"}, http.StatusBadRequest) 389 return 390 } 391 } 392 393 for _, key := range potentialKeys { 394 err := api.wallet.LoadSiagKeys(key, keyfiles) 395 if err == nil { 396 WriteSuccess(w) 397 return 398 } 399 if err != nil && err != modules.ErrBadEncryptionKey { 400 WriteError(w, Error{"error when calling /wallet/siagkey: " + err.Error()}, http.StatusBadRequest) 401 return 402 } 403 } 404 WriteError(w, Error{"error when calling /wallet/siagkey: " + modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest) 405 } 406 407 // walletLockHanlder handles API calls to /wallet/lock. 408 func (api *API) walletLockHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 409 err := api.wallet.Lock() 410 if err != nil { 411 WriteError(w, Error{err.Error()}, http.StatusBadRequest) 412 return 413 } 414 WriteSuccess(w) 415 } 416 417 // walletSeedsHandler handles API calls to /wallet/seeds. 418 func (api *API) walletSeedsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 419 dictionary := mnemonics.DictionaryID(req.FormValue("dictionary")) 420 if dictionary == "" { 421 dictionary = mnemonics.English 422 } 423 424 // Get the primary seed information. 425 primarySeed, addrsRemaining, err := api.wallet.PrimarySeed() 426 if err != nil { 427 WriteError(w, Error{"error when calling /wallet/seeds: " + err.Error()}, http.StatusBadRequest) 428 return 429 } 430 primarySeedStr, err := modules.SeedToString(primarySeed, dictionary) 431 if err != nil { 432 WriteError(w, Error{"error when calling /wallet/seeds: " + err.Error()}, http.StatusBadRequest) 433 return 434 } 435 436 // Get the list of seeds known to the wallet. 437 allSeeds, err := api.wallet.AllSeeds() 438 if err != nil { 439 WriteError(w, Error{"error when calling /wallet/seeds: " + err.Error()}, http.StatusBadRequest) 440 return 441 } 442 var allSeedsStrs []string 443 for _, seed := range allSeeds { 444 str, err := modules.SeedToString(seed, dictionary) 445 if err != nil { 446 WriteError(w, Error{"error when calling /wallet/seeds: " + err.Error()}, http.StatusBadRequest) 447 return 448 } 449 allSeedsStrs = append(allSeedsStrs, str) 450 } 451 WriteJSON(w, WalletSeedsGET{ 452 PrimarySeed: primarySeedStr, 453 AddressesRemaining: int(addrsRemaining), 454 AllSeeds: allSeedsStrs, 455 }) 456 } 457 458 // walletSiacoinsHandler handles API calls to /wallet/siacoins. 459 func (api *API) walletSiacoinsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 460 var txns []types.Transaction 461 if req.FormValue("outputs") != "" { 462 // multiple amounts + destinations 463 if req.FormValue("amount") != "" || req.FormValue("destination") != "" { 464 WriteError(w, Error{"cannot supply both 'outputs' and single amount+destination pair"}, http.StatusInternalServerError) 465 return 466 } 467 468 var outputs []types.SiacoinOutput 469 err := json.Unmarshal([]byte(req.FormValue("outputs")), &outputs) 470 if err != nil { 471 WriteError(w, Error{"could not decode outputs: " + err.Error()}, http.StatusInternalServerError) 472 return 473 } 474 txns, err = api.wallet.SendSiacoinsMulti(outputs) 475 if err != nil { 476 WriteError(w, Error{"error when calling /wallet/siacoins: " + err.Error()}, http.StatusInternalServerError) 477 return 478 } 479 } else { 480 // single amount + destination 481 amount, ok := scanAmount(req.FormValue("amount")) 482 if !ok { 483 WriteError(w, Error{"could not read amount from POST call to /wallet/siacoins"}, http.StatusBadRequest) 484 return 485 } 486 dest, err := scanAddress(req.FormValue("destination")) 487 if err != nil { 488 WriteError(w, Error{"could not read address from POST call to /wallet/siacoins"}, http.StatusBadRequest) 489 return 490 } 491 492 txns, err = api.wallet.SendSiacoins(amount, dest) 493 if err != nil { 494 WriteError(w, Error{"error when calling /wallet/siacoins: " + err.Error()}, http.StatusInternalServerError) 495 return 496 } 497 498 } 499 500 var txids []types.TransactionID 501 for _, txn := range txns { 502 txids = append(txids, txn.ID()) 503 } 504 WriteJSON(w, WalletSiacoinsPOST{ 505 TransactionIDs: txids, 506 }) 507 } 508 509 // walletSiafundsHandler handles API calls to /wallet/siafunds. 510 func (api *API) walletSiafundsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 511 amount, ok := scanAmount(req.FormValue("amount")) 512 if !ok { 513 WriteError(w, Error{"could not read 'amount' from POST call to /wallet/siafunds"}, http.StatusBadRequest) 514 return 515 } 516 dest, err := scanAddress(req.FormValue("destination")) 517 if err != nil { 518 WriteError(w, Error{"error when calling /wallet/siafunds: " + err.Error()}, http.StatusBadRequest) 519 return 520 } 521 522 txns, err := api.wallet.SendSiafunds(amount, dest) 523 if err != nil { 524 WriteError(w, Error{"error when calling /wallet/siafunds: " + err.Error()}, http.StatusInternalServerError) 525 return 526 } 527 var txids []types.TransactionID 528 for _, txn := range txns { 529 txids = append(txids, txn.ID()) 530 } 531 WriteJSON(w, WalletSiafundsPOST{ 532 TransactionIDs: txids, 533 }) 534 } 535 536 // walletSweepSeedHandler handles API calls to /wallet/sweep/seed. 537 func (api *API) walletSweepSeedHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 538 // Get the seed using the ditionary + phrase 539 dictID := mnemonics.DictionaryID(req.FormValue("dictionary")) 540 if dictID == "" { 541 dictID = "english" 542 } 543 seed, err := modules.StringToSeed(req.FormValue("seed"), dictID) 544 if err != nil { 545 WriteError(w, Error{"error when calling /wallet/sweep/seed: " + err.Error()}, http.StatusBadRequest) 546 return 547 } 548 549 coins, funds, err := api.wallet.SweepSeed(seed) 550 if err != nil { 551 WriteError(w, Error{"error when calling /wallet/sweep/seed: " + err.Error()}, http.StatusBadRequest) 552 return 553 } 554 WriteJSON(w, WalletSweepPOST{ 555 Coins: coins, 556 Funds: funds, 557 }) 558 } 559 560 // walletTransactionHandler handles API calls to /wallet/transaction/:id. 561 func (api *API) walletTransactionHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { 562 // Parse the id from the url. 563 var id types.TransactionID 564 jsonID := "\"" + ps.ByName("id") + "\"" 565 err := id.UnmarshalJSON([]byte(jsonID)) 566 if err != nil { 567 WriteError(w, Error{"error when calling /wallet/transaction/id:" + err.Error()}, http.StatusBadRequest) 568 return 569 } 570 571 txn, ok, err := api.wallet.Transaction(id) 572 if err != nil { 573 WriteError(w, Error{"error when calling /wallet/transaction/id:" + err.Error()}, http.StatusBadRequest) 574 return 575 } 576 if !ok { 577 WriteError(w, Error{"error when calling /wallet/transaction/:id : transaction not found"}, http.StatusBadRequest) 578 return 579 } 580 WriteJSON(w, WalletTransactionGETid{ 581 Transaction: txn, 582 }) 583 } 584 585 // walletTransactionsHandler handles API calls to /wallet/transactions. 586 func (api *API) walletTransactionsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 587 startheightStr, endheightStr, depthStr := req.FormValue("startheight"), req.FormValue("endheight"), req.FormValue("depth") 588 var start, end, depth uint64 589 var err error 590 if depthStr == "" { 591 if startheightStr == "" || endheightStr == "" { 592 WriteError(w, Error{"startheight and endheight must be provided to a /wallet/transactions call if depth is unspecified."}, http.StatusBadRequest) 593 return 594 } 595 // Get the start and end blocks. 596 start, err = strconv.ParseUint(startheightStr, 10, 64) 597 if err != nil { 598 WriteError(w, Error{"parsing integer value for parameter `startheight` failed: " + err.Error()}, http.StatusBadRequest) 599 return 600 } 601 // Check if endheightStr is set to -1. If it is, we use MaxUint64 as the 602 // end. Otherwise we parse the argument as an unsigned integer. 603 if endheightStr == "-1" { 604 end = math.MaxUint64 605 } else { 606 end, err = strconv.ParseUint(endheightStr, 10, 64) 607 } 608 if err != nil { 609 WriteError(w, Error{"parsing integer value for parameter `endheight` failed: " + err.Error()}, http.StatusBadRequest) 610 return 611 } 612 } else { 613 if startheightStr != "" || endheightStr != "" { 614 WriteError(w, Error{"startheight and endheight must not be provided to a /wallet/transactions call if depth is specified."}, http.StatusBadRequest) 615 return 616 } 617 // Get the start and end blocks by looking backwards from our current height. 618 depth, err = strconv.ParseUint(depthStr, 10, 64) 619 if err != nil { 620 WriteError(w, Error{"parsing integer value for parameter `depth` failed: " + err.Error()}, http.StatusBadRequest) 621 return 622 } 623 height, err := api.wallet.Height() 624 if err != nil { 625 WriteError(w, Error{fmt.Sprintf("Error when calling /wallet: %v", err)}, http.StatusBadRequest) 626 return 627 } 628 end = uint64(height) 629 start = end - depth - 1 630 if start < 0 { 631 start = 0 632 } 633 } 634 confirmedTxns, err := api.wallet.Transactions(types.BlockHeight(start), types.BlockHeight(end)) 635 if err != nil { 636 WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest) 637 return 638 } 639 unconfirmedTxns, err := api.wallet.UnconfirmedTransactions() 640 if err != nil { 641 WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest) 642 return 643 } 644 645 WriteJSON(w, WalletTransactionsGET{ 646 ConfirmedTransactions: confirmedTxns, 647 UnconfirmedTransactions: unconfirmedTxns, 648 }) 649 } 650 651 // walletTransactionsAddrHandler handles API calls to 652 // /wallet/transactions/:addr. 653 func (api *API) walletTransactionsAddrHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { 654 // Parse the address being input. 655 jsonAddr := "\"" + ps.ByName("addr") + "\"" 656 var addr types.UnlockHash 657 err := addr.UnmarshalJSON([]byte(jsonAddr)) 658 if err != nil { 659 WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest) 660 return 661 } 662 663 confirmedATs, err := api.wallet.AddressTransactions(addr) 664 if err != nil { 665 WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest) 666 return 667 } 668 unconfirmedATs, err := api.wallet.AddressUnconfirmedTransactions(addr) 669 if err != nil { 670 WriteError(w, Error{"error when calling /wallet/transactions: " + err.Error()}, http.StatusBadRequest) 671 return 672 } 673 WriteJSON(w, WalletTransactionsGETaddr{ 674 ConfirmedTransactions: confirmedATs, 675 UnconfirmedTransactions: unconfirmedATs, 676 }) 677 } 678 679 // walletUnlockHandler handles API calls to /wallet/unlock. 680 func (api *API) walletUnlockHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 681 potentialKeys := encryptionKeys(req.FormValue("encryptionpassword")) 682 for _, key := range potentialKeys { 683 err := api.wallet.Unlock(key) 684 if err == nil { 685 WriteSuccess(w) 686 return 687 } 688 if err != nil && err != modules.ErrBadEncryptionKey { 689 WriteError(w, Error{"error when calling /wallet/unlock: " + err.Error()}, http.StatusBadRequest) 690 return 691 } 692 } 693 WriteError(w, Error{"error when calling /wallet/unlock: " + modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest) 694 } 695 696 // walletChangePasswordHandler handles API calls to /wallet/changepassword 697 func (api *API) walletChangePasswordHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 698 var newKey crypto.TwofishKey 699 newPassword := req.FormValue("newpassword") 700 if newPassword == "" { 701 WriteError(w, Error{"a password must be provided to newpassword"}, http.StatusBadRequest) 702 return 703 } 704 newKey = crypto.TwofishKey(crypto.HashObject(newPassword)) 705 706 originalKeys := encryptionKeys(req.FormValue("encryptionpassword")) 707 for _, key := range originalKeys { 708 err := api.wallet.ChangeKey(key, newKey) 709 if err == nil { 710 WriteSuccess(w) 711 return 712 } 713 if err != nil && err != modules.ErrBadEncryptionKey { 714 WriteError(w, Error{"error when calling /wallet/changepassword: " + err.Error()}, http.StatusBadRequest) 715 return 716 } 717 } 718 WriteError(w, Error{"error when calling /wallet/changepassword: " + modules.ErrBadEncryptionKey.Error()}, http.StatusBadRequest) 719 } 720 721 // walletVerifyAddressHandler handles API calls to /wallet/verify/address/:addr. 722 func (api *API) walletVerifyAddressHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { 723 addrString := ps.ByName("addr") 724 725 err := new(types.UnlockHash).LoadString(addrString) 726 WriteJSON(w, WalletVerifyAddressGET{Valid: err == nil}) 727 } 728 729 // walletUnlockConditionsHandlerGET handles GET calls to /wallet/unlockconditions. 730 func (api *API) walletUnlockConditionsHandlerGET(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { 731 var addr types.UnlockHash 732 err := addr.LoadString(ps.ByName("addr")) 733 if err != nil { 734 WriteError(w, Error{"error when calling /wallet/unlockconditions: " + err.Error()}, http.StatusBadRequest) 735 return 736 } 737 uc, err := api.wallet.UnlockConditions(addr) 738 if err != nil { 739 WriteError(w, Error{"error when calling /wallet/unlockconditions: " + err.Error()}, http.StatusBadRequest) 740 return 741 } 742 WriteJSON(w, WalletUnlockConditionsGET{ 743 UnlockConditions: uc, 744 }) 745 } 746 747 // walletUnlockConditionsHandlerPOST handles POST calls to /wallet/unlockconditions. 748 func (api *API) walletUnlockConditionsHandlerPOST(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { 749 var params WalletUnlockConditionsPOSTParams 750 err := json.NewDecoder(req.Body).Decode(¶ms) 751 if err != nil { 752 WriteError(w, Error{"invalid parameters: " + err.Error()}, http.StatusBadRequest) 753 return 754 } 755 err = api.wallet.AddUnlockConditions(params.UnlockConditions) 756 if err != nil { 757 WriteError(w, Error{"error when calling /wallet/unlockconditions: " + err.Error()}, http.StatusBadRequest) 758 return 759 } 760 WriteSuccess(w) 761 } 762 763 // walletUnspentHandler handles API calls to /wallet/unspent. 764 func (api *API) walletUnspentHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 765 outputs, err := api.wallet.UnspentOutputs() 766 if err != nil { 767 WriteError(w, Error{"error when calling /wallet/unspent: " + err.Error()}, http.StatusInternalServerError) 768 return 769 } 770 WriteJSON(w, WalletUnspentGET{ 771 Outputs: outputs, 772 }) 773 } 774 775 // walletSignHandler handles API calls to /wallet/sign. 776 func (api *API) walletSignHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 777 var params WalletSignPOSTParams 778 err := json.NewDecoder(req.Body).Decode(¶ms) 779 if err != nil { 780 WriteError(w, Error{"invalid parameters: " + err.Error()}, http.StatusBadRequest) 781 return 782 } 783 err = api.wallet.SignTransaction(¶ms.Transaction, params.ToSign) 784 if err != nil { 785 WriteError(w, Error{"failed to sign transaction: " + err.Error()}, http.StatusBadRequest) 786 return 787 } 788 WriteJSON(w, WalletSignPOSTResp{ 789 Transaction: params.Transaction, 790 }) 791 } 792 793 // walletWatchHandlerGET handles GET calls to /wallet/watch. 794 func (api *API) walletWatchHandlerGET(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 795 addrs, err := api.wallet.WatchAddresses() 796 if err != nil { 797 WriteError(w, Error{"failed to get watch addresses: " + err.Error()}, http.StatusBadRequest) 798 return 799 } 800 WriteJSON(w, WalletWatchGET{ 801 Addresses: addrs, 802 }) 803 } 804 805 // walletWatchHandlerPOST handles POST calls to /wallet/watch. 806 func (api *API) walletWatchHandlerPOST(w http.ResponseWriter, req *http.Request, _ httprouter.Params) { 807 var wwpp WalletWatchPOST 808 err := json.NewDecoder(req.Body).Decode(&wwpp) 809 if err != nil { 810 WriteError(w, Error{"invalid parameters: " + err.Error()}, http.StatusBadRequest) 811 return 812 } 813 if wwpp.Remove { 814 err = api.wallet.RemoveWatchAddresses(wwpp.Addresses, wwpp.Unused) 815 } else { 816 err = api.wallet.AddWatchAddresses(wwpp.Addresses, wwpp.Unused) 817 } 818 if err != nil { 819 WriteError(w, Error{"failed to update watch set: " + err.Error()}, http.StatusBadRequest) 820 return 821 } 822 WriteSuccess(w) 823 }