Skip to content

Commit

Permalink
Merge branch 'bitcoin' into auxpow
Browse files Browse the repository at this point in the history
  • Loading branch information
domob1812 committed May 20, 2024
2 parents 2d4d1d3 + 058af75 commit 8aff7d6
Show file tree
Hide file tree
Showing 64 changed files with 1,237 additions and 577 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ env:
jobs:
test-each-commit:
name: 'test each commit'
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
if: github.event_name == 'pull_request' && github.event.pull_request.commits != 1
timeout-minutes: 360 # Use maximum time, see https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes. Assuming a worst case time of 1 hour per commit, this leads to a --max-count=6 below.
env:
Expand Down Expand Up @@ -62,12 +62,12 @@ jobs:
echo "TEST_BASE=$(git rev-list -n$((${{ env.MAX_COUNT }} + 1)) --reverse HEAD ^$(git rev-list -n1 --merges HEAD)^@ | head -1)" >> "$GITHUB_ENV"
- run: |
sudo apt-get update
sudo apt-get install clang-15 ccache build-essential libtool autotools-dev automake pkg-config bsdmainutils python3-zmq libevent-dev libboost-dev libsqlite3-dev libdb++-dev systemtap-sdt-dev libminiupnpc-dev libnatpmp-dev qtbase5-dev qttools5-dev qttools5-dev-tools qtwayland5 libqrencode-dev -y
sudo apt-get install clang ccache build-essential libtool autotools-dev automake pkg-config bsdmainutils python3-zmq libevent-dev libboost-dev libsqlite3-dev libdb++-dev systemtap-sdt-dev libminiupnpc-dev libnatpmp-dev qtbase5-dev qttools5-dev qttools5-dev-tools qtwayland5 libqrencode-dev -y
- name: Compile and run tests
run: |
# Run tests on commits after the last merge commit and before the PR head commit
# Use clang++, because it is a bit faster and uses less memory than g++
git rebase --exec "echo Running test-one-commit on \$( git log -1 ) && ./autogen.sh && CC=clang-15 CXX=clang++-15 ./configure && make clean && make -j $(nproc) check && ./test/functional/test_runner.py -j $(( $(nproc) * 2 ))" ${{ env.TEST_BASE }}
git rebase --exec "echo Running test-one-commit on \$( git log -1 ) && ./autogen.sh && CC=clang CXX=clang++ ./configure && make clean && make -j $(nproc) check && ./test/functional/test_runner.py -j $(( $(nproc) * 2 ))" ${{ env.TEST_BASE }}
macos-native-x86_64:
name: 'macOS 13 native, x86_64, no depends, sqlite only, gui'
Expand Down
12 changes: 7 additions & 5 deletions contrib/devtools/test-security-check.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,24 @@ def clean_files(source, executable):
os.remove(source)
os.remove(executable)

def call_security_check(cc: str, source: str, executable: str, options) -> tuple:
def env_flags() -> list[str]:
# This should behave the same as AC_TRY_LINK, so arrange well-known flags
# in the same order as autoconf would.
#
# See the definitions for ac_link in autoconf's lib/autoconf/c.m4 file for
# reference.
env_flags: list[str] = []
flags: list[str] = []
for var in ['CFLAGS', 'CPPFLAGS', 'LDFLAGS']:
env_flags += filter(None, os.environ.get(var, '').split(' '))
flags += filter(None, os.environ.get(var, '').split(' '))
return flags

subprocess.run([*cc,source,'-o',executable] + env_flags + options, check=True)
def call_security_check(cc: str, source: str, executable: str, options) -> tuple:
subprocess.run([*cc,source,'-o',executable] + env_flags() + options, check=True)
p = subprocess.run([os.path.join(os.path.dirname(__file__), 'security-check.py'), executable], stdout=subprocess.PIPE, text=True)
return (p.returncode, p.stdout.rstrip())

def get_arch(cc, source, executable):
subprocess.run([*cc, source, '-o', executable], check=True)
subprocess.run([*cc, source, '-o', executable] + env_flags(), check=True)
binary = lief.parse(executable)
arch = binary.abstract.header.architecture
os.remove(executable)
Expand Down
4 changes: 0 additions & 4 deletions contrib/devtools/test-symbol-check.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ def call_symbol_check(cc: list[str], source, executable, options):
os.remove(executable)
return (p.returncode, p.stdout.rstrip())

def get_machine(cc: list[str]):
p = subprocess.run([*cc,'-dumpmachine'], stdout=subprocess.PIPE, text=True)
return p.stdout.rstrip()

class TestSymbolChecks(unittest.TestCase):
def test_ELF(self):
source = 'test1.c'
Expand Down
3 changes: 3 additions & 0 deletions depends/funcs.mk
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ $(1)_cmake=env CC="$$($(1)_cc)" \
CXXFLAGS="$$($(1)_cppflags) $$($(1)_cxxflags)" \
LDFLAGS="$$($(1)_ldflags)" \
cmake -DCMAKE_INSTALL_PREFIX:PATH="$$($($(1)_type)_prefix)" \
-DCMAKE_AR=`which $$($(1)_ar)` \
-DCMAKE_NM=`which $$($(1)_nm)` \
-DCMAKE_RANLIB=`which $$($(1)_ranlib)` \
-DCMAKE_INSTALL_LIBDIR=lib/ \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DCMAKE_VERBOSE_MAKEFILE:BOOL=$(V) \
Expand Down
16 changes: 16 additions & 0 deletions doc/JSON-RPC-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,22 @@ major version via the `-deprecatedrpc=` command line option. The release notes
of a new major release come with detailed instructions on what RPC features
were deprecated and how to re-enable them temporarily.

## JSON-RPC 1.1 vs 2.0

The server recognizes [JSON-RPC v2.0](https://www.jsonrpc.org/specification) requests
and responds accordingly. A 2.0 request is identified by the presence of
`"jsonrpc": "2.0"` in the request body. If that key + value is not present in a request,
the legacy JSON-RPC v1.1 protocol is followed instead, which was the only available
protocol in previous releases.

|| 1.1 | 2.0 |
|-|-|-|
| Request marker | `"version": "1.1"` (or none) | `"jsonrpc": "2.0"` |
| Response marker | (none) | `"jsonrpc": "2.0"` |
| `"error"` and `"result"` fields in response | both present | only one is present |
| HTTP codes in response | `200` unless there is any kind of RPC error (invalid parameters, method not found, etc) | Always `200` unless there is an actual HTTP server error (request parsing error, endpoint not found, etc) |
| Notifications: requests that get no reply | (not supported) | Supported for requests that exclude the "id" field |

## Security

The RPC interface allows other programs to control Bitcoin Core,
Expand Down
9 changes: 9 additions & 0 deletions doc/release-notes-27101.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
JSON-RPC
--------

The JSON-RPC server now recognizes JSON-RPC 2.0 requests and responds with
strict adherence to the specification (https://www.jsonrpc.org/specification):

- Returning HTTP "204 No Content" responses to JSON-RPC 2.0 notifications instead of full responses.
- Returning HTTP "200 OK" responses in all other cases, rather than 404 responses for unknown methods, 500 responses for invalid parameters, etc.
- Returning either "result" fields or "error" fields in JSON-RPC responses, rather than returning both fields with one field set to null.
1 change: 0 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -979,7 +979,6 @@ libbitcoinkernel_la_SOURCES = \
txdb.cpp \
txmempool.cpp \
uint256.cpp \
util/batchpriority.cpp \
util/chaintype.cpp \
util/check.cpp \
util/feefrac.cpp \
Expand Down
4 changes: 2 additions & 2 deletions src/addrman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ void AddrManImpl::Serialize(Stream& s_) const
*/

// Always serialize in the latest version (FILE_FORMAT).
ParamsStream s{CAddress::V2_DISK, s_};
ParamsStream s{s_, CAddress::V2_DISK};

s << static_cast<uint8_t>(FILE_FORMAT);

Expand Down Expand Up @@ -238,7 +238,7 @@ void AddrManImpl::Unserialize(Stream& s_)
s_ >> Using<CustomUintFormatter<1>>(format);

const auto ser_params = (format >= Format::V3_BIP155 ? CAddress::V2_DISK : CAddress::V1_DISK);
ParamsStream s{ser_params, s_};
ParamsStream s{s_, ser_params};

uint8_t compat;
s >> compat;
Expand Down
2 changes: 1 addition & 1 deletion src/bench/readblock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static FlatFilePos WriteBlockToDisk(ChainstateManager& chainman)
CBlock block;
stream >> TX_WITH_WITNESS(block);

return chainman.m_blockman.SaveBlockToDisk(block, 0, nullptr);
return chainman.m_blockman.SaveBlockToDisk(block, 0);
}

static void ReadBlockFromDiskTest(benchmark::Bench& bench)
Expand Down
2 changes: 1 addition & 1 deletion src/bitcoin-chainstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ int main(int argc, char* argv[])
{
LOCK(chainman.GetMutex());
std::cout
<< "\t" << "Reindexing: " << std::boolalpha << node::fReindex.load() << std::noboolalpha << std::endl
<< "\t" << "Reindexing: " << std::boolalpha << chainman.m_blockman.m_reindexing.load() << std::noboolalpha << std::endl
<< "\t" << "Snapshot Active: " << std::boolalpha << chainman.IsSnapshotActive() << std::noboolalpha << std::endl
<< "\t" << "Active Height: " << chainman.ActiveHeight() << std::endl
<< "\t" << "Active IBD: " << std::boolalpha << chainman.IsInitialBlockDownload() << std::noboolalpha << std::endl;
Expand Down
8 changes: 4 additions & 4 deletions src/bitcoin-cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ class AddrinfoRequestHandler : public BaseRequestHandler
}
addresses.pushKV("total", total);
result.pushKV("addresses_known", addresses);
return JSONRPCReplyObj(result, NullUniValue, 1);
return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V1_LEGACY);
}
};

Expand Down Expand Up @@ -367,7 +367,7 @@ class GetinfoRequestHandler: public BaseRequestHandler
}
result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]);
result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]);
return JSONRPCReplyObj(result, NullUniValue, 1);
return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V1_LEGACY);
}
};

Expand Down Expand Up @@ -622,7 +622,7 @@ class NetinfoRequestHandler : public BaseRequestHandler
}
}

return JSONRPCReplyObj(UniValue{result}, NullUniValue, 1);
return JSONRPCReplyObj(UniValue{result}, NullUniValue, /*id=*/1, JSONRPCVersion::V1_LEGACY);
}

const std::string m_help_doc{
Expand Down Expand Up @@ -709,7 +709,7 @@ class GenerateToAddressRequestHandler : public BaseRequestHandler
UniValue result(UniValue::VOBJ);
result.pushKV("address", address_str);
result.pushKV("blocks", reply.get_obj()["result"]);
return JSONRPCReplyObj(result, NullUniValue, 1);
return JSONRPCReplyObj(std::move(result), NullUniValue, /*id=*/1, JSONRPCVersion::V1_LEGACY);
}
protected:
std::string address_str;
Expand Down
18 changes: 4 additions & 14 deletions src/clientversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
#include <config/bitcoin-config.h> // IWYU pragma: keep

#include <clientversion.h>
#include <util/string.h>
#include <util/translation.h>

#include <tinyformat.h>

#include <sstream>
#include <string>
#include <vector>

Expand Down Expand Up @@ -64,19 +64,9 @@ std::string FormatFullVersion()
*/
std::string FormatSubVersion(const std::string& name, int nClientVersion, const std::vector<std::string>& comments)
{
std::ostringstream ss;
ss << "/";
ss << name << ":" << FormatVersion(nClientVersion);
if (!comments.empty())
{
std::vector<std::string>::const_iterator it(comments.begin());
ss << "(" << *it;
for(++it; it != comments.end(); ++it)
ss << "; " << *it;
ss << ")";
}
ss << "/";
return ss.str();
std::string comments_str;
if (!comments.empty()) comments_str = strprintf("(%s)", Join(comments, "; "));
return strprintf("/%s:%s%s/", name, FormatVersion(nClientVersion), comments_str);
}

std::string CopyrightHolders(const std::string& strPrefix)
Expand Down
7 changes: 7 additions & 0 deletions src/crypto/sha256_sse4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
namespace sha256_sse4
{
void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks)
#if defined(__clang__) && !defined(__OPTIMIZE__)
/*
clang is unable to compile this with -O0 and -fsanitize=address.
See upstream bug: https://github.com/llvm/llvm-project/issues/92182
*/
__attribute__((no_sanitize("address")))
#endif
{
static const uint32_t K256 alignas(16) [] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
Expand Down
66 changes: 55 additions & 11 deletions src/httprpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,11 @@ static std::vector<std::vector<std::string>> g_rpcauth;
static std::map<std::string, std::set<std::string>> g_rpc_whitelist;
static bool g_rpc_whitelist_default = false;

static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
static void JSONErrorReply(HTTPRequest* req, UniValue objError, const JSONRPCRequest& jreq)
{
// Sending HTTP errors is a legacy JSON-RPC behavior.
Assume(jreq.m_json_version != JSONRPCVersion::V2);

// Send error reply from json-rpc error object
int nStatus = HTTP_INTERNAL_SERVER_ERROR;
int code = objError.find_value("code").getInt<int>();
Expand All @@ -84,7 +87,7 @@ static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const Uni
else if (code == RPC_METHOD_NOT_FOUND)
nStatus = HTTP_NOT_FOUND;

std::string strReply = JSONRPCReply(NullUniValue, objError, id);
std::string strReply = JSONRPCReplyObj(NullUniValue, std::move(objError), jreq.id, jreq.m_json_version).write() + "\n";

req->WriteHeader("Content-Type", "application/json");
req->WriteReply(nStatus, strReply);
Expand Down Expand Up @@ -185,7 +188,7 @@ static bool HTTPReq_JSONRPC(const std::any& context, HTTPRequest* req)
// Set the URI
jreq.URI = req->GetURI();

std::string strReply;
UniValue reply;
bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser);
if (!user_has_whitelist && g_rpc_whitelist_default) {
LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser);
Expand All @@ -200,13 +203,23 @@ static bool HTTPReq_JSONRPC(const std::any& context, HTTPRequest* req)
req->WriteReply(HTTP_FORBIDDEN);
return false;
}
UniValue result = tableRPC.execute(jreq);

// Send reply
strReply = JSONRPCReply(result, NullUniValue, jreq.id);
// Legacy 1.0/1.1 behavior is for failed requests to throw
// exceptions which return HTTP errors and RPC errors to the client.
// 2.0 behavior is to catch exceptions and return HTTP success with
// RPC errors, as long as there is not an actual HTTP server error.
const bool catch_errors{jreq.m_json_version == JSONRPCVersion::V2};
reply = JSONRPCExec(jreq, catch_errors);

if (jreq.IsNotification()) {
// Even though we do execute notifications, we do not respond to them
req->WriteReply(HTTP_NO_CONTENT);
return true;
}

// array of requests
} else if (valRequest.isArray()) {
// Check authorization for each request's method
if (user_has_whitelist) {
for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) {
if (!valRequest[reqIdx].isObject()) {
Expand All @@ -223,18 +236,49 @@ static bool HTTPReq_JSONRPC(const std::any& context, HTTPRequest* req)
}
}
}
strReply = JSONRPCExecBatch(jreq, valRequest.get_array());

// Execute each request
reply = UniValue::VARR;
for (size_t i{0}; i < valRequest.size(); ++i) {
// Batches never throw HTTP errors, they are always just included
// in "HTTP OK" responses. Notifications never get any response.
UniValue response;
try {
jreq.parse(valRequest[i]);
response = JSONRPCExec(jreq, /*catch_errors=*/true);
} catch (UniValue& e) {
response = JSONRPCReplyObj(NullUniValue, std::move(e), jreq.id, jreq.m_json_version);
} catch (const std::exception& e) {
response = JSONRPCReplyObj(NullUniValue, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id, jreq.m_json_version);
}
if (!jreq.IsNotification()) {
reply.push_back(std::move(response));
}
}
// Return no response for an all-notification batch, but only if the
// batch request is non-empty. Technically according to the JSON-RPC
// 2.0 spec, an empty batch request should also return no response,
// However, if the batch request is empty, it means the request did
// not contain any JSON-RPC version numbers, so returning an empty
// response could break backwards compatibility with old RPC clients
// relying on previous behavior. Return an empty array instead of an
// empty response in this case to favor being backwards compatible
// over complying with the JSON-RPC 2.0 spec in this case.
if (reply.size() == 0 && valRequest.size() > 0) {
req->WriteReply(HTTP_NO_CONTENT);
return true;
}
}
else
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");

req->WriteHeader("Content-Type", "application/json");
req->WriteReply(HTTP_OK, strReply);
} catch (const UniValue& objError) {
JSONErrorReply(req, objError, jreq.id);
req->WriteReply(HTTP_OK, reply.write() + "\n");
} catch (UniValue& e) {
JSONErrorReply(req, std::move(e), jreq);
return false;
} catch (const std::exception& e) {
JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq);
return false;
}
return true;
Expand Down
Loading

0 comments on commit 8aff7d6

Please sign in to comment.