From b58be132c994b6f9b25cb4a702186ef96104953f Mon Sep 17 00:00:00 2001
From: Pieter Wuille <pieter.wuille@gmail.com>
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 <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include <vector>
+#include <string>
+
+/* All alphanumeric characters except for "0", "I", "O", and "l" */
+static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
+
+bool DecodeBase58(const char *psz, std::vector<unsigned char>& 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<unsigned char> 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<unsigned char>::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<unsigned char>::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<unsigned char> 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<unsigned char>::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<unsigned char>::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 <boost/variant/apply_visitor.hpp>
 #include <boost/variant/static_visitor.hpp>
 
-/* 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<unsigned char> 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<unsigned char>& 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<unsigned char>& 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<unsigned char> 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<unsigned char>& 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<unsigned char>& vchRet)
 {