diff --git a/src/ripple/protocol/AccountID.h b/src/ripple/protocol/AccountID.h index d4de74029fd..f8393bc2637 100644 --- a/src/ripple/protocol/AccountID.h +++ b/src/ripple/protocol/AccountID.h @@ -57,12 +57,6 @@ template <> boost::optional parseBase58(std::string const& s); -// Parses AccountID using Bitcoin's alphabet -// This is to catch user error. Likely not needed -// DEPRECATED -boost::optional -deprecatedParseBitcoinAccountID(std::string const& s); - // Compatibility with legacy code bool deprecatedParseBase58(AccountID& account, Json::Value const& jv); diff --git a/src/ripple/protocol/ErrorCodes.h b/src/ripple/protocol/ErrorCodes.h index 68c6c4395f5..1eb91e2b512 100644 --- a/src/ripple/protocol/ErrorCodes.h +++ b/src/ripple/protocol/ErrorCodes.h @@ -86,7 +86,7 @@ enum error_code_i { rpcNO_PF_REQUEST = 33, // Bad parameter - rpcACT_BITCOIN = 34, + // NOT USED DO NOT USE AGAIN rpcACT_BITCOIN = 34, rpcACT_MALFORMED = 35, rpcALREADY_MULTISIG = 36, rpcALREADY_SINGLE_SIG = 37, diff --git a/src/ripple/protocol/PublicKey.h b/src/ripple/protocol/PublicKey.h index 34c3d2ecd07..89532ea56c7 100644 --- a/src/ripple/protocol/PublicKey.h +++ b/src/ripple/protocol/PublicKey.h @@ -185,7 +185,7 @@ struct STExchange inline std::string toBase58(TokenType type, PublicKey const& pk) { - return base58EncodeToken(type, pk.data(), pk.size()); + return encodeBase58Token(type, pk.data(), pk.size()); } template <> diff --git a/src/ripple/protocol/SecretKey.h b/src/ripple/protocol/SecretKey.h index 00dbdd9999c..69c390a166a 100644 --- a/src/ripple/protocol/SecretKey.h +++ b/src/ripple/protocol/SecretKey.h @@ -119,7 +119,7 @@ parseBase58(TokenType type, std::string const& s); inline std::string toBase58(TokenType type, SecretKey const& sk) { - return base58EncodeToken(type, sk.data(), sk.size()); + return encodeBase58Token(type, sk.data(), sk.size()); } /** Create a secret key using secure random numbers. */ diff --git a/src/ripple/protocol/Seed.h b/src/ripple/protocol/Seed.h index 2bad213f032..b31ad1d6cb5 100644 --- a/src/ripple/protocol/Seed.h +++ b/src/ripple/protocol/Seed.h @@ -128,7 +128,7 @@ seedAs1751(Seed const& seed); inline std::string toBase58(Seed const& seed) { - return base58EncodeToken(TokenType::FamilySeed, seed.data(), seed.size()); + return encodeBase58Token(TokenType::FamilySeed, seed.data(), seed.size()); } } // namespace ripple diff --git a/src/ripple/protocol/impl/AccountID.cpp b/src/ripple/protocol/impl/AccountID.cpp index a61cfd9b31d..47193513558 100644 --- a/src/ripple/protocol/impl/AccountID.cpp +++ b/src/ripple/protocol/impl/AccountID.cpp @@ -28,7 +28,7 @@ namespace ripple { std::string toBase58(AccountID const& v) { - return base58EncodeToken(TokenType::AccountID, v.data(), v.size()); + return encodeBase58Token(TokenType::AccountID, v.data(), v.size()); } template <> @@ -45,19 +45,6 @@ parseBase58(std::string const& s) return id; } -boost::optional -deprecatedParseBitcoinAccountID(std::string const& s) -{ - auto const result = decodeBase58TokenBitcoin(s, TokenType::AccountID); - if (result.empty()) - return boost::none; - AccountID id; - if (result.size() != id.size()) - return boost::none; - std::memcpy(id.data(), result.data(), result.size()); - return id; -} - bool deprecatedParseBase58(AccountID& account, Json::Value const& jv) { diff --git a/src/ripple/protocol/impl/ErrorCodes.cpp b/src/ripple/protocol/impl/ErrorCodes.cpp index 3df10624655..e16bd630334 100644 --- a/src/ripple/protocol/impl/ErrorCodes.cpp +++ b/src/ripple/protocol/impl/ErrorCodes.cpp @@ -32,7 +32,6 @@ namespace detail { // This array will be omitted from the object file; only the sorted version // will remain in the object file. But the string literals will remain. constexpr static ErrorInfo unorderedErrorInfos[]{ - {rpcACT_BITCOIN, "actBitcoin", "Account is bitcoin address."}, {rpcACT_MALFORMED, "actMalformed", "Account malformed."}, {rpcACT_NOT_FOUND, "actNotFound", "Account not found."}, {rpcALREADY_MULTISIG, "alreadyMultisig", "Already multisigned."}, diff --git a/src/ripple/protocol/impl/tokens.cpp b/src/ripple/protocol/impl/tokens.cpp index 21a84c79c75..ea08d636d9b 100644 --- a/src/ripple/protocol/impl/tokens.cpp +++ b/src/ripple/protocol/impl/tokens.cpp @@ -30,13 +30,17 @@ namespace ripple { -static char rippleAlphabet[] = +static constexpr char const* alphabetForward = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"; -static char bitcoinAlphabet[] = - "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - -//------------------------------------------------------------------------------ +static constexpr std::array const alphabetReverse = []() { + std::array map{}; + for (auto& m : map) + m = -1; + for (int i = 0, j = 0; alphabetForward[i] != 0; ++i) + map[static_cast(alphabetForward[i])] = j++; + return map; +}(); template static typename Hasher::result_type @@ -66,7 +70,7 @@ digest2(Args const&... args) return digest(digest(args...)); } -/* Calculate a 4-byte checksum of the data +/** Calculate a 4-byte checksum of the data The checksum is calculated as the first 4 bytes of the SHA256 digest of the message. This is added @@ -75,32 +79,28 @@ digest2(Args const&... args) @note This checksum algorithm is part of the client API */ -void +static void checksum(void* out, void const* message, std::size_t size) { auto const h = digest2(message, size); std::memcpy(out, h.data(), 4); } -//------------------------------------------------------------------------------ +namespace detail { -// Code from Bitcoin: https://github.com/bitcoin/bitcoin -// Copyright (c) 2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -// -// Modified from the original -// -// WARNING Do not call this directly, use -// encodeBase58Token instead since it -// calculates the size of buffer needed. +/* The base58 encoding & decoding routines in this namespace are taken from + * Bitcoin but have been modified from the original. + * + * Copyright (c) 2014 The Bitcoin Core developers + * Distributed under the MIT software license, see the accompanying + * file COPYING or http://www.opensource.org/licenses/mit-license.php. + */ static std::string encodeBase58( void const* message, std::size_t size, void* temp, - std::size_t temp_size, - char const* const alphabet) + std::size_t temp_size) { auto pbegin = reinterpret_cast(message); auto const pend = pbegin + size; @@ -140,73 +140,20 @@ encodeBase58( // Translate the result into a string. std::string str; str.reserve(zeroes + (b58end - iter)); - str.assign(zeroes, alphabet[0]); + str.assign(zeroes, alphabetForward[0]); while (iter != b58end) - str += alphabet[*(iter++)]; + str += alphabetForward[*(iter++)]; return str; } static std::string -encodeToken( - TokenType type, - void const* token, - std::size_t size, - char const* const alphabet) -{ - // expanded token includes type + 4 byte checksum - auto const expanded = 1 + size + 4; - - // We need expanded + expanded * (log(256) / log(58)) which is - // bounded by expanded + expanded * (138 / 100 + 1) which works - // out to expanded * 3: - auto const bufsize = expanded * 3; - - boost::container::small_vector buf(bufsize); - - // Lay the data out as - // - buf[0] = safe_cast>(type); - if (size) - std::memcpy(buf.data() + 1, token, size); - checksum(buf.data() + 1 + size, buf.data(), 1 + size); - - return encodeBase58( - buf.data(), - expanded, - buf.data() + expanded, - bufsize - expanded, - alphabet); -} - -std::string -base58EncodeToken(TokenType type, void const* token, std::size_t size) -{ - return encodeToken(type, token, size, rippleAlphabet); -} - -std::string -base58EncodeTokenBitcoin(TokenType type, void const* token, std::size_t size) -{ - return encodeToken(type, token, size, bitcoinAlphabet); -} - -//------------------------------------------------------------------------------ - -// Code from Bitcoin: https://github.com/bitcoin/bitcoin -// Copyright (c) 2014 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. -// -// Modified from the original -template -static std::string -decodeBase58(std::string const& s, InverseArray const& inv) +decodeBase58(std::string const& s) { auto psz = s.c_str(); auto remain = s.size(); // Skip and count leading zeroes int zeroes = 0; - while (remain > 0 && inv[*psz] == 0) + while (remain > 0 && alphabetReverse[*psz] == 0) { ++zeroes; ++psz; @@ -221,7 +168,7 @@ decodeBase58(std::string const& s, InverseArray const& inv) std::vector b256(remain * 733 / 1000 + 1); while (remain > 0) { - auto carry = inv[*psz]; + auto carry = alphabetReverse[*psz]; if (carry == -1) return {}; // Apply "b256 = b256 * 58 + carry". @@ -246,16 +193,36 @@ decodeBase58(std::string const& s, InverseArray const& inv) return result; } -/* Base58 decode a Ripple token +} // namespace detail - The type and checksum are are checked - and removed from the returned result. -*/ -template -static std::string -decodeBase58Token(std::string const& s, TokenType type, InverseArray const& inv) +std::string +encodeBase58Token(TokenType type, void const* token, std::size_t size) { - std::string const ret = decodeBase58(s, inv); + // expanded token includes type + 4 byte checksum + auto const expanded = 1 + size + 4; + + // We need expanded + expanded * (log(256) / log(58)) which is + // bounded by expanded + expanded * (138 / 100 + 1) which works + // out to expanded * 3: + auto const bufsize = expanded * 3; + + boost::container::small_vector buf(bufsize); + + // Lay the data out as + // + buf[0] = safe_cast>(type); + if (size) + std::memcpy(buf.data() + 1, token, size); + checksum(buf.data() + 1 + size, buf.data(), 1 + size); + + return detail::encodeBase58( + buf.data(), expanded, buf.data() + expanded, bufsize - expanded); +} + +std::string +decodeBase58Token(std::string const& s, TokenType type) +{ + std::string const ret = detail::decodeBase58(s); // Reject zero length tokens if (ret.size() < 6) @@ -275,44 +242,4 @@ decodeBase58Token(std::string const& s, TokenType type, InverseArray const& inv) return ret.substr(1, ret.size() - 1 - guard.size()); } -//------------------------------------------------------------------------------ - -// Maps characters to their base58 digit -class InverseAlphabet -{ -private: - std::array map_; - -public: - explicit InverseAlphabet(std::string const& digits) - { - map_.fill(-1); - int i = 0; - for (auto const c : digits) - map_[static_cast(c)] = i++; - } - - int - operator[](char c) const - { - return map_[static_cast(c)]; - } -}; - -static InverseAlphabet rippleInverse(rippleAlphabet); - -static InverseAlphabet bitcoinInverse(bitcoinAlphabet); - -std::string -decodeBase58Token(std::string const& s, TokenType type) -{ - return decodeBase58Token(s, type, rippleInverse); -} - -std::string -decodeBase58TokenBitcoin(std::string const& s, TokenType type) -{ - return decodeBase58Token(s, type, bitcoinInverse); -} - } // namespace ripple diff --git a/src/ripple/protocol/tokens.h b/src/ripple/protocol/tokens.h index 5854d1b8adf..1da8ca99345 100644 --- a/src/ripple/protocol/tokens.h +++ b/src/ripple/protocol/tokens.h @@ -53,60 +53,30 @@ template boost::optional parseHexOrBase58(std::string const& s); -// Facilities for converting Ripple tokens -// to and from their human readable strings +/** Encode data in Base58Check format using XRPL alphabet -/* Base-58 encode a Ripple Token + For details on the format see + https://xrpl.org/base58-encodings.html#base58-encodings - Ripple Tokens have a one-byte prefx indicating - the type of token, followed by the data for the - token, and finally a 4-byte checksum. + @param type The type of token to encode. + @param token Pointer to the data to encode. + @param size The size of the data to encode. - Tokens include the following: - - Wallet Seed - Account Public Key - Account ID - - @param type A single byte representing the TokenType - @param token A pointer to storage of not - less than 2*(size+6) bytes - @param size the size of the token buffer in bytes + @return the encoded token. */ std::string -base58EncodeToken(TokenType type, void const* token, std::size_t size); - -/* Base-58 encode a Bitcoin Token - * - * provided here for symmetry, but should never be needed - * except for testing. - * - * @see base58EncodeToken for format description. - * - */ -std::string -base58EncodeTokenBitcoin(TokenType type, void const* token, std::size_t size); +encodeBase58Token(TokenType type, void const* token, std::size_t size); -/** Decode a Base58 token +/** Decode a token of given type encoded using Base58Check and the XRPL alphabet - The type and checksum must match or an - empty string is returned. -*/ -std::string -decodeBase58Token(std::string const& s, TokenType type); - -/** Decode a Base58 token using Bitcoin alphabet + @param s The encoded token + @param type The type expected for this token. - The type and checksum must match or an - empty string is returned. - - This is used to detect user error. Specifically, - when an AccountID is specified using the wrong - base58 alphabet, so that a better error message - may be returned. + @return If the encoded token decodes correctly, the token data without + the type or checksum. And empty string otherwise. */ std::string -decodeBase58TokenBitcoin(std::string const& s, TokenType type); +decodeBase58Token(std::string const& s, TokenType type); } // namespace ripple diff --git a/src/ripple/rpc/impl/RPCHelpers.cpp b/src/ripple/rpc/impl/RPCHelpers.cpp index dbc86774d19..878c03cb62a 100644 --- a/src/ripple/rpc/impl/RPCHelpers.cpp +++ b/src/ripple/rpc/impl/RPCHelpers.cpp @@ -63,10 +63,7 @@ accountFromStringWithCode( } if (bStrict) - { - auto id = deprecatedParseBitcoinAccountID(strIdent); - return id ? rpcACT_BITCOIN : rpcACT_MALFORMED; - } + return rpcACT_MALFORMED; // We allow the use of the seeds which is poor practice // and merely for debugging convenience. diff --git a/src/test/rpc/AccountCurrencies_test.cpp b/src/test/rpc/AccountCurrencies_test.cpp index 77f8df22a1a..f1f4695900f 100644 --- a/src/test/rpc/AccountCurrencies_test.cpp +++ b/src/test/rpc/AccountCurrencies_test.cpp @@ -69,20 +69,6 @@ class AccountCurrencies_test : public beast::unit_test::suite BEAST_EXPECT(result[jss::error_message] == "Account malformed."); } - { // strict mode, using properly formatted bitcoin token - Json::Value params; - params[jss::account] = base58EncodeTokenBitcoin( - TokenType::AccountID, alice.id().data(), alice.id().size()); - params[jss::strict] = true; - auto const result = env.rpc( - "json", - "account_currencies", - boost::lexical_cast(params))[jss::result]; - BEAST_EXPECT(result[jss::error] == "actBitcoin"); - BEAST_EXPECT( - result[jss::error_message] == "Account is bitcoin address."); - } - { // ask for nonexistent account Json::Value params; params[jss::account] = Account{"bob"}.human();