mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
Merge pull request #4412 from PastaPastaPasta/backport-trivial-pr15
Backport trivial pr15
This commit is contained in:
commit
c829f1a889
@ -21,5 +21,5 @@ test/lint/lint-all.sh
|
||||
if [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then
|
||||
git log --merges --before="2 days ago" -1 --format='%H' > ./contrib/verify-commits/trusted-sha512-root-commit
|
||||
while read -r LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys &&
|
||||
travis_wait 50 contrib/verify-commits/verify-commits.py --clean-merge=2;
|
||||
./contrib/verify-commits/verify-commits.py --clean-merge=2;
|
||||
fi
|
||||
|
@ -25,6 +25,7 @@ BITCOIND_BIN=$(top_builddir)/src/$(BITCOIN_DAEMON_NAME)$(EXEEXT)
|
||||
BITCOIN_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT)
|
||||
BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT)
|
||||
BITCOIN_TX_BIN=$(top_builddir)/src/$(BITCOIN_TX_NAME)$(EXEEXT)
|
||||
BITCOIN_WALLET_BIN=$(top_builddir)/src/$(BITCOIN_WALLET_TOOL_NAME)$(EXEEXT)
|
||||
BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT)
|
||||
|
||||
empty :=
|
||||
@ -81,6 +82,7 @@ $(BITCOIN_WIN_INSTALLER): all-recursive
|
||||
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_QT_BIN) $(top_builddir)/release
|
||||
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_CLI_BIN) $(top_builddir)/release
|
||||
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_TX_BIN) $(top_builddir)/release
|
||||
STRIPPROG="$(STRIP)" $(INSTALL_STRIP_PROGRAM) $(BITCOIN_WALLET_BIN) $(top_builddir)/release
|
||||
@test -f $(MAKENSIS) && $(MAKENSIS) -V2 $(top_builddir)/share/setup.nsi || \
|
||||
echo error: could not build $@
|
||||
@echo built $@
|
||||
@ -177,6 +179,9 @@ $(BITCOIN_CLI_BIN): FORCE
|
||||
$(BITCOIN_TX_BIN): FORCE
|
||||
$(MAKE) -C src $(@F)
|
||||
|
||||
$(BITCOIN_WALLET_BIN): FORCE
|
||||
$(MAKE) -C src $(@F)
|
||||
|
||||
if USE_LCOV
|
||||
LCOV_FILTER_PATTERN=-p "/usr/include/" -p "/usr/lib/" -p "src/leveldb/" -p "src/crc32c/" -p "src/bench/" -p "src/univalue" -p "src/crypto/ctaes" -p "src/secp256k1"
|
||||
|
||||
|
@ -47,13 +47,12 @@ def git_config_get(option, default=None):
|
||||
except subprocess.CalledProcessError:
|
||||
return default
|
||||
|
||||
def retrieve_pr_info(repo,pull,ghtoken):
|
||||
def retrieve_json(req, ghtoken):
|
||||
'''
|
||||
Retrieve pull request information from github.
|
||||
Return None if no title can be found, or an error happens.
|
||||
Retrieve json from github.
|
||||
Return None if an error happens.
|
||||
'''
|
||||
try:
|
||||
req = Request("https://api.github.com/repos/"+repo+"/pulls/"+pull)
|
||||
if ghtoken is not None:
|
||||
req.add_header('Authorization', 'token ' + ghtoken)
|
||||
result = urlopen(req)
|
||||
@ -69,6 +68,18 @@ def retrieve_pr_info(repo,pull,ghtoken):
|
||||
print('Warning: unable to retrieve pull information from github: %s' % e)
|
||||
return None
|
||||
|
||||
def retrieve_pr_info(repo,pull,ghtoken):
|
||||
req = Request("https://api.github.com/repos/"+repo+"/pulls/"+pull)
|
||||
return retrieve_json(req,ghtoken)
|
||||
|
||||
def retrieve_pr_comments(repo,pull,ghtoken):
|
||||
req = Request("https://api.github.com/repos/"+repo+"/issues/"+pull+"/comments")
|
||||
return retrieve_json(req,ghtoken)
|
||||
|
||||
def retrieve_pr_reviews(repo,pull,ghtoken):
|
||||
req = Request("https://api.github.com/repos/"+repo+"/pulls/"+pull+"/reviews")
|
||||
return retrieve_json(req,ghtoken)
|
||||
|
||||
def ask_prompt(text):
|
||||
print(text,end=" ",file=stderr)
|
||||
stderr.flush()
|
||||
@ -133,6 +144,16 @@ def tree_sha512sum(commit='HEAD'):
|
||||
raise IOError('Non-zero return value executing git cat-file')
|
||||
return overall.hexdigest()
|
||||
|
||||
def get_acks_from_comments(head_commit, comments):
|
||||
assert len(head_commit) == 6
|
||||
ack_str ='\n\nACKs for commit {}:\n'.format(head_commit)
|
||||
for c in comments:
|
||||
review = [l for l in c['body'].split('\r\n') if 'ACK' in l and head_commit in l]
|
||||
if review:
|
||||
ack_str += ' {}:\n'.format(c['user']['login'])
|
||||
ack_str += ' {}\n'.format(review[0])
|
||||
return ack_str
|
||||
|
||||
def print_merge_details(pull, title, branch, base_branch, head_branch):
|
||||
print('%s#%s%s %s %sinto %s%s' % (ATTR_RESET+ATTR_PR,pull,ATTR_RESET,title,ATTR_RESET+ATTR_PR,branch,ATTR_RESET))
|
||||
subprocess.check_call([GIT,'log','--graph','--topo-order','--pretty=format:'+COMMIT_FORMAT,base_branch+'..'+head_branch])
|
||||
@ -185,6 +206,9 @@ def main():
|
||||
info = retrieve_pr_info(repo,pull,ghtoken)
|
||||
if info is None:
|
||||
sys.exit(1)
|
||||
comments = retrieve_pr_comments(repo,pull,ghtoken) + retrieve_pr_reviews(repo,pull,ghtoken)
|
||||
if comments is None:
|
||||
sys.exit(1)
|
||||
title = info['title'].strip()
|
||||
body = info['body'].strip()
|
||||
# precedence order for destination branch argument:
|
||||
@ -238,6 +262,7 @@ def main():
|
||||
message = firstline + '\n\n'
|
||||
message += subprocess.check_output([GIT,'log','--no-merges','--topo-order','--pretty=format:%H %s (%an)',base_branch+'..'+head_branch]).decode('utf-8')
|
||||
message += '\n\nPull request description:\n\n ' + body.replace('\n', '\n ') + '\n'
|
||||
message += get_acks_from_comments(head_commit=subprocess.check_output([GIT,'log','-1','--pretty=format:%H',head_branch]).decode('utf-8')[:6], comments=comments)
|
||||
try:
|
||||
subprocess.check_call([GIT,'merge','-q','--commit','--no-edit','--no-ff','--no-gpg-sign','-m',message.encode('utf-8'),head_branch])
|
||||
except subprocess.CalledProcessError:
|
||||
|
@ -5,6 +5,7 @@
|
||||
"""Verify commits against a trusted keys list."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
@ -66,6 +67,11 @@ def tree_sha512sum(commit='HEAD'):
|
||||
return overall.hexdigest()
|
||||
|
||||
def main():
|
||||
|
||||
# Enable debug logging if running in CI
|
||||
if 'CI' in os.environ and os.environ['CI'].lower() == "true":
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
# Parse arguments
|
||||
parser = argparse.ArgumentParser(usage='%(prog)s [options] [commit id]')
|
||||
parser.add_argument('--disable-tree-check', action='store_false', dest='verify_tree', help='disable SHA-512 tree check')
|
||||
@ -95,6 +101,10 @@ def main():
|
||||
|
||||
# Iterate through commits
|
||||
while True:
|
||||
|
||||
# Log a message to prevent Travis from timing out
|
||||
logging.debug("verify-commits: [in-progress] processing commit {}".format(current_commit[:8]))
|
||||
|
||||
if current_commit == verified_root:
|
||||
print('There is a valid path from "{}" to {} where all commits are signed!'.format(initial_commit, verified_root))
|
||||
sys.exit(0)
|
||||
|
@ -5,6 +5,97 @@ The headless daemon `dashd` has the JSON-RPC API enabled by default, the GUI
|
||||
option. In the GUI it is possible to execute RPC methods in the Debug Console
|
||||
Dialog.
|
||||
|
||||
## Versioning
|
||||
|
||||
The RPC interface might change from one major version of Dash Core to the
|
||||
next. This makes the RPC interface implicitly versioned on the major version.
|
||||
The version tuple can be retrieved by e.g. the `getnetworkinfo` RPC in
|
||||
`version`.
|
||||
|
||||
Usually deprecated features can be re-enabled during the grace-period of one
|
||||
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.
|
||||
|
||||
## Security
|
||||
|
||||
The RPC interface allows other programs to control Dash Core,
|
||||
including the ability to spend funds from your wallets, affect consensus
|
||||
verification, read private data, and otherwise perform operations that
|
||||
can cause loss of money, data, or privacy. This section suggests how
|
||||
you should use and configure Dash Core to reduce the risk that its
|
||||
RPC interface will be abused.
|
||||
|
||||
- **Securing the executable:** Anyone with physical or remote access to
|
||||
the computer, container, or virtual machine running Dash Core can
|
||||
compromise either the whole program or just the RPC interface. This
|
||||
includes being able to record any passphrases you enter for unlocking
|
||||
your encrypted wallets or changing settings so that your Dash Core
|
||||
program tells you that certain transactions have multiple
|
||||
confirmations even when they aren't part of the best block chain. For
|
||||
this reason, you should not use Dash Core for security sensitive
|
||||
operations on systems you do not exclusively control, such as shared
|
||||
computers or virtual private servers.
|
||||
|
||||
- **Securing local network access:** By default, the RPC interface can
|
||||
only be accessed by a client running on the same computer and only
|
||||
after the client provides a valid authentication credential (username
|
||||
and passphrase). Any program on your computer with access to the file
|
||||
system and local network can obtain this level of access.
|
||||
Additionally, other programs on your computer can attempt to provide
|
||||
an RPC interface on the same port as used by Dash Core in order to
|
||||
trick you into revealing your authentication credentials. For this
|
||||
reason, it is important to only use Dash Core for
|
||||
security-sensitive operations on a computer whose other programs you
|
||||
trust.
|
||||
|
||||
- **Securing remote network access:** You may optionally allow other
|
||||
computers to remotely control Dash Core by setting the `rpcallowip`
|
||||
and `rpcbind` configuration parameters. These settings are only meant
|
||||
for enabling connections over secure private networks or connections
|
||||
that have been otherwise secured (e.g. using a VPN or port forwarding
|
||||
with SSH or stunnel). **Do not enable RPC connections over the public
|
||||
Internet.** Although Dash Core's RPC interface does use
|
||||
authentication, it does not use encryption, so your login credentials
|
||||
are sent as clear text that can be read by anyone on your network
|
||||
path. Additionally, the RPC interface has not been hardened to
|
||||
withstand arbitrary Internet traffic, so changing the above settings
|
||||
to expose it to the Internet (even using something like a Tor hidden
|
||||
service) could expose you to unconsidered vulnerabilities. See
|
||||
`dashd -help` for more information about these settings and other
|
||||
settings described in this document.
|
||||
|
||||
Related, if you use Dash Core inside a Docker container, you may
|
||||
need to expose the RPC port to the host system. The default way to
|
||||
do this in Docker also exposes the port to the public Internet.
|
||||
Instead, expose it only on the host system's localhost, for example:
|
||||
`-p 127.0.0.1:8332:8332`
|
||||
|
||||
- **Secure authentication:** By default, Dash Core generates unique
|
||||
login credentials each time it restarts and puts them into a file
|
||||
readable only by the user that started Dash Core, allowing any of
|
||||
that user's RPC clients with read access to the file to login
|
||||
automatically. The file is `.cookie` in the Dash Core
|
||||
configuration directory, and using these credentials is the preferred
|
||||
RPC authentication method. If you need to generate static login
|
||||
credentials for your programs, you can use the script in the
|
||||
`share/rpcauth` directory in the Dash Core source tree. As a final
|
||||
fallback, you can directly use manually-chosen `rpcuser` and
|
||||
`rpcpassword` configuration parameters---but you must ensure that you
|
||||
choose a strong and unique passphrase (and still don't use insecure
|
||||
networks, as mentioned above).
|
||||
|
||||
- **Secure string handling:** The RPC interface does not guarantee any
|
||||
escaping of data beyond what's necessary to encode it as JSON,
|
||||
although it does usually provide serialized data using a hex
|
||||
representation of the bytes. If you use RPC data in your programs or
|
||||
provide its data to other programs, you must ensure any problem
|
||||
strings are properly escaped. For example, multiple websites have
|
||||
been manipulated because they displayed decoded hex strings that
|
||||
included HTML `<script>` tags. For this reason, and other
|
||||
non-security reasons, it is recommended to display all serialized data
|
||||
in hex form only.
|
||||
|
||||
## RPC consistency guarantees
|
||||
|
||||
State that can be queried via RPCs is guaranteed to be at least up-to-date with
|
||||
|
@ -80,6 +80,7 @@ Section -Main SEC0000
|
||||
File @abs_top_srcdir@/release/@BITCOIN_DAEMON_NAME@@EXEEXT@
|
||||
File @abs_top_srcdir@/release/@BITCOIN_CLI_NAME@@EXEEXT@
|
||||
File @abs_top_srcdir@/release/@BITCOIN_TX_NAME@@EXEEXT@
|
||||
File @abs_top_srcdir@/release/@BITCOIN_WALLET_TOOL_NAME@@EXEEXT@
|
||||
SetOutPath $INSTDIR\doc
|
||||
File /r /x Makefile* @abs_top_srcdir@/doc\*.*
|
||||
SetOutPath $INSTDIR
|
||||
|
@ -180,6 +180,7 @@ static std::string openmodeToStr(std::ios_base::openmode mode)
|
||||
void ifstream::open(const fs::path& p, std::ios_base::openmode mode)
|
||||
{
|
||||
close();
|
||||
mode |= std::ios_base::in;
|
||||
m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
|
||||
if (m_file == nullptr) {
|
||||
return;
|
||||
@ -203,6 +204,7 @@ void ifstream::close()
|
||||
void ofstream::open(const fs::path& p, std::ios_base::openmode mode)
|
||||
{
|
||||
close();
|
||||
mode |= std::ios_base::out;
|
||||
m_file = fsbridge::fopen(p, openmodeToStr(mode).c_str());
|
||||
if (m_file == nullptr) {
|
||||
return;
|
||||
|
@ -211,7 +211,7 @@ Q_SIGNALS:
|
||||
void requestedRestart(QStringList args);
|
||||
void requestedShutdown();
|
||||
void stopThread();
|
||||
void splashFinished(QWidget *window);
|
||||
void splashFinished();
|
||||
|
||||
private:
|
||||
QThread *coreThread;
|
||||
@ -358,9 +358,9 @@ void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle)
|
||||
{
|
||||
SplashScreen *splash = new SplashScreen(m_node, nullptr, networkStyle);
|
||||
// We don't hold a direct pointer to the splash screen after creation, but the splash
|
||||
// screen will take care of deleting itself when slotFinish happens.
|
||||
// screen will take care of deleting itself when finish() happens.
|
||||
splash->show();
|
||||
connect(this, &BitcoinApplication::splashFinished, splash, &SplashScreen::slotFinish);
|
||||
connect(this, &BitcoinApplication::splashFinished, splash, &SplashScreen::finish);
|
||||
connect(this, &BitcoinApplication::requestedShutdown, splash, &QWidget::close);
|
||||
}
|
||||
|
||||
@ -501,7 +501,7 @@ void BitcoinApplication::initializeResult(bool success)
|
||||
} else {
|
||||
window->showMinimized();
|
||||
}
|
||||
Q_EMIT splashFinished(window);
|
||||
Q_EMIT splashFinished();
|
||||
|
||||
// Let the users setup their preferred appearance if there are no settings for it defined yet.
|
||||
GUIUtil::setupAppearance(window, clientModel->getOptionsModel());
|
||||
@ -518,7 +518,7 @@ void BitcoinApplication::initializeResult(bool success)
|
||||
#endif
|
||||
pollShutdownTimer->start(200);
|
||||
} else {
|
||||
Q_EMIT splashFinished(window); // Make sure splash screen doesn't stick around during shutdown
|
||||
Q_EMIT splashFinished(); // Make sure splash screen doesn't stick around during shutdown
|
||||
quit(); // Exit first main loop invocation
|
||||
}
|
||||
}
|
||||
|
@ -149,10 +149,8 @@ bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) {
|
||||
return QObject::eventFilter(obj, ev);
|
||||
}
|
||||
|
||||
void SplashScreen::slotFinish(QWidget *mainWin)
|
||||
void SplashScreen::finish()
|
||||
{
|
||||
Q_UNUSED(mainWin);
|
||||
|
||||
/* If the window is minimized, hide() will be ignored. */
|
||||
/* Make sure we de-minimize the splashscreen window before hiding */
|
||||
if (isMinimized())
|
||||
|
@ -5,8 +5,7 @@
|
||||
#ifndef BITCOIN_QT_SPLASHSCREEN_H
|
||||
#define BITCOIN_QT_SPLASHSCREEN_H
|
||||
|
||||
#include <functional>
|
||||
#include <QSplashScreen>
|
||||
#include <QWidget>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@ -37,8 +36,8 @@ protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
public Q_SLOTS:
|
||||
/** Slot to call finish() method as it's not defined as slot */
|
||||
void slotFinish(QWidget *mainWin);
|
||||
/** Hide the splash screen window and schedule the splash screen object for deletion */
|
||||
void finish();
|
||||
|
||||
/** Show message and progress */
|
||||
void showMessage(const QString &message, int alignment, const QColor &color);
|
||||
|
@ -49,15 +49,11 @@ void WalletFrame::setClientModel(ClientModel *_clientModel)
|
||||
}
|
||||
}
|
||||
|
||||
bool WalletFrame::addWallet(WalletModel *walletModel)
|
||||
void WalletFrame::addWallet(WalletModel *walletModel)
|
||||
{
|
||||
if (!gui || !clientModel || !walletModel) {
|
||||
return false;
|
||||
}
|
||||
if (!gui || !clientModel || !walletModel) return;
|
||||
|
||||
if (mapWalletViews.count(walletModel) > 0) {
|
||||
return false;
|
||||
}
|
||||
if (mapWalletViews.count(walletModel) > 0) return;
|
||||
|
||||
WalletView* walletView = new WalletView(this);
|
||||
walletView->setBitcoinGUI(gui);
|
||||
@ -81,31 +77,25 @@ bool WalletFrame::addWallet(WalletModel *walletModel)
|
||||
});
|
||||
|
||||
connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalletFrame::setCurrentWallet(WalletModel* wallet_model)
|
||||
void WalletFrame::setCurrentWallet(WalletModel* wallet_model)
|
||||
{
|
||||
if (mapWalletViews.count(wallet_model) == 0)
|
||||
return false;
|
||||
if (mapWalletViews.count(wallet_model) == 0) return;
|
||||
|
||||
WalletView *walletView = mapWalletViews.value(wallet_model);
|
||||
walletStack->setCurrentWidget(walletView);
|
||||
assert(walletView);
|
||||
walletView->updateEncryptionStatus();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WalletFrame::removeWallet(WalletModel* wallet_model)
|
||||
void WalletFrame::removeWallet(WalletModel* wallet_model)
|
||||
{
|
||||
if (mapWalletViews.count(wallet_model) == 0)
|
||||
return false;
|
||||
if (mapWalletViews.count(wallet_model) == 0) return;
|
||||
|
||||
WalletView *walletView = mapWalletViews.take(wallet_model);
|
||||
walletStack->removeWidget(walletView);
|
||||
delete walletView;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WalletFrame::removeAllWallets()
|
||||
|
@ -36,9 +36,9 @@ public:
|
||||
|
||||
void setClientModel(ClientModel *clientModel);
|
||||
|
||||
bool addWallet(WalletModel *walletModel);
|
||||
bool setCurrentWallet(WalletModel* wallet_model);
|
||||
bool removeWallet(WalletModel* wallet_model);
|
||||
void addWallet(WalletModel *walletModel);
|
||||
void setCurrentWallet(WalletModel* wallet_model);
|
||||
void removeWallet(WalletModel* wallet_model);
|
||||
void removeAllWallets();
|
||||
|
||||
bool handlePaymentRequest(const SendCoinsRecipient& recipient);
|
||||
|
@ -935,7 +935,7 @@ static bool GetConfigOptions(std::istream& stream, std::string& error, std::vect
|
||||
} else if ((pos = str.find('=')) != std::string::npos) {
|
||||
std::string name = prefix + TrimString(str.substr(0, pos), pattern);
|
||||
std::string value = TrimString(str.substr(pos + 1), pattern);
|
||||
if (used_hash && name == "rpcpassword") {
|
||||
if (used_hash && name.find("rpcpassword") != std::string::npos) {
|
||||
error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr);
|
||||
return false;
|
||||
}
|
||||
|
@ -4462,19 +4462,6 @@ UniValue importmulti(const JSONRPCRequest& request);
|
||||
UniValue dumphdinfo(const JSONRPCRequest& request);
|
||||
UniValue importelectrumwallet(const JSONRPCRequest& request);
|
||||
|
||||
void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubKey, KeyOriginInfo>& hd_keypaths)
|
||||
{
|
||||
CPubKey vchPubKey;
|
||||
if (!pwallet->GetPubKey(keyID, vchPubKey)) {
|
||||
return;
|
||||
}
|
||||
KeyOriginInfo info;
|
||||
if (!pwallet->GetKeyOrigin(keyID, info)) {
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal keypath is broken");
|
||||
}
|
||||
hd_keypaths.emplace(vchPubKey, std::move(info));
|
||||
}
|
||||
|
||||
UniValue walletprocesspsbt(const JSONRPCRequest& request)
|
||||
{
|
||||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <wallet/walletutil.h>
|
||||
|
||||
#include <logging.h>
|
||||
#include <util/system.h>
|
||||
|
||||
fs::path GetWalletDir()
|
||||
@ -33,9 +34,11 @@ static bool IsBerkeleyBtree(const fs::path& path)
|
||||
// A Berkeley DB Btree file has at least 4K.
|
||||
// This check also prevents opening lock files.
|
||||
boost::system::error_code ec;
|
||||
if (fs::file_size(path, ec) < 4096) return false;
|
||||
auto size = fs::file_size(path, ec);
|
||||
if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), path.string());
|
||||
if (size < 4096) return false;
|
||||
|
||||
fs::ifstream file(path.string(), std::ios::binary);
|
||||
fsbridge::ifstream file(path, std::ios::binary);
|
||||
if (!file.is_open()) return false;
|
||||
|
||||
file.seekg(12, std::ios::beg); // Magic bytes start at offset 12
|
||||
@ -54,8 +57,14 @@ std::vector<fs::path> ListWalletDir()
|
||||
const fs::path wallet_dir = GetWalletDir();
|
||||
const size_t offset = wallet_dir.string().size() + 1;
|
||||
std::vector<fs::path> paths;
|
||||
boost::system::error_code ec;
|
||||
|
||||
for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) {
|
||||
if (ec) {
|
||||
LogPrintf("%s: %s %s\n", __func__, ec.message(), it->path().string());
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto it = fs::recursive_directory_iterator(wallet_dir); it != fs::recursive_directory_iterator(); ++it) {
|
||||
// Get wallet path relative to walletdir by removing walletdir from the wallet path.
|
||||
// This can be replaced by boost::filesystem::lexically_relative once boost is bumped to 1.60.
|
||||
const fs::path path = it->path().string().substr(offset);
|
||||
|
@ -43,6 +43,14 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
conf.write('server=1\nrpcuser=someuser\nrpcpassword=some#pass')
|
||||
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
|
||||
|
||||
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
|
||||
conf.write('server=1\nrpcuser=someuser\nmain.rpcpassword=some#pass')
|
||||
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 3, using # in rpcpassword can be ambiguous and should be avoided')
|
||||
|
||||
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
|
||||
conf.write('server=1\nrpcuser=someuser\n[main]\nrpcpassword=some#pass')
|
||||
self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 4, using # in rpcpassword can be ambiguous and should be avoided')
|
||||
|
||||
with open(inc_conf_file_path, 'w', encoding='utf-8') as conf:
|
||||
conf.write('testnot.datadir=1\n[testnet]\n')
|
||||
self.restart_node(0)
|
||||
|
@ -4,9 +4,19 @@
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Tests some generic aspects of the RPC interface."""
|
||||
|
||||
from test_framework.authproxy import JSONRPCException
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import assert_equal
|
||||
|
||||
def expect_http_status(expected_http_status, expected_rpc_code,
|
||||
fcn, *args):
|
||||
try:
|
||||
fcn(*args)
|
||||
raise AssertionError("Expected RPC error %d, got none" % expected_rpc_code)
|
||||
except JSONRPCException as exc:
|
||||
assert_equal(exc.error["code"], expected_rpc_code)
|
||||
assert_equal(exc.http_status, expected_http_status)
|
||||
|
||||
class RPCInterfaceTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 1
|
||||
@ -38,8 +48,15 @@ class RPCInterfaceTest(BitcoinTestFramework):
|
||||
assert_equal(result_by_id[3]['error'], None)
|
||||
assert result_by_id[3]['result'] is not None
|
||||
|
||||
def test_http_status_codes(self):
|
||||
self.log.info("Testing HTTP status codes for JSON-RPC requests...")
|
||||
|
||||
expect_http_status(404, -32601, self.nodes[0].invalidmethod)
|
||||
expect_http_status(500, -8, self.nodes[0].getblockhash, 42)
|
||||
|
||||
def run_test(self):
|
||||
self.test_batch_request()
|
||||
self.test_http_status_codes()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -35,6 +35,7 @@ ServiceProxy class:
|
||||
|
||||
import base64
|
||||
import decimal
|
||||
from http import HTTPStatus
|
||||
import http.client
|
||||
import json
|
||||
import logging
|
||||
@ -48,13 +49,14 @@ USER_AGENT = "AuthServiceProxy/0.1"
|
||||
log = logging.getLogger("BitcoinRPC")
|
||||
|
||||
class JSONRPCException(Exception):
|
||||
def __init__(self, rpc_error):
|
||||
def __init__(self, rpc_error, http_status=None):
|
||||
try:
|
||||
errmsg = '%(message)s (%(code)i)' % rpc_error
|
||||
except (KeyError, TypeError):
|
||||
errmsg = ''
|
||||
super().__init__(errmsg)
|
||||
self.error = rpc_error
|
||||
self.http_status = http_status
|
||||
|
||||
|
||||
def EncodeDecimal(o):
|
||||
@ -136,19 +138,26 @@ class AuthServiceProxy():
|
||||
|
||||
def __call__(self, *args, **argsn):
|
||||
postdata = json.dumps(self.get_request(*args, **argsn), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
|
||||
response = self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
||||
response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
||||
if response['error'] is not None:
|
||||
raise JSONRPCException(response['error'])
|
||||
raise JSONRPCException(response['error'], status)
|
||||
elif 'result' not in response:
|
||||
raise JSONRPCException({
|
||||
'code': -343, 'message': 'missing JSON-RPC result'})
|
||||
'code': -343, 'message': 'missing JSON-RPC result'}, status)
|
||||
elif status != HTTPStatus.OK:
|
||||
raise JSONRPCException({
|
||||
'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
|
||||
else:
|
||||
return response['result']
|
||||
|
||||
def batch(self, rpc_call_list):
|
||||
postdata = json.dumps(list(rpc_call_list), default=EncodeDecimal, ensure_ascii=self.ensure_ascii)
|
||||
log.debug("--> " + postdata)
|
||||
return self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
||||
response, status = self._request('POST', self.__url.path, postdata.encode('utf-8'))
|
||||
if status != HTTPStatus.OK:
|
||||
raise JSONRPCException({
|
||||
'code': -342, 'message': 'non-200 HTTP status code but no JSON-RPC error'}, status)
|
||||
return response
|
||||
|
||||
def _get_response(self):
|
||||
req_start_time = time.time()
|
||||
@ -167,8 +176,9 @@ class AuthServiceProxy():
|
||||
|
||||
content_type = http_response.getheader('Content-Type')
|
||||
if content_type != 'application/json':
|
||||
raise JSONRPCException({
|
||||
'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)})
|
||||
raise JSONRPCException(
|
||||
{'code': -342, 'message': 'non-JSON HTTP response with \'%i %s\' from server' % (http_response.status, http_response.reason)},
|
||||
http_response.status)
|
||||
|
||||
responsedata = http_response.read().decode('utf8')
|
||||
response = json.loads(responsedata, parse_float=decimal.Decimal)
|
||||
@ -177,7 +187,7 @@ class AuthServiceProxy():
|
||||
log.debug("<-%s- [%.6f] %s" % (response["id"], elapsed, json.dumps(response["result"], default=EncodeDecimal, ensure_ascii=self.ensure_ascii)))
|
||||
else:
|
||||
log.debug("<-- [%.6f] %s" % (elapsed, responsedata))
|
||||
return response
|
||||
return response, http_response.status
|
||||
|
||||
def __truediv__(self, relative_uri):
|
||||
return AuthServiceProxy("{}/{}".format(self.__service_url, relative_uri), self._service_name, connection=self.__conn)
|
||||
|
Loading…
Reference in New Issue
Block a user