Merge pull request #1041 from gavinandresen/listtransactionsfix
authorGavin Andresen <gavinandresen@gmail.com>
Thu, 12 Apr 2012 19:20:51 +0000 (12:20 -0700)
committerGavin Andresen <gavinandresen@gmail.com>
Thu, 12 Apr 2012 19:20:51 +0000 (12:20 -0700)
Bug fix listtransactions from/count handling.

1  2 
src/bitcoinrpc.cpp

diff --combined src/bitcoinrpc.cpp
  #include <boost/iostreams/stream.hpp>
  #include <boost/algorithm/string.hpp>
  #include <boost/lexical_cast.hpp>
 -#ifdef USE_SSL
  #include <boost/asio/ssl.hpp> 
 -#include <boost/filesystem.hpp>
  #include <boost/filesystem/fstream.hpp>
  typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
 -#endif
 +
  #include "json/json_spirit_reader_template.h"
  #include "json/json_spirit_writer_template.h"
  #include "json/json_spirit_utils.h"
@@@ -984,6 -986,8 +984,6 @@@ Value addmultisigaddress(const Array& p
              "If [account] is specified, assign address to [account].";
          throw runtime_error(msg);
      }
 -    if (!fTestNet)
 -        throw runtime_error("addmultisigaddress available only when running -testnet\n");
  
      int nRequired = params[0].get_int();
      const Array& keys = params[1].get_array();
@@@ -1281,14 -1285,21 +1281,21 @@@ Value listtransactions(const Array& par
      if (params.size() > 2)
          nFrom = params[2].get_int();
  
+     if (nCount < 0)
+         throw JSONRPCError(-8, "Negative count");
+     if (nFrom < 0)
+         throw JSONRPCError(-8, "Negative from");
      Array ret;
      CWalletDB walletdb(pwalletMain->strWalletFile);
  
-     // Firs: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap:
+     // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.
      typedef pair<CWalletTx*, CAccountingEntry*> TxPair;
      typedef multimap<int64, TxPair > TxItems;
      TxItems txByTime;
  
+     // Note: maintaining indices in the database of (account,time) --> txid and (account, time) --> acentry
+     // would make this much faster for applications that do this a lot.
      for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
      {
          CWalletTx* wtx = &((*it).second);
          txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));
      }
  
-     // Now: iterate backwards until we have nCount items to return:
-     TxItems::reverse_iterator it = txByTime.rbegin();
-     if (txByTime.size() > nFrom) std::advance(it, nFrom);
-     for (; it != txByTime.rend(); ++it)
+     // iterate backwards until we have nCount items to return:
+     for (TxItems::reverse_iterator it = txByTime.rbegin(); it != txByTime.rend(); ++it)
      {
          CWalletTx *const pwtx = (*it).second.first;
          if (pwtx != 0)
          if (pacentry != 0)
              AcentryToJSON(*pacentry, strAccount, ret);
  
-         if (ret.size() >= nCount) break;
+         if (ret.size() >= (nCount+nFrom)) break;
      }
-     // ret is now newest to oldest
+     // ret is newest to oldest
      
-     // Make sure we return only last nCount items (sends-to-self might give us an extra):
-     if (ret.size() > nCount)
-     {
-         Array::iterator last = ret.begin();
-         std::advance(last, nCount);
-         ret.erase(last, ret.end());
-     }
-     std::reverse(ret.begin(), ret.end()); // oldest to newest
+     if (nFrom > ret.size()) nFrom = ret.size();
+     if (nFrom+nCount > ret.size()) nCount = ret.size()-nFrom;
+     Array::iterator first = ret.begin();
+     std::advance(first, nFrom);
+     Array::iterator last = ret.begin();
+     std::advance(last, nFrom+nCount);
+     if (last != ret.end()) ret.erase(last, ret.end());
+     if (first != ret.begin()) ret.erase(ret.begin(), first);
+     std::reverse(ret.begin(), ret.end()); // Return oldest to newest
  
      return ret;
  }
@@@ -1647,8 -1659,8 +1655,8 @@@ Value walletlock(const Array& params, b
      if (!pwalletMain->IsCrypted())
          throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletlock was called.");
  
 -    CRITICAL_BLOCK(cs_nWalletUnlockTime)
      {
 +        LOCK(cs_nWalletUnlockTime);
          pwalletMain->Lock();
          nWalletUnlockTime = 0;
      }
@@@ -2271,6 -2283,7 +2279,6 @@@ bool ClientAllowed(const string& strAdd
      return false;
  }
  
 -#ifdef USE_SSL
  //
  // IOStream device that speaks SSL but can also speak non-SSL
  //
@@@ -2322,6 -2335,7 +2330,6 @@@ private
      bool fUseSSL;
      SSLStream& stream;
  };
 -#endif
  
  void ThreadRPCServer(void* parg)
  {
@@@ -2364,7 -2378,7 +2372,7 @@@ void ThreadRPCServer2(void* parg
                "(you do not need to remember this password)\n"
                "If the file does not exist, create it with owner-readable-only file permissions.\n"),
                  strWhatAmI.c_str(),
 -                GetConfigFile().c_str(),
 +                GetConfigFile().string().c_str(),
                  EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
              _("Error"), wxOK | wxMODAL);
          QueueShutdown();
  
      asio::io_service io_service;
      ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
 -    ip::tcp::acceptor acceptor(io_service, endpoint);
 -
 -    acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
 +    ip::tcp::acceptor acceptor(io_service);
 +    try
 +    {
 +        acceptor.open(endpoint.protocol());
 +        acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
 +        acceptor.bind(endpoint);
 +        acceptor.listen(socket_base::max_connections);
 +    }
 +    catch(boost::system::system_error &e)
 +    {
 +        ThreadSafeMessageBox(strprintf(_("An error occured while setting up the RPC port %i for listening: %s"), endpoint.port(), e.what()),
 +                             _("Error"), wxOK | wxMODAL);
 +        QueueShutdown();
 +        return;
 +    }
  
 -#ifdef USE_SSL
      ssl::context context(io_service, ssl::context::sslv23);
      if (fUseSSL)
      {
          context.set_options(ssl::context::no_sslv2);
 -        filesystem::path certfile = GetArg("-rpcsslcertificatechainfile", "server.cert");
 -        if (!certfile.is_complete()) certfile = filesystem::path(GetDataDir()) / certfile;
 -        if (filesystem::exists(certfile)) context.use_certificate_chain_file(certfile.string().c_str());
 -        else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", certfile.string().c_str());
 -        filesystem::path pkfile = GetArg("-rpcsslprivatekeyfile", "server.pem");
 -        if (!pkfile.is_complete()) pkfile = filesystem::path(GetDataDir()) / pkfile;
 -        if (filesystem::exists(pkfile)) context.use_private_key_file(pkfile.string().c_str(), ssl::context::pem);
 -        else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pkfile.string().c_str());
 -
 -        string ciphers = GetArg("-rpcsslciphers",
 -                                         "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
 -        SSL_CTX_set_cipher_list(context.impl(), ciphers.c_str());
 +
 +        filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert"));
 +        if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile;
 +        if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string());
 +        else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str());
 +
 +        filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem"));
 +        if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile;
 +        if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem);
 +        else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str());
 +
 +        string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH");
 +        SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str());
      }
 -#else
 -    if (fUseSSL)
 -        throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries.");
 -#endif
  
      loop
      {
          // Accept connection
          SSLStream sslStream(io_service, context);
          SSLIOStreamDevice d(sslStream, fUseSSL);
          iostreams::stream<SSLIOStreamDevice> stream(d);
  
          ip::tcp::endpoint peer;
          vnThreadsRunning[THREAD_RPCSERVER]--;
 -#ifdef USE_SSL
          acceptor.accept(sslStream.lowest_layer(), peer);
 -#else
 -        acceptor.accept(*stream.rdbuf(), peer);
 -#endif
          vnThreadsRunning[4]++;
          if (fShutdown)
              return;
              {
                  // Execute
                  Value result;
 -                CRITICAL_BLOCK(cs_main)
 -                CRITICAL_BLOCK(pwalletMain->cs_wallet)
 +                {
 +                    LOCK2(cs_main, pwalletMain->cs_wallet);
                      result = (*(*mi).second)(params, false);
 +                }
  
                  // Send reply
                  string strReply = JSONRPCReply(result, Value::null, id);
@@@ -2544,10 -2557,11 +2552,10 @@@ Object CallRPC(const string& strMethod
          throw runtime_error(strprintf(
              _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
                "If the file does not exist, create it with owner-readable-only file permissions."),
 -                GetConfigFile().c_str()));
 +                GetConfigFile().string().c_str()));
  
      // Connect to localhost
      bool fUseSSL = GetBoolArg("-rpcssl");
 -#ifdef USE_SSL
      asio::io_service io_service;
      ssl::context context(io_service, ssl::context::sslv23);
      context.set_options(ssl::context::no_sslv2);
      iostreams::stream<SSLIOStreamDevice> stream(d);
      if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
          throw runtime_error("couldn't connect to server");
 -#else
 -    if (fUseSSL)
 -        throw runtime_error("-rpcssl=1, but bitcoin compiled without full openssl libraries.");
 -
 -    ip::tcp::iostream stream(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332"));
 -    if (stream.fail())
 -        throw runtime_error("couldn't connect to server");
 -#endif
 -
  
      // HTTP basic authentication
      string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);