Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport: Merge bitcoin#15367, 17458, 18309, 18965, 16525 #5728

Merged
merged 4 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/release-notes-15367.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Configuration option changes
----------------------------

- The `startupnotify` option is used to specify a command to
execute when Dash Core has finished with its startup
sequence. (#5728)
9 changes: 9 additions & 0 deletions doc/release-notes-16525.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
RPC changes
-----------

Exposed transaction version numbers are now treated as unsigned 16-bit integers
instead of signed 16-bit integers. This matches their treatment in consensus
logic. Versions greater than 3 continue to be non-standard (matching previous
behavior of smaller than 1 or greater than 3 being non-standard). Note that
this includes the joinpsbt command, which combines partially-signed
transactions by selecting the highest version number.
4 changes: 4 additions & 0 deletions doc/release-notes-18309.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Command-line options
-----------------------------

The same ZeroMQ notification (e.g. `-zmqpubhashtx=address`) can now be specified multiple times to publish the same notification to different ZeroMQ sockets.
2 changes: 2 additions & 0 deletions doc/zmq.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Currently, the following notifications are supported:

The socket type is PUB and the address must be a valid ZeroMQ socket
address. The same address can be used in more than one notification.
The same notification can be specified more than once.

The option to set the PUB socket's outbound message high water mark
(SNDHWM) may be set individually for each notification:
Expand Down Expand Up @@ -108,6 +109,7 @@ The high water mark value must be an integer greater than or equal to 0.
For instance:

$ dashd -zmqpubhashtx=tcp://127.0.0.1:28332 \
-zmqpubhashtx=tcp://192.168.1.2:28332 \
-zmqpubrawtx=ipc:///tmp/dashd.tx.raw \
-zmqpubhashtxhwm=10000

Expand Down
4 changes: 3 additions & 1 deletion src/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,9 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
{
uint256 txid = tx.GetHash();
entry.pushKV("txid", txid.GetHex());
entry.pushKV("version", tx.nVersion);
// Transaction version is actually unsigned in consensus checks, just signed in memory,
// so cast to unsigned before giving it to the user.
entry.pushKV("version", static_cast<int64_t>(static_cast<uint16_t>(tx.nVersion)));
entry.pushKV("type", tx.nType);
entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION));
entry.pushKV("locktime", (int64_t)tx.nLockTime);
Expand Down
18 changes: 18 additions & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,9 @@ void SetupServerArgs(NodeContext& node)
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-settings=<file>", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-syncmempool", strprintf("Sync mempool from other nodes on start (default: %u)", DEFAULT_SYNC_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#if HAVE_SYSTEM
argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#endif
#ifndef WIN32
argsman.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
#else
Expand Down Expand Up @@ -859,6 +862,17 @@ static void CleanupBlockRevFiles()
}
}

#if HAVE_SYSTEM
static void StartupNotify(const ArgsManager& args)
{
std::string cmd = args.GetArg("-startupnotify", "");
if (!cmd.empty()) {
std::thread t(runCommand, cmd);
t.detach(); // thread runs free
}
}
#endif

static void PeriodicStats(ArgsManager& args, const CTxMemPool& mempool)
{
assert(args.GetBoolArg("-statsenabled", DEFAULT_STATSD_ENABLE));
Expand Down Expand Up @@ -2537,5 +2551,9 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc
banman->DumpBanlist();
}, DUMP_BANS_INTERVAL);

#if HAVE_SYSTEM
StartupNotify(args);
#endif

return true;
}
8 changes: 4 additions & 4 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1604,7 +1604,7 @@ UniValue joinpsbts(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "At least two PSBTs are required to join PSBTs.");
}

int32_t best_version = 1;
uint16_t best_version = 1;
uint32_t best_locktime = 0xffffffff;
for (unsigned int i = 0; i < txs.size(); ++i) {
PartiallySignedTransaction psbtx;
Expand All @@ -1614,8 +1614,8 @@ UniValue joinpsbts(const JSONRPCRequest& request)
}
psbtxs.push_back(psbtx);
// Choose the highest version number
if (psbtx.tx->nVersion > best_version) {
best_version = psbtx.tx->nVersion;
if (static_cast<uint16_t>(psbtx.tx->nVersion) > best_version) {
best_version = static_cast<uint16_t>(psbtx.tx->nVersion);
}
// Choose the lowest lock time
if (psbtx.tx->nLockTime < best_locktime) {
Expand All @@ -1626,7 +1626,7 @@ UniValue joinpsbts(const JSONRPCRequest& request)
// Create a blank psbt where everything will be added
PartiallySignedTransaction merged_psbt;
merged_psbt.tx = CMutableTransaction();
merged_psbt.tx->nVersion = best_version;
merged_psbt.tx->nVersion = static_cast<int16_t>(best_version);
merged_psbt.tx->nLockTime = best_locktime;

// Merge
Expand Down
6 changes: 2 additions & 4 deletions src/zmq/zmqnotificationinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,8 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create()
for (const auto& entry : factories)
{
std::string arg("-zmq" + entry.first);
if (gArgs.IsArgSet(arg))
{
const auto& factory = entry.second;
const std::string address = gArgs.GetArg(arg, "");
const auto& factory = entry.second;
for (const std::string& address : gArgs.GetArgs(arg)) {
std::unique_ptr<CZMQAbstractNotifier> notifier = factory();
notifier->SetType(entry.first);
notifier->SetAddress(address);
Expand Down
16 changes: 8 additions & 8 deletions src/zmq/zmqpublishnotifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ bool CZMQAbstractPublishNotifier::SendZmqMessage(const char *command, const void
bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
uint256 hash = pindex->GetBlockHash();
LogPrint(BCLog::ZMQ, "zmq: Publish hashblock %s\n", hash.GetHex());
LogPrint(BCLog::ZMQ, "zmq: Publish hashblock %s to %s\n", hash.GetHex(), this->address);
char data[32];
for (unsigned int i = 0; i < 32; i++)
data[31 - i] = hash.begin()[i];
Expand All @@ -218,7 +218,7 @@ bool CZMQPublishHashChainLockNotifier::NotifyChainLock(const CBlockIndex *pindex
bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
LogPrint(BCLog::ZMQ, "zmq: Publish hashtx %s\n", hash.GetHex());
LogPrint(BCLog::ZMQ, "zmq: Publish hashtx %s to %s\n", hash.GetHex(), this->address);
char data[32];
for (unsigned int i = 0; i < 32; i++)
data[31 - i] = hash.begin()[i];
Expand Down Expand Up @@ -279,7 +279,7 @@ bool CZMQPublishHashRecoveredSigNotifier::NotifyRecoveredSig(const std::shared_p

bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
{
LogPrint(BCLog::ZMQ, "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex());
LogPrint(BCLog::ZMQ, "zmq: Publish rawblock %s to %s\n", pindex->GetBlockHash().GetHex(), this->address);

const Consensus::Params& consensusParams = Params().GetConsensus();
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
Expand Down Expand Up @@ -344,7 +344,7 @@ bool CZMQPublishRawChainLockSigNotifier::NotifyChainLock(const CBlockIndex *pind
bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
{
uint256 hash = transaction.GetHash();
LogPrint(BCLog::ZMQ, "zmq: Publish rawtx %s\n", hash.GetHex());
LogPrint(BCLog::ZMQ, "zmq: Publish rawtx %s to %s\n", hash.GetHex(), this->address);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << transaction;
return SendZmqMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
Expand All @@ -353,7 +353,7 @@ bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &tr
bool CZMQPublishRawTransactionLockNotifier::NotifyTransactionLock(const CTransactionRef& transaction, const std::shared_ptr<const llmq::CInstantSendLock>& islock)
{
uint256 hash = transaction->GetHash();
LogPrint(BCLog::ZMQ, "zmq: Publish rawtxlock %s\n", hash.GetHex());
LogPrint(BCLog::ZMQ, "zmq: Publish rawtxlock %s to %s\n", hash.GetHex(), this->address);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *transaction;
return SendZmqMessage(MSG_RAWTXLOCK, &(*ss.begin()), ss.size());
Expand All @@ -362,7 +362,7 @@ bool CZMQPublishRawTransactionLockNotifier::NotifyTransactionLock(const CTransac
bool CZMQPublishRawTransactionLockSigNotifier::NotifyTransactionLock(const CTransactionRef& transaction, const std::shared_ptr<const llmq::CInstantSendLock>& islock)
{
uint256 hash = transaction->GetHash();
LogPrint(BCLog::ZMQ, "zmq: Publish rawtxlocksig %s\n", hash.GetHex());
LogPrint(BCLog::ZMQ, "zmq: Publish rawtxlocksig %s to %s\n", hash.GetHex(), this->address);
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *transaction;
ss << *islock;
Expand All @@ -372,7 +372,7 @@ bool CZMQPublishRawTransactionLockSigNotifier::NotifyTransactionLock(const CTran
bool CZMQPublishRawGovernanceVoteNotifier::NotifyGovernanceVote(const std::shared_ptr<const CGovernanceVote>& vote)
{
uint256 nHash = vote->GetHash();
LogPrint(BCLog::ZMQ, "zmq: Publish rawgovernanceobject: hash = %s, vote = %d\n", nHash.ToString(), vote->ToString());
LogPrint(BCLog::ZMQ, "zmq: Publish rawgovernanceobject: hash = %s to %s, vote = %d\n", nHash.ToString(), this->address, vote->ToString());
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *vote;
return SendZmqMessage(MSG_RAWGVOTE, &(*ss.begin()), ss.size());
Expand All @@ -381,7 +381,7 @@ bool CZMQPublishRawGovernanceVoteNotifier::NotifyGovernanceVote(const std::share
bool CZMQPublishRawGovernanceObjectNotifier::NotifyGovernanceObject(const std::shared_ptr<const CGovernanceObject>& govobj)
{
uint256 nHash = govobj->GetHash();
LogPrint(BCLog::ZMQ, "zmq: Publish rawgovernanceobject: hash = %s, type = %d\n", nHash.ToString(), ToUnderlying(govobj->GetObjectType()));
LogPrint(BCLog::ZMQ, "zmq: Publish rawgovernanceobject: hash = %s to %s, type = %d\n", nHash.ToString(), this->address, ToUnderlying(govobj->GetObjectType()));
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
ss << *govobj;
return SendZmqMessage(MSG_RAWGOBJ, &(*ss.begin()), ss.size());
Expand Down
25 changes: 25 additions & 0 deletions test/functional/interface_zmq.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ def setup_nodes(self):
def run_test(self):
try:
self._zmq_test()
self.test_multiple_interfaces()
finally:
# Destroy the ZMQ context.
self.log.debug("Destroying ZMQ context")
Expand Down Expand Up @@ -131,5 +132,29 @@ def _zmq_test(self):

assert_equal(self.nodes[1].getzmqnotifications(), [])

def test_multiple_interfaces(self):
import zmq
# Set up two subscribers with different addresses
subscribers = []
for i in range(2):
address = 'tcp://127.0.0.1:%d' % (28334 + i)
socket = self.zmq_context.socket(zmq.SUB)
socket.set(zmq.RCVTIMEO, 60000)
hashblock = ZMQSubscriber(socket, b"hashblock")
socket.connect(address)
subscribers.append({'address': address, 'hashblock': hashblock})

self.restart_node(0, ['-zmqpub%s=%s' % (subscriber['hashblock'].topic.decode(), subscriber['address']) for subscriber in subscribers])

# Relax so that the subscriber is ready before publishing zmq messages
sleep(0.2)

# Generate 1 block in nodes[0] and receive all notifications
self.nodes[0].generatetoaddress(1, ADDRESS_BCRT1_UNSPENDABLE)

# Should receive the same block hash on both subscribers
assert_equal(self.nodes[0].getbestblockhash(), subscribers[0]['hashblock'].receive().hex())
assert_equal(self.nodes[0].getbestblockhash(), subscribers[1]['hashblock'].receive().hex())

if __name__ == '__main__':
ZMQTest().main()
3 changes: 2 additions & 1 deletion test/functional/rpc_rawtransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,12 @@ def run_test(self):
# Note, this is different to bitcoin. Bitcoin has a 32 bit integer
# representing the version, we have 16 bits of version and 16 bits of
# type.
# As transaction version is unsigned, this should convert to its unsigned equivalent.
tx = CTransaction()
tx.nVersion = -0x8000
rawtx = ToHex(tx)
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
assert_equal(decrawtx['version'], -0x8000)
assert_equal(decrawtx['version'], 0x8000)

# Test the maximum transaction version number that fits in a signed 32-bit integer.
tx = CTransaction()
Expand Down
48 changes: 47 additions & 1 deletion test/functional/test_framework/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
#
# This file encodes and decodes BASE58 P2PKH and P2SH addresses
#
import unittest

from .script import hash256, hash160, CScript
from .util import hex_str_to_bytes
from test_framework.util import assert_equal

# Note unlike in bitcoin, this address isn't bech32 since we don't (at this time) support bech32.
ADDRESS_BCRT1_UNSPENDABLE = 'yVg3NBUHNEhgDceqwVUjsZHreC5PBHnUo9'
Expand All @@ -35,7 +37,32 @@ def byte_to_base58(b, version):
str = str[2:]
return result

# TODO: def base58_decode

def base58_to_byte(s, verify_checksum=True):
if not s:
return b''
n = 0
for c in s:
n *= 58
assert c in chars
digit = chars.index(c)
n += digit
h = '%x' % n
if len(h) % 2:
h = '0' + h
res = n.to_bytes((n.bit_length() + 7) // 8, 'big')
pad = 0
for c in s:
if c == chars[0]:
pad += 1
else:
break
res = b'\x00' * pad + res
if verify_checksum:
assert_equal(hash256(res[:-4])[:4], res[-4:])

return res[1:-4], int(res[0])


def keyhash_to_p2pkh(hash, main = False):
assert len(hash) == 20
Expand Down Expand Up @@ -68,3 +95,22 @@ def check_script(script):
if (type(script) is bytes or type(script) is CScript):
return script
assert False


class TestFrameworkScript(unittest.TestCase):
def test_base58encodedecode(self):
def check_base58(data, version):
self.assertEqual(base58_to_byte(byte_to_base58(data, version)), (data, version))

check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 111)
check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 111)
check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 111)
check_base58(b'\x1f\x8e\xa1p*{\xd4\x94\x1b\xca\tA\xb8R\xc4\xbb\xfe\xdb.\x05', 0)
check_base58(b':\x0b\x05\xf4\xd7\xf6l;\xa7\x00\x9fE50)l\x84\\\xc9\xcf', 0)
check_base58(b'A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
check_base58(b'\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
check_base58(b'\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
check_base58(b'\0\0\0A\xc1\xea\xf1\x11\x80%Y\xba\xd6\x1b`\xd6+\x1f\x89|c\x92\x8a', 0)
1 change: 1 addition & 0 deletions test/functional/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
TEST_EXIT_SKIPPED = 77

TEST_FRAMEWORK_MODULES = [
"address",
"blocktools",
"muhash",
"script",
Expand Down
Loading