diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 871386c0b..da946229c 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -1,813 +1,853 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include static const std::string CHARS_ALPHA_NUM = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static const std::string SAFE_CHARS[] = { // SAFE_CHARS_DEFAULT CHARS_ALPHA_NUM + " .,;-_/:?@()", // SAFE_CHARS_UA_COMMENT CHARS_ALPHA_NUM + " .,;-_?@", // SAFE_CHARS_FILENAME CHARS_ALPHA_NUM + ".-_", }; std::string SanitizeString(const std::string &str, int rule) { std::string strResult; for (std::string::size_type i = 0; i < str.size(); i++) { if (SAFE_CHARS[rule].find(str[i]) != std::string::npos) { strResult.push_back(str[i]); } } return strResult; } const signed char p_util_hexdigit[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; signed char HexDigit(char c) { return p_util_hexdigit[(uint8_t)c]; } bool IsHex(const std::string &str) { for (std::string::const_iterator it(str.begin()); it != str.end(); ++it) { if (HexDigit(*it) < 0) { return false; } } return (str.size() > 0) && (str.size() % 2 == 0); } bool IsHexNumber(const std::string &str) { size_t starting_location = 0; if (str.size() > 2 && *str.begin() == '0' && *(str.begin() + 1) == 'x') { starting_location = 2; } for (auto c : str.substr(starting_location)) { if (HexDigit(c) < 0) { return false; } } // Return false for empty string or "0x". return (str.size() > starting_location); } std::vector ParseHex(const char *psz) { // convert hex dump to vector std::vector vch; while (true) { while (IsSpace(*psz)) { psz++; } signed char c = HexDigit(*psz++); if (c == (signed char)-1) { break; } uint8_t n = (c << 4); c = HexDigit(*psz++); if (c == (signed char)-1) { break; } n |= c; vch.push_back(n); } return vch; } std::vector ParseHex(const std::string &str) { return ParseHex(str.c_str()); } void SplitHostPort(std::string in, int &portOut, std::string &hostOut) { size_t colon = in.find_last_of(':'); // if a : is found, and it either follows a [...], or no other : is in the // string, treat it as port separator bool fHaveColon = colon != in.npos; bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and // in[0]=='[', colon is not 0, // so in[colon-1] is safe bool fMultiColon = fHaveColon && (in.find_last_of(':', colon - 1) != in.npos); if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) { int32_t n; if (ParseInt32(in.substr(colon + 1), &n) && n > 0 && n < 0x10000) { in = in.substr(0, colon); portOut = n; } } if (in.size() > 0 && in[0] == '[' && in[in.size() - 1] == ']') { hostOut = in.substr(1, in.size() - 2); } else { hostOut = in; } } std::string EncodeBase64(const uint8_t *pch, size_t len) { static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; std::string strRet; strRet.reserve((len + 2) / 3 * 4); int mode = 0, left = 0; const uint8_t *pchEnd = pch + len; while (pch < pchEnd) { int enc = *(pch++); switch (mode) { case 0: // we have no bits strRet += pbase64[enc >> 2]; left = (enc & 3) << 4; mode = 1; break; case 1: // we have two bits strRet += pbase64[left | (enc >> 4)]; left = (enc & 15) << 2; mode = 2; break; case 2: // we have four bits strRet += pbase64[left | (enc >> 6)]; strRet += pbase64[enc & 63]; mode = 0; break; } } if (mode) { strRet += pbase64[left]; strRet += '='; if (mode == 1) { strRet += '='; } } return strRet; } std::string EncodeBase64(const std::string &str) { return EncodeBase64((const uint8_t *)str.c_str(), str.size()); } std::vector DecodeBase64(const char *p, bool *pfInvalid) { static const int decode64_table[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; if (pfInvalid) { *pfInvalid = false; } std::vector vchRet; vchRet.reserve(strlen(p) * 3 / 4); int mode = 0; int left = 0; while (1) { int dec = decode64_table[(uint8_t)*p]; if (dec == -1) { break; } p++; switch (mode) { case 0: // we have no bits and get 6 left = dec; mode = 1; break; case 1: // we have 6 bits and keep 4 vchRet.push_back((left << 2) | (dec >> 4)); left = dec & 15; mode = 2; break; case 2: // we have 4 bits and get 6, we keep 2 vchRet.push_back((left << 4) | (dec >> 2)); left = dec & 3; mode = 3; break; case 3: // we have 2 bits and get 6 vchRet.push_back((left << 6) | dec); mode = 0; break; } } if (pfInvalid) { switch (mode) { case 0: // 4n base64 characters processed: ok break; case 1: // 4n+1 base64 character processed: impossible *pfInvalid = true; break; case 2: // 4n+2 base64 characters processed: require '==' if (left || p[0] != '=' || p[1] != '=' || decode64_table[(uint8_t)p[2]] != -1) { *pfInvalid = true; } break; case 3: // 4n+3 base64 characters processed: require '=' if (left || p[0] != '=' || decode64_table[(uint8_t)p[1]] != -1) { *pfInvalid = true; } break; } } return vchRet; } std::string DecodeBase64(const std::string &str) { std::vector vchRet = DecodeBase64(str.c_str()); return std::string((const char *)vchRet.data(), vchRet.size()); } std::string EncodeBase32(const uint8_t *pch, size_t len) { static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567"; std::string strRet; strRet.reserve((len + 4) / 5 * 8); int mode = 0, left = 0; const uint8_t *pchEnd = pch + len; while (pch < pchEnd) { int enc = *(pch++); switch (mode) { case 0: // we have no bits strRet += pbase32[enc >> 3]; left = (enc & 7) << 2; mode = 1; break; case 1: // we have three bits strRet += pbase32[left | (enc >> 6)]; strRet += pbase32[(enc >> 1) & 31]; left = (enc & 1) << 4; mode = 2; break; case 2: // we have one bit strRet += pbase32[left | (enc >> 4)]; left = (enc & 15) << 1; mode = 3; break; case 3: // we have four bits strRet += pbase32[left | (enc >> 7)]; strRet += pbase32[(enc >> 2) & 31]; left = (enc & 3) << 3; mode = 4; break; case 4: // we have two bits strRet += pbase32[left | (enc >> 5)]; strRet += pbase32[enc & 31]; mode = 0; } } static const int nPadding[5] = {0, 6, 4, 3, 1}; if (mode) { strRet += pbase32[left]; for (int n = 0; n < nPadding[mode]; n++) { strRet += '='; } } return strRet; } std::string EncodeBase32(const std::string &str) { return EncodeBase32((const uint8_t *)str.c_str(), str.size()); } std::vector DecodeBase32(const char *p, bool *pfInvalid) { static const int decode32_table[256] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; if (pfInvalid) { *pfInvalid = false; } std::vector vchRet; vchRet.reserve((strlen(p)) * 5 / 8); int mode = 0; int left = 0; while (1) { int dec = decode32_table[(uint8_t)*p]; if (dec == -1) { break; } p++; switch (mode) { case 0: // we have no bits and get 5 left = dec; mode = 1; break; case 1: // we have 5 bits and keep 2 vchRet.push_back((left << 3) | (dec >> 2)); left = dec & 3; mode = 2; break; case 2: // we have 2 bits and keep 7 left = left << 5 | dec; mode = 3; break; case 3: // we have 7 bits and keep 4 vchRet.push_back((left << 1) | (dec >> 4)); left = dec & 15; mode = 4; break; case 4: // we have 4 bits, and keep 1 vchRet.push_back((left << 4) | (dec >> 1)); left = dec & 1; mode = 5; break; case 5: // we have 1 bit, and keep 6 left = left << 5 | dec; mode = 6; break; case 6: // we have 6 bits, and keep 3 vchRet.push_back((left << 2) | (dec >> 3)); left = dec & 7; mode = 7; break; case 7: // we have 3 bits, and keep 0 vchRet.push_back((left << 5) | dec); mode = 0; break; } } if (pfInvalid) switch (mode) { case 0: // 8n base32 characters processed: ok break; case 1: // 8n+1 base32 characters processed: impossible case 3: // +3 case 6: // +6 *pfInvalid = true; break; case 2: // 8n+2 base32 characters processed: require '======' if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(uint8_t)p[6]] != -1) { *pfInvalid = true; } break; case 4: // 8n+4 base32 characters processed: require '====' if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(uint8_t)p[4]] != -1) { *pfInvalid = true; } break; case 5: // 8n+5 base32 characters processed: require '===' if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(uint8_t)p[3]] != -1) { *pfInvalid = true; } break; case 7: // 8n+7 base32 characters processed: require '=' if (left || p[0] != '=' || decode32_table[(uint8_t)p[1]] != -1) { *pfInvalid = true; } break; } return vchRet; } std::string DecodeBase32(const std::string &str) { std::vector vchRet = DecodeBase32(str.c_str()); return std::string((const char *)vchRet.data(), vchRet.size()); } static bool ParsePrechecks(const std::string &str) { // No empty string allowed if (str.empty()) { return false; } // No padding allowed if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size() - 1]))) { return false; } // No embedded NUL characters allowed if (str.size() != strlen(str.c_str())) { return false; } return true; } bool ParseInt32(const std::string &str, int32_t *out) { if (!ParsePrechecks(str)) { return false; } char *endp = nullptr; // strtol will not set errno if valid errno = 0; long int n = strtol(str.c_str(), &endp, 10); if (out) { *out = (int32_t)n; } // Note that strtol returns a *long int*, so even if strtol doesn't report // an over/underflow we still have to check that the returned value is // within the range of an *int32_t*. On 64-bit platforms the size of these // types may be different. return endp && *endp == 0 && !errno && n >= std::numeric_limits::min() && n <= std::numeric_limits::max(); } bool ParseInt64(const std::string &str, int64_t *out) { if (!ParsePrechecks(str)) { return false; } char *endp = nullptr; // strtoll will not set errno if valid errno = 0; long long int n = strtoll(str.c_str(), &endp, 10); if (out) { *out = (int64_t)n; } // Note that strtoll returns a *long long int*, so even if strtol doesn't // report an over/underflow we still have to check that the returned value // is within the range of an *int64_t*. return endp && *endp == 0 && !errno && n >= std::numeric_limits::min() && n <= std::numeric_limits::max(); } bool ParseUInt32(const std::string &str, uint32_t *out) { if (!ParsePrechecks(str)) { return false; } // Reject negative values, unfortunately strtoul accepts these by default if // they fit in the range if (str.size() >= 1 && str[0] == '-') { return false; } char *endp = nullptr; // strtoul will not set errno if valid errno = 0; unsigned long int n = strtoul(str.c_str(), &endp, 10); if (out) { *out = (uint32_t)n; } // Note that strtoul returns a *unsigned long int*, so even if it doesn't // report an over/underflow we still have to check that the returned value // is within the range of an *uint32_t*. On 64-bit platforms the size of // these types may be different. return endp && *endp == 0 && !errno && n <= std::numeric_limits::max(); } bool ParseUInt64(const std::string &str, uint64_t *out) { if (!ParsePrechecks(str)) { return false; } // Reject negative values, unfortunately strtoull accepts these by default // if they fit in the range if (str.size() >= 1 && str[0] == '-') { return false; } char *endp = nullptr; // strtoull will not set errno if valid errno = 0; unsigned long long int n = strtoull(str.c_str(), &endp, 10); if (out) { *out = (uint64_t)n; } // Note that strtoull returns a *unsigned long long int*, so even if it // doesn't report an over/underflow we still have to check that the returned // value is within the range of an *uint64_t*. return endp && *endp == 0 && !errno && n <= std::numeric_limits::max(); } bool ParseDouble(const std::string &str, double *out) { if (!ParsePrechecks(str)) { return false; } // No hexadecimal floats allowed if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') { return false; } std::istringstream text(str); text.imbue(std::locale::classic()); double result; text >> result; if (out) { *out = result; } return text.eof() && !text.fail(); } std::string FormatParagraph(const std::string &in, size_t width, size_t indent) { std::stringstream out; size_t ptr = 0; size_t indented = 0; while (ptr < in.size()) { size_t lineend = in.find_first_of('\n', ptr); if (lineend == std::string::npos) { lineend = in.size(); } const size_t linelen = lineend - ptr; const size_t rem_width = width - indented; if (linelen <= rem_width) { out << in.substr(ptr, linelen + 1); ptr = lineend + 1; indented = 0; } else { size_t finalspace = in.find_last_of(" \n", ptr + rem_width); if (finalspace == std::string::npos || finalspace < ptr) { // No place to break; just include the entire word and move on finalspace = in.find_first_of("\n ", ptr); if (finalspace == std::string::npos) { // End of the string, just add it and break out << in.substr(ptr); break; } } out << in.substr(ptr, finalspace - ptr) << "\n"; if (in[finalspace] == '\n') { indented = 0; } else if (indent) { out << std::string(indent, ' '); indented = indent; } ptr = finalspace + 1; } } return out.str(); } std::string i64tostr(int64_t n) { return strprintf("%d", n); } std::string itostr(int n) { return strprintf("%d", n); } int64_t atoi64(const char *psz) { #ifdef _MSC_VER return _atoi64(psz); #else return strtoll(psz, nullptr, 10); #endif } int64_t atoi64(const std::string &str) { #ifdef _MSC_VER return _atoi64(str.c_str()); #else return strtoll(str.c_str(), nullptr, 10); #endif } int atoi(const std::string &str) { return atoi(str.c_str()); } /** * Upper bound for mantissa. * 10^18-1 is the largest arbitrary decimal that will fit in a signed 64-bit * integer. Larger integers cannot consist of arbitrary combinations of 0-9: * * 999999999999999999 1^18-1 * 9223372036854775807 (1<<63)-1 (max int64_t) * 9999999999999999999 1^19-1 (would overflow) */ static const int64_t UPPER_BOUND = 1000000000000000000LL - 1LL; /** Helper function for ParseFixedPoint */ static inline bool ProcessMantissaDigit(char ch, int64_t &mantissa, int &mantissa_tzeros) { if (ch == '0') { ++mantissa_tzeros; } else { for (int i = 0; i <= mantissa_tzeros; ++i) { // overflow if (mantissa > (UPPER_BOUND / 10LL)) { return false; } mantissa *= 10; } mantissa += ch - '0'; mantissa_tzeros = 0; } return true; } bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) { int64_t mantissa = 0; int64_t exponent = 0; int mantissa_tzeros = 0; bool mantissa_sign = false; bool exponent_sign = false; int ptr = 0; int end = val.size(); int point_ofs = 0; if (ptr < end && val[ptr] == '-') { mantissa_sign = true; ++ptr; } if (ptr < end) { if (val[ptr] == '0') { // pass single 0 ++ptr; } else if (val[ptr] >= '1' && val[ptr] <= '9') { while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) { // overflow return false; } ++ptr; } } else { // missing expected digit return false; } } else { // empty string or loose '-' return false; } if (ptr < end && val[ptr] == '.') { ++ptr; if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros)) { // overflow return false; } ++ptr; ++point_ofs; } } else { // missing expected digit return false; } } if (ptr < end && (val[ptr] == 'e' || val[ptr] == 'E')) { ++ptr; if (ptr < end && val[ptr] == '+') { ++ptr; } else if (ptr < end && val[ptr] == '-') { exponent_sign = true; ++ptr; } if (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { while (ptr < end && val[ptr] >= '0' && val[ptr] <= '9') { if (exponent > (UPPER_BOUND / 10LL)) { // overflow return false; } exponent = exponent * 10 + val[ptr] - '0'; ++ptr; } } else { // missing expected digit return false; } } if (ptr != end) { // trailing garbage return false; } // finalize exponent if (exponent_sign) { exponent = -exponent; } exponent = exponent - point_ofs + mantissa_tzeros; // finalize mantissa if (mantissa_sign) { mantissa = -mantissa; } // convert to one 64-bit fixed-point value exponent += decimals; if (exponent < 0) { // cannot represent values smaller than 10^-decimals return false; } if (exponent >= 18) { // cannot represent values larger than or equal to 10^(18-decimals) return false; } for (int i = 0; i < exponent; ++i) { if (mantissa > (UPPER_BOUND / 10LL) || mantissa < -(UPPER_BOUND / 10LL)) { // overflow return false; } mantissa *= 10; } if (mantissa > UPPER_BOUND || mantissa < -UPPER_BOUND) { // overflow return false; } if (amount_out) { *amount_out = mantissa; } return true; } +bool ParseHDKeypath(const std::string &keypath_str, + std::vector &keypath) { + std::stringstream ss(keypath_str); + std::string item; + bool first = true; + while (std::getline(ss, item, '/')) { + if (item.compare("m") == 0) { + if (first) { + first = false; + continue; + } + return false; + } + // Finds whether it is hardened + uint32_t path = 0; + size_t pos = item.find("'"); + if (pos != std::string::npos) { + // The hardened tick can only be in the last index of the string + if (pos != item.size() - 1) { + return false; + } + path |= 0x80000000; + // Drop the last character which is the hardened tick + item = item.substr(0, item.size() - 1); + } + + // Ensure this is only numbers + if (item.find_first_not_of("0123456789") != std::string::npos) { + return false; + } + uint32_t number; + ParseUInt32(item, &number); + path |= number; + + keypath.push_back(path); + first = false; + } + return true; +} + void Downcase(std::string &str) { std::transform(str.begin(), str.end(), str.begin(), [](uint8_t c) { return ToLower(c); }); } std::string Capitalize(std::string str) { if (str.empty()) return str; str[0] = ToUpper(str.front()); return str; } diff --git a/src/util/strencodings.h b/src/util/strencodings.h index 3f4b8df53..024cc893b 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -1,250 +1,254 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. /** * Utilities for converting data from/to strings. */ #ifndef BITCOIN_UTIL_STRENCODINGS_H #define BITCOIN_UTIL_STRENCODINGS_H #include #include #include #define BEGIN(a) ((char *)&(a)) #define END(a) ((char *)&((&(a))[1])) #define UBEGIN(a) ((uint8_t *)&(a)) #define UEND(a) ((uint8_t *)&((&(a))[1])) #define ARRAYLEN(array) (sizeof(array) / sizeof((array)[0])) /** Used by SanitizeString() */ enum SafeChars { //!< The full set of allowed chars SAFE_CHARS_DEFAULT, //!< BIP-0014 subset SAFE_CHARS_UA_COMMENT, //!< Chars allowed in filenames SAFE_CHARS_FILENAME, }; /** * Remove unsafe chars. Safe chars chosen to allow simple messages/URLs/email * addresses, but avoid anything even possibly remotely dangerous like & or > * @param[in] str The string to sanitize * @param[in] rule The set of safe chars to choose (default: least * restrictive) * @return A new string without unsafe chars */ std::string SanitizeString(const std::string &str, int rule = SAFE_CHARS_DEFAULT); std::vector ParseHex(const char *psz); std::vector ParseHex(const std::string &str); signed char HexDigit(char c); /** * Returns true if each character in str is a hex character, and has an even * number of hex digits. */ bool IsHex(const std::string &str); /** * Return true if the string is a hex number, optionally prefixed with "0x" */ bool IsHexNumber(const std::string &str); std::vector DecodeBase64(const char *p, bool *pfInvalid = nullptr); std::string DecodeBase64(const std::string &str); std::string EncodeBase64(const uint8_t *pch, size_t len); std::string EncodeBase64(const std::string &str); std::vector DecodeBase32(const char *p, bool *pfInvalid = nullptr); std::string DecodeBase32(const std::string &str); std::string EncodeBase32(const uint8_t *pch, size_t len); std::string EncodeBase32(const std::string &str); void SplitHostPort(std::string in, int &portOut, std::string &hostOut); std::string i64tostr(int64_t n); std::string itostr(int n); int64_t atoi64(const char *psz); int64_t atoi64(const std::string &str); int atoi(const std::string &str); /** * Tests if the given character is a whitespace character. The whitespace * characters are: space, form-feed ('\f'), newline ('\n'), carriage return * ('\r'), horizontal tab ('\t'), and vertical tab ('\v'). * * This function is locale independent. Under the C locale this function gives * the same result as std::isspace. * * @param[in] c character to test * @return true if the argument is a whitespace character; otherwise * false */ constexpr inline bool IsSpace(char c) noexcept { return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; } /** * Convert string to signed 32-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, false if * not the entire string could be parsed or when overflow or underflow occurred. */ bool ParseInt32(const std::string &str, int32_t *out); /** * Convert string to signed 64-bit integer with strict parse error feedback. * @returns true if the entire string could be parsed as valid integer, false if * not the entire string could be parsed or when overflow or underflow occurred. */ bool ParseInt64(const std::string &str, int64_t *out); /** * Convert decimal string to unsigned 32-bit integer with strict parse error * feedback. * @returns true if the entire string could be parsed as valid integer, false if * not the entire string could be parsed or when overflow or underflow occurred. */ bool ParseUInt32(const std::string &str, uint32_t *out); /** * Convert decimal string to unsigned 64-bit integer with strict parse error * feedback. * @returns true if the entire string could be parsed as valid integer, false if * not the entire string could be parsed or when overflow or underflow occurred. */ bool ParseUInt64(const std::string &str, uint64_t *out); /** * Convert string to double with strict parse error feedback. * @returns true if the entire string could be parsed as valid double, false if * not the entire string could be parsed or when overflow or underflow occurred. */ bool ParseDouble(const std::string &str, double *out); template std::string HexStr(const T itbegin, const T itend, bool fSpaces = false) { std::string rv; static const char hexmap[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; rv.reserve((itend - itbegin) * 3); for (T it = itbegin; it < itend; ++it) { uint8_t val = uint8_t(*it); if (fSpaces && it != itbegin) rv.push_back(' '); rv.push_back(hexmap[val >> 4]); rv.push_back(hexmap[val & 15]); } return rv; } template inline std::string HexStr(const T &vch, bool fSpaces = false) { return HexStr(vch.begin(), vch.end(), fSpaces); } /** * Format a paragraph of text to a fixed width, adding spaces for indentation to * any added line. */ std::string FormatParagraph(const std::string &in, size_t width = 79, size_t indent = 0); /** * Timing-attack-resistant comparison. * Takes time proportional to length of first argument. */ template bool TimingResistantEqual(const T &a, const T &b) { if (b.size() == 0) return a.size() == 0; size_t accumulator = a.size() ^ b.size(); for (size_t i = 0; i < a.size(); i++) accumulator |= a[i] ^ b[i % b.size()]; return accumulator == 0; } /** * Parse number as fixed point according to JSON number syntax. * See http://json.org/number.gif * @returns true on success, false on error. * @note The result must be in the range (-10^18,10^18), otherwise an overflow * error will trigger. */ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); /** * Convert from one power-of-2 number base to another. * * If padding is enabled, this always return true. If not, then it returns true * of all the bits of the input are encoded in the output. */ template bool ConvertBits(O &out, I it, I end) { size_t acc = 0; size_t bits = 0; constexpr size_t maxv = (1 << tobits) - 1; constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1; while (it != end) { acc = ((acc << frombits) | *it) & max_acc; bits += frombits; while (bits >= tobits) { bits -= tobits; out.push_back((acc >> bits) & maxv); } ++it; } // We have remaining bits to encode but do not pad. if (!pad && bits) { return false; } // We have remaining bits to encode so we do pad. if (pad && bits) { out.push_back((acc << (tobits - bits)) & maxv); } return true; } +/** Parse an HD keypaths like "m/7/0'/2000". */ +bool ParseHDKeypath(const std::string &keypath_str, + std::vector &keypath); + /** * Converts the given character to its lowercase equivalent. * This function is locale independent. It only converts uppercase * characters in the standard 7-bit ASCII range. * @param[in] c the character to convert to lowercase. * @return the lowercase equivalent of c; or the argument * if no conversion is possible. */ constexpr uint8_t ToLower(uint8_t c) { return (c >= 'A' && c <= 'Z' ? (c - 'A') + 'a' : c); } /** * Converts the given string to its lowercase equivalent. * This function is locale independent. It only converts uppercase * characters in the standard 7-bit ASCII range. * @param[in,out] str the string to convert to lowercase. */ void Downcase(std::string &str); /** * Converts the given character to its uppercase equivalent. * This function is locale independent. It only converts lowercase * characters in the standard 7-bit ASCII range. * @param[in] c the character to convert to uppercase. * @return the uppercase equivalent of c; or the argument * if no conversion is possible. */ constexpr uint8_t ToUpper(uint8_t c) { return (c >= 'a' && c <= 'z' ? (c - 'a') + 'A' : c); } /** * Capitalizes the first character of the given string. * This function is locale independent. It only capitalizes the * first character of the argument if it has an uppercase equivalent * in the standard 7-bit ASCII range. * @param[in] str the string to capitalize. * @return string with the first letter capitalized. */ std::string Capitalize(std::string str); #endif // BITCOIN_UTIL_STRENCODINGS_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ac96ea125..14ab7238f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1,5543 +1,5503 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include // for GetConsensus. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; static std::string urlDecode(const std::string &urlEncoded) { std::string res; if (!urlEncoded.empty()) { char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr); if (decoded) { res = std::string(decoded); free(decoded); } } return res; } bool GetWalletNameFromJSONRPCRequest(const JSONRPCRequest &request, std::string &wallet_name) { if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) { // wallet endpoint was used wallet_name = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size())); return true; } return false; } std::shared_ptr GetWalletForJSONRPCRequest(const JSONRPCRequest &request) { std::string wallet_name; if (GetWalletNameFromJSONRPCRequest(request, wallet_name)) { std::shared_ptr pwallet = GetWallet(wallet_name); if (!pwallet) { throw JSONRPCError( RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); } return pwallet; } std::vector> wallets = GetWallets(); return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr; } std::string HelpRequiringPassphrase(CWallet *const pwallet) { return pwallet && pwallet->IsCrypted() ? "\nRequires wallet passphrase to " "be set with walletpassphrase " "call." : ""; } bool EnsureWalletIsAvailable(CWallet *const pwallet, bool avoidException) { if (pwallet) { return true; } if (avoidException) { return false; } if (!HasWallets()) { throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (wallet " "method is disabled because " "no wallet is loaded)"); } throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED, "Wallet file not specified (must request wallet RPC " "through /wallet/ uri-path)."); } void EnsureWalletIsUnlocked(CWallet *const pwallet) { if (pwallet->IsLocked()) { throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the " "wallet passphrase with " "walletpassphrase first."); } } static void WalletTxToJSON(const CWalletTx &wtx, UniValue &entry) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { int confirms = wtx.GetDepthInMainChain(); entry.pushKV("confirmations", confirms); if (wtx.IsCoinBase()) { entry.pushKV("generated", true); } if (confirms > 0) { entry.pushKV("blockhash", wtx.hashBlock.GetHex()); entry.pushKV("blockindex", wtx.nIndex); entry.pushKV("blocktime", LookupBlockIndex(wtx.hashBlock)->GetBlockTime()); } else { entry.pushKV("trusted", wtx.IsTrusted()); } uint256 hash = wtx.GetId(); entry.pushKV("txid", hash.GetHex()); UniValue conflicts(UniValue::VARR); for (const uint256 &conflict : wtx.GetConflicts()) { conflicts.push_back(conflict.GetHex()); } entry.pushKV("walletconflicts", conflicts); entry.pushKV("time", wtx.GetTxTime()); entry.pushKV("timereceived", (int64_t)wtx.nTimeReceived); for (const std::pair &item : wtx.mapValue) { entry.pushKV(item.first, item.second); } } static std::string LabelFromValue(const UniValue &value) { std::string label = value.get_str(); if (label == "*") { throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, "Invalid label name"); } return label; } static UniValue getnewaddress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() > 2) { throw std::runtime_error( "getnewaddress ( \"label\" )\n" "\nReturns a new Bitcoin address for receiving payments.\n" "If 'label' is specified, it is added to the address book \n" "so payments received with the address will be associated with " "'label'.\n" "\nArguments:\n" "1. \"label\" (string, optional) The label name for the " "address to be linked to. If not provided, the default label \"\" " "is used. It can also be set to the empty string \"\" to represent " "the default label. The label does not need to exist, it will be " "created if there is no label by the given name.\n" "\nResult:\n" "\"address\" (string) The new bitcoin address\n" "\nExamples:\n" + HelpExampleRpc("getnewaddress", "")); } if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); } LOCK2(cs_main, pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label; if (!request.params[0].isNull()) { label = LabelFromValue(request.params[0]); } OutputType output_type = pwallet->m_default_address_type; if (!request.params[1].isNull()) { if (!ParseOutputType(request.params[1].get_str(), output_type)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str())); } } if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); } // Generate a new key that is added to wallet CPubKey newKey; if (!pwallet->GetKeyFromPool(newKey)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } pwallet->LearnRelatedScripts(newKey, output_type); CTxDestination dest = GetDestinationForKey(newKey, output_type); pwallet->SetAddressBook(dest, label, "receive"); return EncodeDestination(dest, config); } CTxDestination GetLabelDestination(CWallet *const pwallet, const std::string &label, bool bForceNew = false) { CTxDestination dest; if (!pwallet->GetLabelDestination(dest, label, bForceNew)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } return dest; } static UniValue getlabeladdress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && request.strMethod == "getaccountaddress") { if (request.fHelp) { throw std::runtime_error( "getaccountaddress (Deprecated, will be removed in v0.21. To " "use this command, start bitcoind with " "-deprecatedrpc=accounts)"); } throw JSONRPCError( RPC_METHOD_DEPRECATED, "getaccountaddress is deprecated and will be removed in v0.21. To " "use this command, start bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getlabeladdress \"label\" ( force ) \n" "\nReturns the default receiving address for this label. This will " "reset to a fresh address once there's a transaction that spends " "to it.\n" "\nArguments:\n" "1. \"label\" (string, required) The label for the " "address. It can also be set to the empty string \"\" to represent " "the default label.\n" "2. \"force\" (bool, optional) Whether the label should be " "created if it does not yet exist. If False, the RPC will return " "an error if called with a label that doesn't exist.\n" " Defaults to false (unless the " "getaccountaddress method alias is being called, in which case " "defaults to true for backwards compatibility).\n" "\nResult:\n" "\"address\" (string) The current receiving address for " "the label.\n" "\nExamples:\n" + HelpExampleCli("getlabeladdress", "") + HelpExampleCli("getlabeladdress", "\"\"") + HelpExampleCli("getlabeladdress", "\"mylabel\"") + HelpExampleRpc("getlabeladdress", "\"mylabel\"")); } LOCK2(cs_main, pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label = LabelFromValue(request.params[0]); bool force = request.strMethod == "getaccountaddress"; if (!request.params[1].isNull()) { force = request.params[1].get_bool(); } bool label_found = false; for (const std::pair &item : pwallet->mapAddressBook) { if (item.second.name == label) { label_found = true; break; } } if (!force && !label_found) { throw JSONRPCError(RPC_WALLET_INVALID_LABEL_NAME, std::string("No addresses with label " + label)); } UniValue ret(UniValue::VSTR); ret = EncodeDestination(GetLabelDestination(pwallet, label), config); return ret; } static UniValue getrawchangeaddress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() > 1) { throw std::runtime_error( "getrawchangeaddress\n" "\nReturns a new Bitcoin address, for receiving change.\n" "This is for use with raw transactions, NOT normal use.\n" "\nResult:\n" "\"address\" (string) The address\n" "\nExamples:\n" + HelpExampleCli("getrawchangeaddress", "") + HelpExampleRpc("getrawchangeaddress", "")); } if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); } LOCK2(cs_main, pwallet->cs_wallet); if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); } OutputType output_type = pwallet->m_default_change_type != OutputType::CHANGE_AUTO ? pwallet->m_default_change_type : pwallet->m_default_address_type; if (!request.params[0].isNull()) { if (!ParseOutputType(request.params[0].get_str(), output_type)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str())); } } CReserveKey reservekey(pwallet); CPubKey vchPubKey; if (!reservekey.GetReservedKey(vchPubKey, true)) { throw JSONRPCError( RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } reservekey.KeepKey(); pwallet->LearnRelatedScripts(vchPubKey, output_type); CTxDestination dest = GetDestinationForKey(vchPubKey, output_type); return EncodeDestination(dest, config); } static UniValue setlabel(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && request.strMethod == "setaccount") { if (request.fHelp) { throw std::runtime_error( "setaccount (Deprecated, will be removed in v0.21. To use this " "command, start bitcoind with -deprecatedrpc=accounts)"); } throw JSONRPCError( RPC_METHOD_DEPRECATED, "setaccount is deprecated and will be removed in v0.21. To use " "this command, start bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "setlabel \"address\" \"label\"\n" "\nSets the label associated with the given address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to " "be associated with a label.\n" "2. \"label\" (string, required) The label to assign to " "the address.\n" "\nExamples:\n" + HelpExampleCli("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") + HelpExampleRpc( "setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"")); } LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } std::string label = LabelFromValue(request.params[1]); if (IsMine(*pwallet, dest)) { // Detect when changing the label of an address that is the receiving // address of another label. If so, delete the account record for it. // Labels, unlike addresses, can be deleted, and if we wouldn't do this, // the record would stick around forever. if (pwallet->mapAddressBook.count(dest)) { std::string old_label = pwallet->mapAddressBook[dest].name; if (old_label != label && dest == GetLabelDestination(pwallet, old_label)) { pwallet->DeleteLabel(old_label); } } pwallet->SetAddressBook(dest, label, "receive"); } else { pwallet->SetAddressBook(dest, label, "send"); } return NullUniValue; } static UniValue getaccount(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { if (request.fHelp) { throw std::runtime_error( "getaccount (Deprecated, will be removed in v0.21. To use this " "command, start bitcoind with -deprecatedrpc=accounts)"); } throw JSONRPCError( RPC_METHOD_DEPRECATED, "getaccount is deprecated and will be removed in v0.21. To use " "this command, start bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaccount \"address\"\n" "\nDEPRECATED. Returns the account associated with the given " "address.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address for " "account lookup.\n" "\nResult:\n" "\"accountname\" (string) the account address\n" "\nExamples:\n" + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"")); } LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } std::string strAccount; std::map::iterator mi = pwallet->mapAddressBook.find(dest); if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) { strAccount = (*mi).second.name; } return strAccount; } static UniValue getaddressesbyaccount(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts")) { if (request.fHelp) { throw std::runtime_error( "getaddressbyaccount (Deprecated, will be removed in v0.21. To " "use this command, start bitcoind with " "-deprecatedrpc=accounts)"); } throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaddressesbyaccount is deprecated and will be " "removed in v0.21. To use this command, start " "bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() != 1) { throw std::runtime_error( "getaddressesbyaccount \"account\"\n" "\nDEPRECATED. Returns the list of addresses for the given " "account.\n" "\nArguments:\n" "1. \"account\" (string, required) The account name.\n" "\nResult:\n" "[ (json array of string)\n" " \"address\" (string) a bitcoin address associated with " "the given account\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"")); } LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = LabelFromValue(request.params[0]); // Find all addresses that have the given account UniValue ret(UniValue::VARR); for (const std::pair &item : pwallet->mapAddressBook) { const CTxDestination &dest = item.first; const std::string &strName = item.second.name; if (strName == strAccount) { ret.push_back(EncodeDestination(dest, config)); } } return ret; } static CTransactionRef SendMoney(CWallet *const pwallet, const CTxDestination &address, Amount nValue, bool fSubtractFeeFromAmount, mapValue_t mapValue, std::string fromAccount) { Amount curBalance = pwallet->GetBalance(); // Check amount if (nValue <= Amount::zero()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); } if (nValue > curBalance) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); } if (pwallet->GetBroadcastTransactions() && !g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } // Parse Bitcoin address CScript scriptPubKey = GetScriptForDestination(address); // Create and send the transaction CReserveKey reservekey(pwallet); Amount nFeeRequired; std::string strError; std::vector vecSend; int nChangePosRet = -1; CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; vecSend.push_back(recipient); CCoinControl coinControl; CTransactionRef tx; if (!pwallet->CreateTransaction(vecSend, tx, reservekey, nFeeRequired, nChangePosRet, strError, coinControl)) { if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) { strError = strprintf("Error: This transaction requires a " "transaction fee of at least %s", FormatMoney(nFeeRequired)); } throw JSONRPCError(RPC_WALLET_ERROR, strError); } CValidationState state; if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, std::move(fromAccount), reservekey, g_connman.get(), state)) { strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state)); throw JSONRPCError(RPC_WALLET_ERROR, strError); } return tx; } static UniValue sendtoaddress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 2 || request.params.size() > 5) { throw std::runtime_error( "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" " "subtractfeefromamount )\n" "\nSend an amount to a given address.\n" + HelpRequiringPassphrase(pwallet) + "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address " "to send to.\n" "2. \"amount\" (numeric or string, required) The " "amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" "3. \"comment\" (string, optional) A comment used to " "store what the transaction is for. \n" " This is not part of the transaction, " "just kept in your wallet.\n" "4. \"comment_to\" (string, optional) A comment to store " "the name of the person or organization \n" " to which you're sending the " "transaction. This is not part of the \n" " transaction, just kept in your " "wallet.\n" "5. subtractfeefromamount (boolean, optional, default=false) The " "fee will be deducted from the amount being sent.\n" " The recipient will receive less " "bitcoins than you enter in the amount field.\n" "\nResult:\n" "\"txid\" (string) The transaction id.\n" "\nExamples:\n" + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay" "dd\" 0.1 \"donation\" \"seans " "outpost\"") + HelpExampleCli( "sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay" "dd\", 0.1, \"donation\", \"seans " "outpost\"")); } // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } // Amount Amount nAmount = AmountFromValue(request.params[1]); if (nAmount <= Amount::zero()) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); } // Wallet comments mapValue_t mapValue; if (!request.params[2].isNull() && !request.params[2].get_str().empty()) { mapValue["comment"] = request.params[2].get_str(); } if (!request.params[3].isNull() && !request.params[3].get_str().empty()) { mapValue["to"] = request.params[3].get_str(); } bool fSubtractFeeFromAmount = false; if (!request.params[4].isNull()) { fSubtractFeeFromAmount = request.params[4].get_bool(); } EnsureWalletIsUnlocked(pwallet); CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, std::move(mapValue), {} /* fromAccount */); return tx->GetId().GetHex(); } static UniValue listaddressgroupings(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 0) { throw std::runtime_error( "listaddressgroupings\n" "\nLists groups of addresses which have had their common " "ownership\n" "made public by common use as inputs or as the resulting change\n" "in past transactions\n" "\nResult:\n" "[\n" " [\n" " [\n" " \"address\", (string) The bitcoin address\n" " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" " \"label\" (string, optional) The label\n" " ]\n" " ,...\n" " ]\n" " ,...\n" "]\n" "\nExamples:\n" + HelpExampleCli("listaddressgroupings", "") + HelpExampleRpc("listaddressgroupings", "")); } // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); UniValue jsonGroupings(UniValue::VARR); std::map balances = pwallet->GetAddressBalances(); for (const std::set &grouping : pwallet->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); for (const CTxDestination &address : grouping) { UniValue addressInfo(UniValue::VARR); addressInfo.push_back(EncodeDestination(address, config)); addressInfo.push_back(ValueFromAmount(balances[address])); if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) { addressInfo.push_back( pwallet->mapAddressBook.find(address)->second.name); } jsonGrouping.push_back(addressInfo); } jsonGroupings.push_back(jsonGrouping); } return jsonGroupings; } static UniValue signmessage(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() != 2) { throw std::runtime_error( "signmessage \"address\" \"message\"\n" "\nSign a message with the private key of an address" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address to " "use for the private key.\n" "2. \"message\" (string, required) The message to create a " "signature of.\n" "\nResult:\n" "\"signature\" (string) The signature of the message " "encoded in base 64\n" "\nExamples:\n" "\nUnlock the wallet for 30 seconds\n" + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + "\nCreate the signature\n" + HelpExampleCli( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") + "\nVerify the signature\n" + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4" "XX\" \"signature\" \"my " "message\"") + "\nAs json rpc\n" + HelpExampleRpc( "signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\"")); } LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); CTxDestination dest = DecodeDestination(strAddress, config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); } const CKeyID *keyID = boost::get(&dest); if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } CKey key; if (!pwallet->GetKey(*keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } CHashWriter ss(SER_GETHASH, 0); ss << strMessageMagic; ss << strMessage; std::vector vchSig; if (!key.SignCompact(ss.GetHash(), vchSig)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); } return EncodeBase64(vchSig.data(), vchSig.size()); } static UniValue getreceivedbyaddress(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getreceivedbyaddress \"address\" ( minconf )\n" "\nReturns the total amount received by the given address in " "transactions with at least minconf confirmations.\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address for " "transactions.\n" "2. minconf (numeric, optional, default=1) Only " "include transactions confirmed at least this many times.\n" "\nResult:\n" "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" "\nExamples:\n" "\nThe amount from transactions with at least 1 confirmation\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") + "\nThe amount including unconfirmed transactions, zero " "confirmations\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") + "\nThe amount with at least 6 confirmations\n" + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") + "\nAs a json rpc call\n" + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6")); } // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); LOCK2(cs_main, pwallet->cs_wallet); // Bitcoin address CTxDestination dest = DecodeDestination(request.params[0].get_str(), config.GetChainParams()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwallet, scriptPubKey)) { throw JSONRPCError(RPC_WALLET_ERROR, "Address not found in wallet"); } // Minimum confirmations int nMinDepth = 1; if (!request.params[1].isNull()) { nMinDepth = request.params[1].get_int(); } // Tally Amount nAmount = Amount::zero(); for (const std::pair &pairWtx : pwallet->mapWallet) { const CWalletTx &wtx = pairWtx.second; CValidationState state; if (wtx.IsCoinBase() || !ContextualCheckTransactionForCurrentBlock( config.GetChainParams().GetConsensus(), *wtx.tx, state)) { continue; } for (const CTxOut &txout : wtx.tx->vout) { if (txout.scriptPubKey == scriptPubKey) { if (wtx.GetDepthInMainChain() >= nMinDepth) { nAmount += txout.nValue; } } } } return ValueFromAmount(nAmount); } static UniValue getreceivedbylabel(const Config &config, const JSONRPCRequest &request) { std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); CWallet *const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } if (!IsDeprecatedRPCEnabled(gArgs, "accounts") && request.strMethod == "getreceivedbyaccount") { if (request.fHelp) { throw std::runtime_error( "getreceivedbyaccount (Deprecated, will be removed in v0.21. " "To use this command, start bitcoind with " "-deprecatedrpc=accounts)"); } throw JSONRPCError(RPC_METHOD_DEPRECATED, "getreceivedbyaccount is deprecated and will be " "removed in v0.21. To use this command, start " "bitcoind with -deprecatedrpc=accounts."); } if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { throw std::runtime_error( "getreceivedbylabel \"label\" ( minconf )\n" "\nReturns the total amount received by addresses with