mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-28 22:45:49 +00:00
Update Base58 codec and remove Bitcoin support:
Use C++17 constant expressions to calculate the inverse alphabet map at compile time instead of at runtime. Remove support for encoding & decoding tokens using the Bitcoin alphabet.
This commit is contained in:
committed by
Nik Bougalis
parent
ab9f3fa42a
commit
831e03ad2a
@@ -57,12 +57,6 @@ template <>
|
||||
boost::optional<AccountID>
|
||||
parseBase58(std::string const& s);
|
||||
|
||||
// Parses AccountID using Bitcoin's alphabet
|
||||
// This is to catch user error. Likely not needed
|
||||
// DEPRECATED
|
||||
boost::optional<AccountID>
|
||||
deprecatedParseBitcoinAccountID(std::string const& s);
|
||||
|
||||
// Compatibility with legacy code
|
||||
bool
|
||||
deprecatedParseBase58(AccountID& account, Json::Value const& jv);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -185,7 +185,7 @@ struct STExchange<STBlob, PublicKey>
|
||||
inline std::string
|
||||
toBase58(TokenType type, PublicKey const& pk)
|
||||
{
|
||||
return base58EncodeToken(type, pk.data(), pk.size());
|
||||
return encodeBase58Token(type, pk.data(), pk.size());
|
||||
}
|
||||
|
||||
template <>
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<AccountID>
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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."},
|
||||
|
||||
@@ -30,13 +30,17 @@
|
||||
|
||||
namespace ripple {
|
||||
|
||||
static char rippleAlphabet[] =
|
||||
static constexpr char const* alphabetForward =
|
||||
"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
|
||||
|
||||
static char bitcoinAlphabet[] =
|
||||
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static constexpr std::array<int, 256> const alphabetReverse = []() {
|
||||
std::array<int, 256> map{};
|
||||
for (auto& m : map)
|
||||
m = -1;
|
||||
for (int i = 0, j = 0; alphabetForward[i] != 0; ++i)
|
||||
map[static_cast<unsigned char>(alphabetForward[i])] = j++;
|
||||
return map;
|
||||
}();
|
||||
|
||||
template <class Hasher>
|
||||
static typename Hasher::result_type
|
||||
@@ -66,7 +70,7 @@ digest2(Args const&... args)
|
||||
return digest<Hasher>(digest<Hasher>(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<sha256_hasher>(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<unsigned char const*>(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<std::uint8_t, 1024> buf(bufsize);
|
||||
|
||||
// Lay the data out as
|
||||
// <type><token><checksum>
|
||||
buf[0] = safe_cast<std::underlying_type_t<TokenType>>(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 <class InverseArray>
|
||||
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<unsigned char> 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 <class InverseArray>
|
||||
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<std::uint8_t, 1024> buf(bufsize);
|
||||
|
||||
// Lay the data out as
|
||||
// <type><token><checksum>
|
||||
buf[0] = safe_cast<std::underlying_type_t<TokenType>>(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<int, 256> map_;
|
||||
|
||||
public:
|
||||
explicit InverseAlphabet(std::string const& digits)
|
||||
{
|
||||
map_.fill(-1);
|
||||
int i = 0;
|
||||
for (auto const c : digits)
|
||||
map_[static_cast<unsigned char>(c)] = i++;
|
||||
}
|
||||
|
||||
int
|
||||
operator[](char c) const
|
||||
{
|
||||
return map_[static_cast<unsigned char>(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
|
||||
|
||||
@@ -53,61 +53,31 @@ template <class T>
|
||||
boost::optional<T>
|
||||
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);
|
||||
encodeBase58Token(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);
|
||||
/** Decode a token of given type encoded using Base58Check and the XRPL alphabet
|
||||
|
||||
/** Decode a Base58 token
|
||||
@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.
|
||||
@return If the encoded token decodes correctly, the token data without
|
||||
the type or checksum. And empty string otherwise.
|
||||
*/
|
||||
std::string
|
||||
decodeBase58Token(std::string const& s, TokenType type);
|
||||
|
||||
/** Decode a Base58 token using Bitcoin alphabet
|
||||
|
||||
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.
|
||||
*/
|
||||
std::string
|
||||
decodeBase58TokenBitcoin(std::string const& s, TokenType type);
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<std::string>(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();
|
||||
|
||||
Reference in New Issue
Block a user