aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--contrib/bitcoind.bash-completion10
-rwxr-xr-xqa/pull-tester/rpc-tests.sh5
-rw-r--r--qa/rpc-tests/util.py25
-rw-r--r--src/Makefile.am1
-rw-r--r--src/qt/forms/coincontroldialog.ui12
-rw-r--r--src/rest.cpp206
-rw-r--r--src/rpcclient.cpp1
-rw-r--r--src/rpcmisc.cpp20
-rw-r--r--src/rpcserver.cpp8
-rw-r--r--src/rpcserver.h7
11 files changed, 279 insertions, 18 deletions
diff --git a/.travis.yml b/.travis.yml
index f6b011ee1c..567428daf9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,7 +40,7 @@ matrix:
env: HOST=i686-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-i686 g++-mingw-w64-i686 binutils-mingw-w64-i686 mingw-w64-dev wine bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-gui" MAKEJOBS="-j2"
install:
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi
- - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-upgrade -qq $PACKAGES; fi
+ - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi
before_script:
- unset CC; unset CXX
- mkdir -p depends/SDKs depends/sdk-sources
diff --git a/contrib/bitcoind.bash-completion b/contrib/bitcoind.bash-completion
index 03ef173c09..37ece25899 100644
--- a/contrib/bitcoind.bash-completion
+++ b/contrib/bitcoind.bash-completion
@@ -39,6 +39,10 @@ _bitcoind() {
if ((cword > 4)); then
case ${words[cword-4]} in
+ listtransactions)
+ COMPREPLY=( $( compgen -W "true false" -- "$cur" ) )
+ return 0
+ ;;
signrawtransaction)
COMPREPLY=( $( compgen -W "ALL NONE SINGLE ALL|ANYONECANPAY NONE|ANYONECANPAY SINGLE|ANYONECANPAY" -- "$cur" ) )
return 0
@@ -52,7 +56,7 @@ _bitcoind() {
_bitcoin_accounts
return 0
;;
- gettxout|importprivkey)
+ getbalance|gettxout|importaddress|importprivkey|listreceivedbyaccount|listreceivedbyaddress|listsinceblock)
COMPREPLY=( $( compgen -W "true false" -- "$cur" ) )
return 0
;;
@@ -65,7 +69,7 @@ _bitcoind() {
COMPREPLY=( $( compgen -W "add remove onetry" -- "$cur" ) )
return 0
;;
- getblock|getrawtransaction|listreceivedbyaccount|listreceivedbyaddress|sendrawtransaction)
+ getblock|getrawtransaction|gettransaction|listaccounts|listreceivedbyaccount|listreceivedbyaddress|sendrawtransaction)
COMPREPLY=( $( compgen -W "true false" -- "$cur" ) )
return 0
;;
@@ -115,7 +119,7 @@ _bitcoind() {
# only parse help if senseful
if [[ -z "$cur" || "$cur" =~ ^[a-z] ]]; then
- commands=$(_bitcoin_rpc help 2>/dev/null | awk '{ print $1; }')
+ commands=$(_bitcoin_rpc help 2>/dev/null | awk '$1 ~ /^[a-z]/ { print $1; }')
fi
COMPREPLY=( $( compgen -W "$helpopts $commands" -- "$cur" ) )
diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh
index a0056c141b..0b5ad20642 100755
--- a/qa/pull-tester/rpc-tests.sh
+++ b/qa/pull-tester/rpc-tests.sh
@@ -8,6 +8,11 @@ CURDIR=$(cd $(dirname "$0"); pwd)
export BITCOINCLI=${BUILDDIR}/qa/pull-tester/run-bitcoin-cli
export BITCOIND=${REAL_BITCOIND}
+if [ "x${EXEEXT}" = "x.exe" ]; then
+ echo "Win tests currently disabled"
+ exit 0
+fi
+
#Run the tests
if [ "x${ENABLE_BITCOIND}${ENABLE_UTILS}${ENABLE_WALLET}" = "x111" ]; then
diff --git a/qa/rpc-tests/util.py b/qa/rpc-tests/util.py
index 0d5eeefa79..c6d918a81c 100644
--- a/qa/rpc-tests/util.py
+++ b/qa/rpc-tests/util.py
@@ -103,12 +103,17 @@ def initialize_chain(test_dir):
# Create a 200-block-long chain; each of the 4 nodes
# gets 25 mature blocks and 25 immature.
- for i in range(4):
- rpcs[i].setgenerate(True, 25)
- sync_blocks(rpcs)
- for i in range(4):
- rpcs[i].setgenerate(True, 25)
- sync_blocks(rpcs)
+ # blocks are created with timestamps 10 minutes apart, starting
+ # at 1 Jan 2014
+ block_time = 1388534400
+ for i in range(2):
+ for peer in range(4):
+ for j in range(25):
+ set_node_times(rpcs, block_time)
+ rpcs[peer].setgenerate(True, 1)
+ block_time += 10*60
+ # Must sync before next peer starts generating blocks
+ sync_blocks(rpcs)
# Shut them down, and clean up cache directories:
stop_nodes(rpcs)
@@ -179,10 +184,14 @@ def stop_node(node, i):
del bitcoind_processes[i]
def stop_nodes(nodes):
- for i in range(len(nodes)):
- nodes[i].stop()
+ for node in nodes:
+ node.stop()
del nodes[:] # Emptying array closes connections as a side effect
+def set_node_times(nodes, t):
+ for node in nodes:
+ node.setmocktime(t)
+
def wait_bitcoinds():
# Wait for all bitcoinds to cleanly exit
for bitcoind in bitcoind_processes.values():
diff --git a/src/Makefile.am b/src/Makefile.am
index b3b41e3d17..3da833d733 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -166,6 +166,7 @@ libbitcoin_server_a_SOURCES = \
net.cpp \
noui.cpp \
pow.cpp \
+ rest.cpp \
rpcblockchain.cpp \
rpcmining.cpp \
rpcmisc.cpp \
diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui
index cbe58fec65..c1fef6b9b1 100644
--- a/src/qt/forms/coincontroldialog.ui
+++ b/src/qt/forms/coincontroldialog.ui
@@ -11,7 +11,7 @@
</rect>
</property>
<property name="windowTitle">
- <string>Coin Control Address Selection</string>
+ <string>Coin Selection</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
@@ -379,9 +379,6 @@
<property name="text">
<string>Tree mode</string>
</property>
- <property name="checked">
- <bool>true</bool>
- </property>
</widget>
</item>
<item>
@@ -395,6 +392,9 @@
<property name="text">
<string>List mode</string>
</property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
</widget>
</item>
<item>
@@ -451,12 +451,12 @@
</column>
<column>
<property name="text">
- <string notr="true">Label</string>
+ <string>Received with label</string>
</property>
</column>
<column>
<property name="text">
- <string>Address</string>
+ <string>Received with address</string>
</property>
</column>
<column>
diff --git a/src/rest.cpp b/src/rest.cpp
new file mode 100644
index 0000000000..9a8793a517
--- /dev/null
+++ b/src/rest.cpp
@@ -0,0 +1,206 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2012 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 <boost/algorithm/string.hpp>
+#include "rpcserver.h"
+#include "streams.h"
+#include "utilstrencodings.h"
+#include "core/block.h"
+#include "core/transaction.h"
+#include "version.h"
+#include "main.h"
+#include "sync.h"
+
+using namespace std;
+using namespace json_spirit;
+
+enum RetFormat {
+ RF_BINARY,
+ RF_HEX,
+ RF_JSON,
+};
+
+static const struct {
+ enum RetFormat rf;
+ const char *name;
+} rf_names[] = {
+ { RF_BINARY, "binary" }, // default, if match not found
+ { RF_HEX, "hex" },
+ { RF_JSON, "json" },
+};
+
+class RestErr {
+public:
+ enum HTTPStatusCode status;
+ string message;
+};
+
+extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry);
+extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex);
+
+static RestErr RESTERR(enum HTTPStatusCode status, string message)
+{
+ RestErr re;
+ re.status = status;
+ re.message = message;
+ return re;
+}
+
+static enum RetFormat ParseDataFormat(const string& format)
+{
+ for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
+ if (format == rf_names[i].name)
+ return rf_names[i].rf;
+
+ return rf_names[0].rf;
+}
+
+static bool ParseHashStr(const string& strReq, uint256& v)
+{
+ if (!IsHex(strReq) || (strReq.size() != 64))
+ return false;
+
+ v.SetHex(strReq);
+ return true;
+}
+
+static bool rest_block(AcceptedConnection *conn,
+ string& strReq,
+ map<string, string>& mapHeaders,
+ bool fRun)
+{
+ vector<string> params;
+ boost::split(params, strReq, boost::is_any_of("/"));
+
+ enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));
+
+ string hashStr = params[0];
+ uint256 hash;
+ if (!ParseHashStr(hashStr, hash))
+ throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
+
+ CBlock block;
+ CBlockIndex* pblockindex = NULL;
+ {
+ LOCK(cs_main);
+ if (mapBlockIndex.count(hash) == 0)
+ throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
+
+ pblockindex = mapBlockIndex[hash];
+ if (!ReadBlockFromDisk(block, pblockindex))
+ throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
+ }
+
+ CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
+ ssBlock << block;
+
+ switch (rf) {
+ case RF_BINARY: {
+ string binaryBlock = ssBlock.str();
+ conn->stream() << HTTPReply(HTTP_OK, binaryBlock, fRun, true, "application/octet-stream") << binaryBlock << std::flush;
+ return true;
+ }
+
+ case RF_HEX: {
+ string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";;
+ conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
+ return true;
+ }
+
+ case RF_JSON: {
+ Object objBlock = blockToJSON(block, pblockindex);
+ string strJSON = write_string(Value(objBlock), false) + "\n";
+ conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
+ return true;
+ }
+ }
+
+ // not reached
+ return true; // continue to process further HTTP reqs on this cxn
+}
+
+static bool rest_tx(AcceptedConnection *conn,
+ string& strReq,
+ map<string, string>& mapHeaders,
+ bool fRun)
+{
+ vector<string> params;
+ boost::split(params, strReq, boost::is_any_of("/"));
+
+ enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));
+
+ string hashStr = params[0];
+ uint256 hash;
+ if (!ParseHashStr(hashStr, hash))
+ throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
+
+ CTransaction tx;
+ uint256 hashBlock = 0;
+ if (!GetTransaction(hash, tx, hashBlock, true))
+ throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
+
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << tx;
+
+ switch (rf) {
+ case RF_BINARY: {
+ string binaryTx = ssTx.str();
+ conn->stream() << HTTPReply(HTTP_OK, binaryTx, fRun, true, "application/octet-stream") << binaryTx << std::flush;
+ return true;
+ }
+
+ case RF_HEX: {
+ string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";;
+ conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
+ return true;
+ }
+
+ case RF_JSON: {
+ Object objTx;
+ TxToJSON(tx, hashBlock, objTx);
+ string strJSON = write_string(Value(objTx), false) + "\n";
+ conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
+ return true;
+ }
+ }
+
+ // not reached
+ return true; // continue to process further HTTP reqs on this cxn
+}
+
+static const struct {
+ const char *prefix;
+ bool (*handler)(AcceptedConnection *conn,
+ string& strURI,
+ map<string, string>& mapHeaders,
+ bool fRun);
+} uri_prefixes[] = {
+ { "/rest/tx/", rest_tx },
+ { "/rest/block/", rest_block },
+};
+
+bool HTTPReq_REST(AcceptedConnection *conn,
+ string& strURI,
+ map<string, string>& mapHeaders,
+ bool fRun)
+{
+ try {
+ for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) {
+ unsigned int plen = strlen(uri_prefixes[i].prefix);
+ if (strURI.substr(0, plen) == uri_prefixes[i].prefix) {
+ string strReq = strURI.substr(plen);
+ return uri_prefixes[i].handler(conn, strReq, mapHeaders, fRun);
+ }
+ }
+ }
+ catch (RestErr& re) {
+ conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush;
+ return false;
+ }
+
+ conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush;
+ return false;
+}
+
diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp
index a9c491cede..7a1f1918f6 100644
--- a/src/rpcclient.cpp
+++ b/src/rpcclient.cpp
@@ -25,6 +25,7 @@ public:
static const CRPCConvertParam vRPCConvertParams[] =
{
{ "stop", 0 },
+ { "setmocktime", 0 },
{ "getaddednodeinfo", 0 },
{ "setgenerate", 0 },
{ "setgenerate", 1 },
diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp
index 08e956c961..31eaae6162 100644
--- a/src/rpcmisc.cpp
+++ b/src/rpcmisc.cpp
@@ -354,3 +354,23 @@ Value verifymessage(const Array& params, bool fHelp)
return (pubkey.GetID() == keyID);
}
+
+Value setmocktime(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 1)
+ throw runtime_error(
+ "setmocktime timestamp\n"
+ "\nSet the local time to given timestamp (-regtest only)\n"
+ "\nArguments:\n"
+ "1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n"
+ " Pass 0 to go back to using the system time."
+ );
+
+ if (!Params().MineBlocksOnDemand())
+ throw runtime_error("setmocktime for regression testing (-regtest mode) only");
+
+ RPCTypeCheck(params, boost::assign::list_of(int_type));
+ SetMockTime(params[0].get_int64());
+
+ return Value::null;
+}
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index cc80887ba4..01005c1cee 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -246,6 +246,7 @@ static const CRPCCommand vRPCCommands[] =
{ "control", "getinfo", &getinfo, true, false, false }, /* uses wallet if enabled */
{ "control", "help", &help, true, true, false },
{ "control", "stop", &stop, true, true, false },
+ { "control", "setmocktime", &setmocktime, true, false, false },
/* P2P networking */
{ "network", "getnetworkinfo", &getnetworkinfo, true, false, false },
@@ -946,9 +947,16 @@ void ServiceConnection(AcceptedConnection *conn)
if (mapHeaders["connection"] == "close")
fRun = false;
+ // Process via JSON-RPC API
if (strURI == "/") {
if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun))
break;
+
+ // Process via HTTP REST API
+ } else if (strURI.substr(0, 6) == "/rest/") {
+ if (!HTTPReq_REST(conn, strURI, mapHeaders, fRun))
+ break;
+
} else {
conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush;
break;
diff --git a/src/rpcserver.h b/src/rpcserver.h
index 2a258dd89a..b3234f65f2 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -194,6 +194,7 @@ extern json_spirit::Value getinfo(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getwalletinfo(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getblockchaininfo(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getnetworkinfo(const json_spirit::Array& params, bool fHelp);
+extern json_spirit::Value setmocktime(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp
extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp);
@@ -218,4 +219,10 @@ extern json_spirit::Value gettxout(const json_spirit::Array& params, bool fHelp)
extern json_spirit::Value verifychain(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value getchaintips(const json_spirit::Array& params, bool fHelp);
+// in rest.cpp
+extern bool HTTPReq_REST(AcceptedConnection *conn,
+ std::string& strURI,
+ std::map<std::string, std::string>& mapHeaders,
+ bool fRun);
+
#endif // BITCOIN_RPCSERVER_H