From b58be132c994b6f9b25cb4a702186ef96104953f Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 12 Apr 2014 23:34:00 +0200 Subject: [PATCH] Replace DecodeBase58/EncodeBase58 with direct implementation. This removes the bignum/OpenSSL dependency. The base58 transformation code is also moved to a separate .cpp file. --- src/Makefile.am | 1 + src/base58.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++ src/base58.h | 102 +++++------------------------------------------- 3 files changed, 101 insertions(+), 93 deletions(-) create mode 100644 src/base58.cpp diff --git a/src/Makefile.am b/src/Makefile.am index c725c4f1c..b037ac2c9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -121,6 +121,7 @@ libbitcoin_wallet_a_SOURCES = \ $(BITCOIN_CORE_H) libbitcoin_common_a_SOURCES = \ + base58.cpp \ allocators.cpp \ chainparams.cpp \ core.cpp \ diff --git a/src/base58.cpp b/src/base58.cpp new file mode 100644 index 000000000..0b08ee3d0 --- /dev/null +++ b/src/base58.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2014 The Bitcoin developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include +#include +#include +#include + +/* All alphanumeric characters except for "0", "I", "O", and "l" */ +static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +bool DecodeBase58(const char *psz, std::vector& vch) { + // Skip leading spaces. + while (*psz && isspace(*psz)) + psz++; + // Skip and count leading '1's. + int zeroes = 0; + while (*psz == '1') { + zeroes++; + psz++; + } + // Allocate enough space in big-endian base256 representation. + std::vector b256(strlen(psz) * 733 / 1000 + 1); // log(58) / log(256), rounded up. + // Process the characters. + while (*psz && !isspace(*psz)) { + // Decode base58 character + const char *ch = strchr(pszBase58, *psz); + if (ch == NULL) + return false; + // Apply "b256 = b256 * 58 + ch". + int carry = ch - pszBase58; + for (std::vector::reverse_iterator it = b256.rbegin(); it != b256.rend(); it++) { + carry += 58 * (*it); + *it = carry % 256; + carry /= 256; + } + assert(carry == 0); + psz++; + } + // Skip trailing spaces. + while (isspace(*psz)) + psz++; + if (*psz != 0) + return false; + // Skip leading zeroes in b256. + std::vector::iterator it = b256.begin(); + while (it != b256.end() && *it == 0) + it++; + // Copy result into output vector. + vch.reserve(zeroes + (b256.end() - it)); + vch.assign(zeroes, 0x00); + while (it != b256.end()) + vch.push_back(*(it++)); + return true; +} + +std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) { + // Skip & count leading zeroes. + int zeroes = 0; + while (pbegin != pend && *pbegin == 0) { + pbegin++; + zeroes++; + } + // Allocate enough space in big-endian base58 representation. + std::vector b58((pend - pbegin) * 138 / 100 + 1); // log(256) / log(58), rounded up. + // Process the bytes. + while (pbegin != pend) { + int carry = *pbegin; + // Apply "b58 = b58 * 256 + ch". + for (std::vector::reverse_iterator it = b58.rbegin(); it != b58.rend(); it++) { + carry += 256 * (*it); + *it = carry % 58; + carry /= 58; + } + assert(carry == 0); + pbegin++; + } + // Skip leading zeroes in base58 result. + std::vector::iterator it = b58.begin(); + while (it != b58.end() && *it == 0) + it++; + // Translate the result into a string. + std::string str; + str.reserve(zeroes + (b58.end() - it)); + str.assign(zeroes, '1'); + while (it != b58.end()) + str += pszBase58[*(it++)]; + return str; +} diff --git a/src/base58.h b/src/base58.h index 5cd58b242..4fb436c5e 100644 --- a/src/base58.h +++ b/src/base58.h @@ -14,7 +14,6 @@ #ifndef BITCOIN_BASE58_H #define BITCOIN_BASE58_H -#include "bignum.h" #include "chainparams.h" #include "hash.h" #include "key.h" @@ -27,51 +26,11 @@ #include #include -/* All alphanumeric characters except for "0", "I", "O", and "l" */ -static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; - /** - * Encode a byte sequence as a base58-encoded string + * Encode a byte sequence as a base58-encoded string. + * pbegin and pend cannot be NULL, unless both are. */ -inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend) -{ - CAutoBN_CTX pctx; - CBigNum bn58 = 58; - CBigNum bn0 = 0; - - // Convert big endian data to little endian - the extra zero at the end will - // ensure bignum interprets it as a positive number */ - std::vector vchTmp(pend-pbegin+1, 0); - reverse_copy(pbegin, pend, vchTmp.begin()); - - // Convert little endian data to bignum - CBigNum bn; - bn.setvch(vchTmp); - - // Convert bignum to std::string - std::string str; - // The expected size increase from base58 conversion is approximately 137%, - // but use 138% to be safe - str.reserve((pend - pbegin) * 138 / 100 + 1); - CBigNum dv; - CBigNum rem; - while (bn > bn0) - { - if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) - throw bignum_error("EncodeBase58 : BN_div failed"); - bn = dv; - unsigned int c = rem.getulong(); - str += pszBase58[c]; - } - - // Leading zeroes encoded as base58 zeros - for (const unsigned char* p = pbegin; p < pend && *p == 0; p++) - str += pszBase58[0]; - - // Convert little endian std::string to big endian - reverse(str.begin(), str.end()); - return str; -} +std::string EncodeBase58(const unsigned char* pbegin, const unsigned char* pend); /** * Encode a byte vector as a base58-encoded string @@ -82,58 +41,15 @@ inline std::string EncodeBase58(const std::vector& vch) } /** - * Decode a base58-encoded string (psz) into a byte vector (vchRet) - * return true if decoding is successful + * Decode a base58-encoded string (psz) into a byte vector (vchRet). + * return true if decoding is successful. + * psz cannot be NULL. */ -inline bool DecodeBase58(const char* psz, std::vector& vchRet) -{ - CAutoBN_CTX pctx; - vchRet.clear(); - CBigNum bn58 = 58; - CBigNum bn = 0; - CBigNum bnChar; - while (isspace(*psz)) - psz++; - - // Convert big endian string to bignum - for (const char* p = psz; *p; p++) - { - const char* p1 = strchr(pszBase58, *p); - if (p1 == NULL) - { - while (isspace(*p)) - p++; - if (*p != '\0') - return false; - break; - } - bnChar.setulong(p1 - pszBase58); - if (!BN_mul(&bn, &bn, &bn58, pctx)) - throw bignum_error("DecodeBase58 : BN_mul failed"); - bn += bnChar; - } - - // Get bignum as little endian data - std::vector vchTmp = bn.getvch(); - - // Trim off the sign byte if present - if (vchTmp.size() >= 2 && vchTmp.end()[-1] == 0 && vchTmp.end()[-2] >= 0x80) - vchTmp.erase(vchTmp.end()-1); - - // Restore leading zeros - int nLeadingZeros = 0; - for (const char* p = psz; *p == pszBase58[0]; p++) - nLeadingZeros++; - vchRet.assign(nLeadingZeros + vchTmp.size(), 0); - - // Convert little endian data to big endian - reverse_copy(vchTmp.begin(), vchTmp.end(), vchRet.end() - vchTmp.size()); - return true; -} +bool DecodeBase58(const char* psz, std::vector& vchRet); /** - * Decode a base58-encoded string (str) into a byte vector (vchRet) - * return true if decoding is successful + * Decode a base58-encoded string (str) into a byte vector (vchRet). + * return true if decoding is successful. */ inline bool DecodeBase58(const std::string& str, std::vector& vchRet) {