Merge pull request #5842 from kwvg/bps_jan_03

backport: merge bitcoin#19289, #19998, #17775, #19401, #20167, #20187, #19961, #21114, #21170 (auxiliary backports: part 12)
This commit is contained in:
PastaPastaPasta 2024-02-06 08:48:47 -06:00 committed by GitHub
commit 9c38b355ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 296 additions and 155 deletions

View File

@ -8,6 +8,16 @@ may not. In particular, the Tor Browser Bundle defaults to listening on port 915
See [Tor Project FAQ:TBBSocksPort](https://www.torproject.org/docs/faq.html.en#TBBSocksPort) See [Tor Project FAQ:TBBSocksPort](https://www.torproject.org/docs/faq.html.en#TBBSocksPort)
for how to properly configure Tor. for how to properly configure Tor.
## How to see information about your Tor configuration via Dash Core
There are several ways to see your local onion address in Dash Core:
- in the debug log (grep for "tor:" or "AddLocal")
- in the output of RPC `getnetworkinfo` in the "localaddresses" section
- in the output of the CLI `-netinfo` peer connections dashboard
You may set the `-debug=tor` config logging option to have additional
information in the debug log about your Tor configuration.
## 1. Run Dash Core behind a Tor proxy ## 1. Run Dash Core behind a Tor proxy
@ -160,14 +170,19 @@ The directory can be different of course, but virtual port numbers should be equ
your dashd's P2P listen port (9999 by default), and target addresses and ports your dashd's P2P listen port (9999 by default), and target addresses and ports
should be equal to binding address and port for inbound Tor connections (127.0.0.1:9996 by default). should be equal to binding address and port for inbound Tor connections (127.0.0.1:9996 by default).
-externalip=X You can tell Dash Core about its publicly reachable address using -externalip=X You can tell Dash Core about its publicly reachable addresses using
this option, and this can be a .onion address. Given the above this option, and this can be an onion address. Given the above
configuration, you can find your .onion address in configuration, you can find your onion address in
/var/lib/tor/dashcore-service/hostname. For connections /var/lib/tor/dashcore-service/hostname. For connections
coming from unroutable addresses (such as 127.0.0.1, where the coming from unroutable addresses (such as 127.0.0.1, where the
Tor proxy typically runs), .onion addresses are given Tor proxy typically runs), onion addresses are given
preference for your node to advertise itself with. preference for your node to advertise itself with.
You can set multiple local addresses with -externalip. The
one that will be rumoured to a particular peer is the most
compatible one and also using heuristics, e.g. the address
with the most incoming connections, etc.
-listen You'll need to enable listening for incoming connections, as this -listen You'll need to enable listening for incoming connections, as this
is off by default behind a proxy. is off by default behind a proxy.
@ -180,7 +195,7 @@ should be equal to binding address and port for inbound Tor connections (127.0.0
In a typical situation, where you're only reachable via Tor, this should suffice: In a typical situation, where you're only reachable via Tor, this should suffice:
./dashd -proxy=127.0.0.1:9050 -externalip=ssapp53tmftyjmjb.onion -listen ./dashd -proxy=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -listen
(obviously, replace the .onion address with your own). It should be noted that you still (obviously, replace the .onion address with your own). It should be noted that you still
listen on all devices and another node could establish a clearnet connection, when knowing listen on all devices and another node could establish a clearnet connection, when knowing
@ -198,7 +213,7 @@ and open port 9999 on your firewall (or use port mapping, i.e., `-upnp` or `-nat
If you only want to use Tor to reach .onion addresses, but not use it as a proxy If you only want to use Tor to reach .onion addresses, but not use it as a proxy
for normal IPv4/IPv6 communication, use: for normal IPv4/IPv6 communication, use:
./dashd -onion=127.0.0.1:9050 -externalip=ssapp53tmftyjmjb.onion -discover ./dashd -onion=127.0.0.1:9050 -externalip=7zvj7a2imdgkdbg4f2dryd5rgtrn7upivr5eeij4cicjh65pooxeshid.onion -discover
## 3.1. List of known Dash Core Tor relays ## 3.1. List of known Dash Core Tor relays

View File

@ -15,24 +15,49 @@
#include <univalue.h> #include <univalue.h>
static void BlockToJsonVerbose(benchmark::Bench& bench) { namespace {
struct TestBlockAndIndex {
TestingSetup test_setup{}; TestingSetup test_setup{};
CBlock block{};
uint256 blockHash{};
CBlockIndex blockindex{};
CDataStream stream(benchmark::data::block813851, SER_NETWORK, PROTOCOL_VERSION); TestBlockAndIndex()
char a = '\0'; {
stream.write(&a, 1); // Prevent compaction CDataStream stream(benchmark::data::block813851, SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
stream.write(&a, 1); // Prevent compaction
CBlock block; stream >> block;
stream >> block;
CBlockIndex blockindex; blockHash = block.GetHash();
const uint256 blockHash = block.GetHash(); blockindex.phashBlock = &blockHash;
blockindex.phashBlock = &blockHash; blockindex.nBits = 403014710;
blockindex.nBits = 403014710; }
};
} // namespace
static void BlockToJsonVerbose(benchmark::Bench& bench)
{
TestBlockAndIndex data;
bench.run([&] { bench.run([&] {
(void)blockToJSON(block, &blockindex, &blockindex, *llmq::chainLocksHandler, *llmq::quorumInstantSendManager, /*verbose*/ true); auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, *llmq::chainLocksHandler, *llmq::quorumInstantSendManager, /*verbose*/ true);
ankerl::nanobench::doNotOptimizeAway(univalue);
}); });
} }
BENCHMARK(BlockToJsonVerbose); BENCHMARK(BlockToJsonVerbose);
static void BlockToJsonVerboseWrite(benchmark::Bench& bench)
{
TestBlockAndIndex data;
auto univalue = blockToJSON(data.block, &data.blockindex, &data.blockindex, *llmq::chainLocksHandler, *llmq::quorumInstantSendManager, /*verbose*/ true);
bench.run([&] {
auto str = univalue.write();
ankerl::nanobench::doNotOptimizeAway(str);
});
}
BENCHMARK(BlockToJsonVerboseWrite);

View File

@ -24,7 +24,7 @@ struct CSpentIndexTxInfo;
// core_read.cpp // core_read.cpp
CScript ParseScript(const std::string& s); CScript ParseScript(const std::string& s);
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false); std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
[[nodiscard]] bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx); [[nodiscard]] bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx);
[[nodiscard]] bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); [[nodiscard]] bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header); bool DecodeHexBlockHeader(CBlockHeader&, const std::string& hex_header);
/** /**

View File

@ -100,25 +100,31 @@ CScript ParseScript(const std::string& s)
return result; return result;
} }
bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx) static bool DecodeTx(CMutableTransaction& tx, const std::vector<unsigned char>& tx_data)
{ {
if (!IsHex(strHexTx)) CDataStream ssData(tx_data, SER_NETWORK, PROTOCOL_VERSION);
return false;
std::vector<unsigned char> txData(ParseHex(strHexTx));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
try { try {
ssData >> tx; ssData >> tx;
if (!ssData.empty()) if (!ssData.empty()) {
return false; return false;
} }
catch (const std::exception&) { } catch (const std::exception&) {
return false; return false;
} }
return true; return true;
} }
bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx)
{
if (!IsHex(hex_tx)) {
return false;
}
std::vector<unsigned char> txData(ParseHex(hex_tx));
return DecodeTx(tx, txData);
}
bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header) bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header)
{ {
if (!IsHex(hex_header)) return false; if (!IsHex(hex_header)) return false;

View File

@ -385,6 +385,11 @@ CNode* CConnman::FindNode(const CService& addr, bool fExcludeDisconnecting)
return nullptr; return nullptr;
} }
bool CConnman::AlreadyConnectedToAddress(const CAddress& addr)
{
return FindNode(addr.ToStringIPPort());
}
bool CConnman::CheckIncomingNonce(uint64_t nonce) bool CConnman::CheckIncomingNonce(uint64_t nonce)
{ {
LOCK(cs_vNodes); LOCK(cs_vNodes);
@ -2397,11 +2402,30 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (nTries > 100) if (nTries > 100)
break; break;
CAddrInfo addr = addrman.SelectTriedCollision(); CAddrInfo addr;
// SelectTriedCollision returns an invalid address if it is empty. if (fFeeler) {
if (!fFeeler || !addr.IsValid()) { // First, try to get a tried table collision address. This returns
addr = addrman.Select(fFeeler); // an empty (invalid) address if there are no collisions to try.
addr = addrman.SelectTriedCollision();
if (!addr.IsValid()) {
// No tried table collisions. Select a new table address
// for our feeler.
addr = addrman.Select(true);
} else if (AlreadyConnectedToAddress(addr)) {
// If test-before-evict logic would have us connect to a
// peer that we're already connected to, just mark that
// address as Good(). We won't be able to initiate the
// connection anyway, so this avoids inadvertently evicting
// a currently-connected peer.
addrman.Good(addr);
// Select a new table address for our feeler instead.
addr = addrman.Select(true);
}
} else {
// Not a feeler
addr = addrman.Select();
} }
auto dmn = mnList.GetMNByService(addr); auto dmn = mnList.GetMNByService(addr);
@ -2768,7 +2792,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
if (!pszDest) { if (!pszDest) {
// banned, discouraged or exact match? // banned, discouraged or exact match?
if ((m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect))) || FindNode(addrConnect.ToStringIPPort())) if ((m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect))) || AlreadyConnectedToAddress(addrConnect))
return; return;
// local and not a connection to itself? // local and not a connection to itself?
bool fAllowLocal = Params().AllowMultiplePorts() && addrConnect.GetPort() != GetListenPort(); bool fAllowLocal = Params().AllowMultiplePorts() && addrConnect.GetPort() != GetListenPort();

View File

@ -625,6 +625,12 @@ private:
CNode* FindNode(const std::string& addrName, bool fExcludeDisconnecting = true); CNode* FindNode(const std::string& addrName, bool fExcludeDisconnecting = true);
CNode* FindNode(const CService& addr, bool fExcludeDisconnecting = true); CNode* FindNode(const CService& addr, bool fExcludeDisconnecting = true);
/**
* Determine whether we're already connected to a given address, in order to
* avoid initiating duplicate connections.
*/
bool AlreadyConnectedToAddress(const CAddress& addr);
bool AttemptToEvictConnection(); bool AttemptToEvictConnection();
CNode* ConnectNode(CAddress addrConnect, const char *pszDest = nullptr, bool fCountFailure = false, ConnectionType conn_type = ConnectionType::OUTBOUND_FULL_RELAY); CNode* ConnectNode(CAddress addrConnect, const char *pszDest = nullptr, bool fCountFailure = false, ConnectionType conn_type = ConnectionType::OUTBOUND_FULL_RELAY);
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const; void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;

View File

@ -3017,14 +3017,8 @@ void PeerManagerImpl::ProcessMessage(
// empty and no one will know who we are, so these mechanisms are // empty and no one will know who we are, so these mechanisms are
// important to help us connect to the network. // important to help us connect to the network.
// //
// We also update the addrman to record connection success for // We skip this for BLOCK_RELAY peers to avoid potentially leaking
// these peers (which include OUTBOUND_FULL_RELAY and FEELER // information about our BLOCK_RELAY connections via address relay.
// connections) so that addrman will have an up-to-date notion of
// which peers are online and available.
//
// We skip these operations for BLOCK_RELAY peers to avoid
// potentially leaking information about our BLOCK_RELAY
// connections via the addrman or address relay.
if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload()) if (fListen && !m_chainman.ActiveChainstate().IsInitialBlockDownload())
{ {
CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices()); CAddress addr = GetLocalAddress(&pfrom.addr, pfrom.GetLocalServices());
@ -3043,11 +3037,24 @@ void PeerManagerImpl::ProcessMessage(
// Get recent addresses // Get recent addresses
m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR)); m_connman.PushMessage(&pfrom, CNetMsgMaker(greatest_common_version).Make(NetMsgType::GETADDR));
pfrom.fGetAddr = true; pfrom.fGetAddr = true;
}
// Moves address from New to Tried table in Addrman, resolves if (!pfrom.IsInboundConn()) {
// tried-table collisions, etc. // For non-inbound connections, we update the addrman to record
// connection success so that addrman will have an up-to-date
// notion of which peers are online and available.
//
// While we strive to not leak information about block-relay-only
// connections via the addrman, not moving an address to the tried
// table is also potentially detrimental because new-table entries
// are subject to eviction in the event of addrman collisions. We
// mitigate the information-leak by never calling
// CAddrMan::Connected() on block-relay-only peers; see
// FinalizeNode().
//
// This moves an address from New to Tried table in Addrman,
// resolves tried-table collisions, etc.
m_addrman.Good(pfrom.addr); m_addrman.Good(pfrom.addr);
} }
std::string remoteAddr; std::string remoteAddr;

View File

@ -214,20 +214,9 @@ UniValue blockheaderToJSON(const CBlockIndex* tip, const CBlockIndex* blockindex
UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails) UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIndex* blockindex, llmq::CChainLocksHandler& clhandler, llmq::CInstantSendManager& isman, bool txDetails)
{ {
// Serialize passed information without accessing chain state of the active chain! UniValue result = blockheaderToJSON(tip, blockindex, clhandler, isman);
AssertLockNotHeld(cs_main); // For performance reasons
UniValue result(UniValue::VOBJ);
result.pushKV("hash", blockindex->GetBlockHash().GetHex());
const CBlockIndex* pnext;
int confirmations = ComputeNextBlockAndDepth(tip, blockindex, pnext);
result.pushKV("confirmations", confirmations);
result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION)); result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION));
result.pushKV("height", blockindex->nHeight);
result.pushKV("version", block.nVersion);
result.pushKV("versionHex", strprintf("%08x", block.nVersion));
result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
bool chainLock = clhandler.HasChainLock(blockindex->nHeight, blockindex->GetBlockHash());
UniValue txs(UniValue::VARR); UniValue txs(UniValue::VARR);
for(const auto& tx : block.vtx) for(const auto& tx : block.vtx)
{ {
@ -236,7 +225,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
UniValue objTx(UniValue::VOBJ); UniValue objTx(UniValue::VOBJ);
TxToUniv(*tx, uint256(), objTx, true); TxToUniv(*tx, uint256(), objTx, true);
bool fLocked = isman.IsLocked(tx->GetHash()); bool fLocked = isman.IsLocked(tx->GetHash());
objTx.pushKV("instantlock", fLocked || chainLock); objTx.pushKV("instantlock", fLocked || result["chainlock"].get_bool());
objTx.pushKV("instantlock_internal", fLocked); objTx.pushKV("instantlock_internal", fLocked);
txs.push_back(objTx); txs.push_back(objTx);
} }
@ -249,20 +238,6 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
result.pushKV("cbTx", opt_cbTx->ToJson()); result.pushKV("cbTx", opt_cbTx->ToJson());
} }
} }
result.pushKV("time", block.GetBlockTime());
result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
result.pushKV("nonce", (uint64_t)block.nNonce);
result.pushKV("bits", strprintf("%08x", block.nBits));
result.pushKV("difficulty", GetDifficulty(blockindex));
result.pushKV("chainwork", blockindex->nChainWork.GetHex());
result.pushKV("nTx", (uint64_t)blockindex->nTx);
if (blockindex->pprev)
result.pushKV("previousblockhash", blockindex->pprev->GetBlockHash().GetHex());
if (pnext)
result.pushKV("nextblockhash", pnext->GetBlockHash().GetHex());
result.pushKV("chainlock", chainLock);
return result; return result;
} }
@ -1168,11 +1143,20 @@ static UniValue getblock(const JSONRPCRequest& request)
{ {
{RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"}, {RPCResult::Type::STR_HEX, "hash", "the block hash (same as provided)"},
{RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"}, {RPCResult::Type::NUM, "confirmations", "The number of confirmations, or -1 if the block is not on the main chain"},
{RPCResult::Type::NUM, "size", "The block size"},
{RPCResult::Type::NUM, "height", "The block height or index"}, {RPCResult::Type::NUM, "height", "The block height or index"},
{RPCResult::Type::NUM, "version", "The block version"}, {RPCResult::Type::NUM, "version", "The block version"},
{RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"}, {RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
{RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"}, {RPCResult::Type::STR_HEX, "merkleroot", "The merkle root"},
{RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM, "nonce", "The nonce"},
{RPCResult::Type::STR_HEX, "bits", "The bits"},
{RPCResult::Type::NUM, "difficulty", "The difficulty"},
{RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the current chain"},
{RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
{RPCResult::Type::STR_HEX, "previousblockhash", /* optional */ true, "The hash of the previous block (if available)"},
{RPCResult::Type::STR_HEX, "nextblockhash", /* optional */ true, "The hash of the next block (if available)"},
{RPCResult::Type::NUM, "size", "The block size"},
{RPCResult::Type::ARR, "tx", "The transaction ids", {RPCResult::Type::ARR, "tx", "The transaction ids",
{{RPCResult::Type::STR_HEX, "", "The transaction id"}}}, {{RPCResult::Type::STR_HEX, "", "The transaction id"}}},
{RPCResult::Type::OBJ, "cbTx", "The coinbase special transaction", {RPCResult::Type::OBJ, "cbTx", "The coinbase special transaction",
@ -1182,15 +1166,6 @@ static UniValue getblock(const JSONRPCRequest& request)
{RPCResult::Type::STR_HEX, "merkleRootMNList", "The merkle root of the masternode list"}, {RPCResult::Type::STR_HEX, "merkleRootMNList", "The merkle root of the masternode list"},
{RPCResult::Type::STR_HEX, "merkleRootQuorums", "The merkle root of the quorum list"}, {RPCResult::Type::STR_HEX, "merkleRootQuorums", "The merkle root of the quorum list"},
}}, }},
{RPCResult::Type::NUM_TIME, "time", "The block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM_TIME, "mediantime", "The median block time expressed in " + UNIX_EPOCH_TIME},
{RPCResult::Type::NUM, "nonce", "The nonce"},
{RPCResult::Type::STR_HEX, "bits", "The bits"},
{RPCResult::Type::NUM, "difficulty", "The difficulty"},
{RPCResult::Type::STR_HEX, "chainwork", "Expected number of hashes required to produce the chain up to this block (in hex)"},
{RPCResult::Type::NUM, "nTx", "The number of transactions in the block"},
{RPCResult::Type::STR_HEX, "previousblockhash", "The hash of the previous block"},
{RPCResult::Type::STR_HEX, "nextblockhash", "The hash of the next block"},
}}, }},
RPCResult{"for verbosity = 2", RPCResult{"for verbosity = 2",
RPCResult::Type::OBJ, "", "", RPCResult::Type::OBJ, "", "",

View File

@ -188,21 +188,60 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test)
CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK); CAddress addr = CAddress(CService(ipv4Addr, 7777), NODE_NETWORK);
std::string pszDest; std::string pszDest;
std::unique_ptr<CNode> pnode1 = std::make_unique<CNode>(id++, NODE_NETWORK, hSocket, addr, 0, 0, CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY); std::unique_ptr<CNode> pnode1 = std::make_unique<CNode>(
id++, NODE_NETWORK, hSocket, addr,
/* nKeyedNetGroupIn = */ 0,
/* nLocalHostNonceIn = */ 0,
CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY);
BOOST_CHECK(pnode1->IsFullOutboundConn() == true); BOOST_CHECK(pnode1->IsFullOutboundConn() == true);
BOOST_CHECK(pnode1->IsManualConn() == false); BOOST_CHECK(pnode1->IsManualConn() == false);
BOOST_CHECK(pnode1->IsBlockOnlyConn() == false); BOOST_CHECK(pnode1->IsBlockOnlyConn() == false);
BOOST_CHECK(pnode1->IsFeelerConn() == false); BOOST_CHECK(pnode1->IsFeelerConn() == false);
BOOST_CHECK(pnode1->IsAddrFetchConn() == false); BOOST_CHECK(pnode1->IsAddrFetchConn() == false);
BOOST_CHECK(pnode1->IsInboundConn() == false); BOOST_CHECK(pnode1->IsInboundConn() == false);
BOOST_CHECK_EQUAL(pnode1->ConnectedThroughNetwork(), Network::NET_IPV4);
std::unique_ptr<CNode> pnode2 = std::make_unique<CNode>(id++, NODE_NETWORK, hSocket, addr, 1, 1, CAddress(), pszDest, ConnectionType::INBOUND); std::unique_ptr<CNode> pnode2 = std::make_unique<CNode>(
id++, NODE_NETWORK, hSocket, addr,
/* nKeyedNetGroupIn = */ 1,
/* nLocalHostNonceIn = */ 1,
CAddress(), pszDest, ConnectionType::INBOUND,
/* inbound_onion = */ false);
BOOST_CHECK(pnode2->IsFullOutboundConn() == false); BOOST_CHECK(pnode2->IsFullOutboundConn() == false);
BOOST_CHECK(pnode2->IsManualConn() == false); BOOST_CHECK(pnode2->IsManualConn() == false);
BOOST_CHECK(pnode2->IsBlockOnlyConn() == false); BOOST_CHECK(pnode2->IsBlockOnlyConn() == false);
BOOST_CHECK(pnode2->IsFeelerConn() == false); BOOST_CHECK(pnode2->IsFeelerConn() == false);
BOOST_CHECK(pnode2->IsAddrFetchConn() == false); BOOST_CHECK(pnode2->IsAddrFetchConn() == false);
BOOST_CHECK(pnode2->IsInboundConn() == true); BOOST_CHECK(pnode2->IsInboundConn() == true);
BOOST_CHECK_EQUAL(pnode2->ConnectedThroughNetwork(), Network::NET_IPV4);
std::unique_ptr<CNode> pnode3 = std::make_unique<CNode>(
id++, NODE_NETWORK, hSocket, addr,
/* nKeyedNetGroupIn = */ 0,
/* nLocalHostNonceIn = */ 0,
CAddress(), pszDest, ConnectionType::OUTBOUND_FULL_RELAY,
/* inbound_onion = */ true);
BOOST_CHECK(pnode3->IsFullOutboundConn() == true);
BOOST_CHECK(pnode3->IsManualConn() == false);
BOOST_CHECK(pnode3->IsBlockOnlyConn() == false);
BOOST_CHECK(pnode3->IsFeelerConn() == false);
BOOST_CHECK(pnode3->IsAddrFetchConn() == false);
BOOST_CHECK(pnode3->IsInboundConn() == false);
BOOST_CHECK_EQUAL(pnode3->ConnectedThroughNetwork(), Network::NET_IPV4);
std::unique_ptr<CNode> pnode4 = std::make_unique<CNode>(
id++, NODE_NETWORK, hSocket, addr,
/* nKeyedNetGroupIn = */ 1,
/* nLocalHostNonceIn = */ 1,
CAddress(), pszDest, ConnectionType::INBOUND,
/* inbound_onion = */ true);
BOOST_CHECK(pnode4->IsFullOutboundConn() == false);
BOOST_CHECK(pnode4->IsManualConn() == false);
BOOST_CHECK(pnode4->IsBlockOnlyConn() == false);
BOOST_CHECK(pnode4->IsFeelerConn() == false);
BOOST_CHECK(pnode4->IsAddrFetchConn() == false);
BOOST_CHECK(pnode4->IsInboundConn() == true);
BOOST_CHECK_EQUAL(pnode4->ConnectedThroughNetwork(), Network::NET_ONION);
} }
BOOST_AUTO_TEST_CASE(PoissonNextSend) BOOST_AUTO_TEST_CASE(PoissonNextSend)

View File

@ -51,6 +51,7 @@ namespace {
//! Construct wallet tx struct. //! Construct wallet tx struct.
WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx)
{ {
LOCK(wallet.cs_wallet);
WalletTx result; WalletTx result;
bool fInputDenomFound{false}, fOutputDenomFound{false}; bool fInputDenomFound{false}, fOutputDenomFound{false};
result.tx = wtx.tx; result.tx = wtx.tx;
@ -172,8 +173,16 @@ public:
{ {
return m_wallet->SignMessage(message, pkhash, str_sig); return m_wallet->SignMessage(message, pkhash, str_sig);
} }
bool isSpendable(const CScript& script) override { return m_wallet->IsMine(script) & ISMINE_SPENDABLE; } bool isSpendable(const CScript& script) override
bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; } {
LOCK(m_wallet->cs_wallet);
return m_wallet->IsMine(script) & ISMINE_SPENDABLE;
}
bool isSpendable(const CTxDestination& dest) override
{
LOCK(m_wallet->cs_wallet);
return m_wallet->IsMine(dest) & ISMINE_SPENDABLE;
}
bool haveWatchOnly() override bool haveWatchOnly() override
{ {
auto spk_man = m_wallet->GetLegacyScriptPubKeyMan(); auto spk_man = m_wallet->GetLegacyScriptPubKeyMan();

View File

@ -361,7 +361,7 @@ std::string COutput::ToString() const
const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const
{ {
LOCK(cs_wallet); AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash); std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);
if (it == mapWallet.end()) if (it == mapWallet.end())
return nullptr; return nullptr;
@ -1326,15 +1326,13 @@ void CWallet::BlockUntilSyncedToCurrentChain() const {
isminetype CWallet::IsMine(const CTxIn &txin) const isminetype CWallet::IsMine(const CTxIn &txin) const
{ {
AssertLockHeld(cs_wallet);
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);
if (mi != mapWallet.end())
{ {
LOCK(cs_wallet); const CWalletTx& prev = (*mi).second;
std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); if (txin.prevout.n < prev.tx->vout.size())
if (mi != mapWallet.end()) return IsMine(prev.tx->vout[txin.prevout.n]);
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.tx->vout.size())
return IsMine(prev.tx->vout[txin.prevout.n]);
}
} }
return ISMINE_NO; return ISMINE_NO;
} }
@ -1487,16 +1485,19 @@ bool CWallet::IsFullyMixed(const COutPoint& outpoint) const
isminetype CWallet::IsMine(const CTxOut& txout) const isminetype CWallet::IsMine(const CTxOut& txout) const
{ {
AssertLockHeld(cs_wallet);
return IsMine(txout.scriptPubKey); return IsMine(txout.scriptPubKey);
} }
isminetype CWallet::IsMine(const CTxDestination& dest) const isminetype CWallet::IsMine(const CTxDestination& dest) const
{ {
AssertLockHeld(cs_wallet);
return IsMine(GetScriptForDestination(dest)); return IsMine(GetScriptForDestination(dest));
} }
isminetype CWallet::IsMine(const CScript& script) const isminetype CWallet::IsMine(const CScript& script) const
{ {
AssertLockHeld(cs_wallet);
isminetype result = ISMINE_NO; isminetype result = ISMINE_NO;
for (const auto& spk_man_pair : m_spk_managers) { for (const auto& spk_man_pair : m_spk_managers) {
result = std::max(result, spk_man_pair.second->IsMine(script)); result = std::max(result, spk_man_pair.second->IsMine(script));
@ -1508,6 +1509,7 @@ CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) cons
{ {
if (!MoneyRange(txout.nValue)) if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range"); throw std::runtime_error(std::string(__func__) + ": value out of range");
LOCK(cs_wallet);
return ((IsMine(txout) & filter) ? txout.nValue : 0); return ((IsMine(txout) & filter) ? txout.nValue : 0);
} }
@ -1525,13 +1527,12 @@ bool CWallet::IsChange(const CScript& script) const
// a better way of identifying which outputs are 'the send' and which are // a better way of identifying which outputs are 'the send' and which are
// 'the change' will need to be implemented (maybe extend CWalletTx to remember // 'the change' will need to be implemented (maybe extend CWalletTx to remember
// which output, if any, was change). // which output, if any, was change).
AssertLockHeld(cs_wallet);
if (IsMine(script)) if (IsMine(script))
{ {
CTxDestination address; CTxDestination address;
if (!ExtractDestination(script, address)) if (!ExtractDestination(script, address))
return true; return true;
LOCK(cs_wallet);
if (!FindAddressBookEntry(address)) { if (!FindAddressBookEntry(address)) {
return true; return true;
} }
@ -1541,6 +1542,7 @@ bool CWallet::IsChange(const CScript& script) const
CAmount CWallet::GetChange(const CTxOut& txout) const CAmount CWallet::GetChange(const CTxOut& txout) const
{ {
AssertLockHeld(cs_wallet);
if (!MoneyRange(txout.nValue)) if (!MoneyRange(txout.nValue))
throw std::runtime_error(std::string(__func__) + ": value out of range"); throw std::runtime_error(std::string(__func__) + ": value out of range");
return (IsChange(txout) ? txout.nValue : 0); return (IsChange(txout) ? txout.nValue : 0);
@ -1548,6 +1550,7 @@ CAmount CWallet::GetChange(const CTxOut& txout) const
bool CWallet::IsMine(const CTransaction& tx) const bool CWallet::IsMine(const CTransaction& tx) const
{ {
AssertLockHeld(cs_wallet);
for (const CTxOut& txout : tx.vout) for (const CTxOut& txout : tx.vout)
if (IsMine(txout)) if (IsMine(txout))
return true; return true;
@ -1606,6 +1609,7 @@ CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) c
CAmount CWallet::GetChange(const CTransaction& tx) const CAmount CWallet::GetChange(const CTransaction& tx) const
{ {
LOCK(cs_wallet);
CAmount nChange = 0; CAmount nChange = 0;
for (const CTxOut& txout : tx.vout) for (const CTxOut& txout : tx.vout)
{ {
@ -1846,6 +1850,7 @@ void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,
nFee = nDebit - nValueOut; nFee = nDebit - nValueOut;
} }
LOCK(pwallet->cs_wallet);
// Sent/received. // Sent/received.
for (unsigned int i = 0; i < tx->vout.size(); ++i) for (unsigned int i = 0; i < tx->vout.size(); ++i)
{ {
@ -2340,6 +2345,7 @@ bool CWalletTx::IsTrusted(std::set<uint256>& trusted_parents) const
if (!InMempool()) return false; if (!InMempool()) return false;
// Trusted if all inputs are from us and are in the mempool: // Trusted if all inputs are from us and are in the mempool:
LOCK(pwallet->cs_wallet);
for (const CTxIn& txin : tx->vin) for (const CTxIn& txin : tx->vin)
{ {
// Transactions not sent by us: not trusted // Transactions not sent by us: not trusted
@ -3957,6 +3963,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256
bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose) bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose)
{ {
bool fUpdated = false; bool fUpdated = false;
bool is_mine;
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address); std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address);
@ -3964,8 +3971,9 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
m_address_book[address].SetLabel(strName); m_address_book[address].SetLabel(strName);
if (!strPurpose.empty()) /* update purpose only if requested */ if (!strPurpose.empty()) /* update purpose only if requested */
m_address_book[address].purpose = strPurpose; m_address_book[address].purpose = strPurpose;
is_mine = IsMine(address) != ISMINE_NO;
} }
NotifyAddressBookChanged(this, address, strName, IsMine(address) != ISMINE_NO, NotifyAddressBookChanged(this, address, strName, is_mine,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose)) if (!strPurpose.empty() && !batch.WritePurpose(EncodeDestination(address), strPurpose))
return false; return false;
@ -3980,18 +3988,17 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
bool CWallet::DelAddressBook(const CTxDestination& address) bool CWallet::DelAddressBook(const CTxDestination& address)
{ {
// If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted) bool is_mine;
// NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
// When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
if (IsMine(address)) {
WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
return false;
}
WalletBatch batch(GetDatabase()); WalletBatch batch(GetDatabase());
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
// If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
// NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
// When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
if (IsMine(address)) {
WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
return false;
}
// Delete destdata tuples associated with address // Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address); std::string strAddress = EncodeDestination(address);
for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata) for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata)
@ -3999,9 +4006,10 @@ bool CWallet::DelAddressBook(const CTxDestination& address)
batch.EraseDestData(strAddress, item.first); batch.EraseDestData(strAddress, item.first);
} }
m_address_book.erase(address); m_address_book.erase(address);
is_mine = IsMine(address) != ISMINE_NO;
} }
NotifyAddressBookChanged(this, address, "", IsMine(address) != ISMINE_NO, "", CT_DELETED); NotifyAddressBookChanged(this, address, "", is_mine, "", CT_DELETED);
batch.ErasePurpose(EncodeDestination(address)); batch.ErasePurpose(EncodeDestination(address));
return batch.EraseName(EncodeDestination(address)); return batch.EraseName(EncodeDestination(address));

View File

@ -885,7 +885,7 @@ public:
/** Interface for accessing CoinJoin state. */ /** Interface for accessing CoinJoin state. */
interfaces::CoinJoin::Loader& coinjoin_loader() { assert(m_coinjoin_loader); return *m_coinjoin_loader; } interfaces::CoinJoin::Loader& coinjoin_loader() { assert(m_coinjoin_loader); return *m_coinjoin_loader; }
const CWalletTx* GetWalletTx(const uint256& hash) const; const CWalletTx* GetWalletTx(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! check whether we are allowed to upgrade (or already support) to the named feature //! check whether we are allowed to upgrade (or already support) to the named feature
bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } bool CanSupportFeature(enum WalletFeature wf) const override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
@ -1146,20 +1146,20 @@ public:
bool GetNewDestination(const std::string label, CTxDestination& dest, std::string& error); bool GetNewDestination(const std::string label, CTxDestination& dest, std::string& error);
bool GetNewChangeDestination(CTxDestination& dest, std::string& error); bool GetNewChangeDestination(CTxDestination& dest, std::string& error);
isminetype IsMine(const CTxDestination& dest) const; isminetype IsMine(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
isminetype IsMine(const CScript& script) const; isminetype IsMine(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
isminetype IsMine(const CTxIn& txin) const; isminetype IsMine(const CTxIn& txin) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** /**
* Returns amount of debit if the input matches the * Returns amount of debit if the input matches the
* filter, otherwise returns 0 * filter, otherwise returns 0
*/ */
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const; CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
isminetype IsMine(const CTxOut& txout) const; isminetype IsMine(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const; CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
bool IsChange(const CTxOut& txout) const; bool IsChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsChange(const CScript& script) const; bool IsChange(const CScript& script) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
CAmount GetChange(const CTxOut& txout) const; CAmount GetChange(const CTxOut& txout) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsMine(const CTransaction& tx) const; bool IsMine(const CTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** should probably be renamed to IsRelevantToMe */ /** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const; bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;

View File

@ -4,7 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test BIP68 implementation.""" """Test BIP68 implementation."""
from test_framework.blocktools import create_block, create_coinbase from test_framework.blocktools import create_block, NORMAL_GBT_REQUEST_PARAMS
from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, ToHex from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, ToHex
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import ( from test_framework.util import (
@ -270,6 +270,8 @@ class BIP68Test(BitcoinTestFramework):
# Advance the time on the node so that we can test timelocks # Advance the time on the node so that we can test timelocks
self.nodes[0].setmocktime(cur_time+600) self.nodes[0].setmocktime(cur_time+600)
# Save block template now to use for the reorg later
tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
self.nodes[0].generate(1) self.nodes[0].generate(1)
assert tx2.hash not in self.nodes[0].getrawmempool() assert tx2.hash not in self.nodes[0].getrawmempool()
@ -313,16 +315,15 @@ class BIP68Test(BitcoinTestFramework):
# diagram above). # diagram above).
# This would cause tx2 to be added back to the mempool, which in turn causes # This would cause tx2 to be added back to the mempool, which in turn causes
# tx3 to be removed. # tx3 to be removed.
tip = int(self.nodes[0].getblockhash(self.nodes[0].getblockcount()-1), 16)
height = self.nodes[0].getblockcount()
for i in range(2): for i in range(2):
block = create_block(tip, create_coinbase(height), cur_time) block = create_block(tmpl=tmpl, ntime=cur_time)
block.nVersion = 3
block.rehash() block.rehash()
block.solve() block.solve()
tip = block.sha256 tip = block.sha256
height += 1
assert_equal(None if i == 1 else 'inconclusive', self.nodes[0].submitblock(ToHex(block))) assert_equal(None if i == 1 else 'inconclusive', self.nodes[0].submitblock(ToHex(block)))
tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
tmpl['previousblockhash'] = '%x' % tip
tmpl['transactions'] = []
cur_time += 1 cur_time += 1
mempool = self.nodes[0].getrawmempool() mempool = self.nodes[0].getrawmempool()
@ -370,9 +371,7 @@ class BIP68Test(BitcoinTestFramework):
assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3)) assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3))
# make a block that violates bip68; ensure that the tip updates # make a block that violates bip68; ensure that the tip updates
tip = int(self.nodes[0].getbestblockhash(), 16) block = create_block(tmpl=self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS))
block = create_block(tip, create_coinbase(self.nodes[0].getblockcount()+1), self.mocktime + 600)
block.nVersion = 3
block.vtx.extend([tx1, tx2, tx3]) block.vtx.extend([tx1, tx2, tx3])
block.hashMerkleRoot = block.calc_merkle_root() block.hashMerkleRoot = block.calc_merkle_root()
block.rehash() block.rehash()

View File

@ -1273,7 +1273,7 @@ class FullBlockTest(BitcoinTestFramework):
blocks = [] blocks = []
spend = out[32] spend = out[32]
for i in range(89, LARGE_REORG_SIZE + 89): for i in range(89, LARGE_REORG_SIZE + 89):
b = self.next_block(i, spend, version=4) b = self.next_block(i, spend)
tx = CTransaction() tx = CTransaction()
script_length = MAX_BLOCK_SIZE - len(b.serialize()) - 69 script_length = MAX_BLOCK_SIZE - len(b.serialize()) - 69
script_output = CScript([b'\x00' * script_length]) script_output = CScript([b'\x00' * script_length])
@ -1292,18 +1292,18 @@ class FullBlockTest(BitcoinTestFramework):
self.move_tip(88) self.move_tip(88)
blocks2 = [] blocks2 = []
for i in range(89, LARGE_REORG_SIZE + 89): for i in range(89, LARGE_REORG_SIZE + 89):
blocks2.append(self.next_block("alt" + str(i), version=4)) blocks2.append(self.next_block("alt" + str(i)))
self.send_blocks(blocks2, False, force_send=True) self.send_blocks(blocks2, False, force_send=True)
# extend alt chain to trigger re-org # extend alt chain to trigger re-org
block = self.next_block("alt" + str(chain1_tip + 1), version=4) block = self.next_block("alt" + str(chain1_tip + 1))
self.send_blocks([block], True, timeout=2440) self.send_blocks([block], True, timeout=2440)
# ... and re-org back to the first chain # ... and re-org back to the first chain
self.move_tip(chain1_tip) self.move_tip(chain1_tip)
block = self.next_block(chain1_tip + 1, version=4) block = self.next_block(chain1_tip + 1)
self.send_blocks([block], False, force_send=True) self.send_blocks([block], False, force_send=True)
block = self.next_block(chain1_tip + 2, version=4) block = self.next_block(chain1_tip + 2)
self.send_blocks([block], True, timeout=2440) self.send_blocks([block], True, timeout=2440)
self.log.info("Reject a block with an invalid block header version") self.log.info("Reject a block with an invalid block header version")
@ -1311,7 +1311,7 @@ class FullBlockTest(BitcoinTestFramework):
self.send_blocks([b_v1], success=False, force_send=True, reject_reason='bad-version(0x00000001)', reconnect=True) self.send_blocks([b_v1], success=False, force_send=True, reject_reason='bad-version(0x00000001)', reconnect=True)
self.move_tip(chain1_tip + 2) self.move_tip(chain1_tip + 2)
b_cb34 = self.next_block('b_cb34', version=4) b_cb34 = self.next_block('b_cb34')
b_cb34.vtx[0].vin[0].scriptSig = b_cb34.vtx[0].vin[0].scriptSig[:-1] b_cb34.vtx[0].vin[0].scriptSig = b_cb34.vtx[0].vin[0].scriptSig[:-1]
b_cb34.vtx[0].rehash() b_cb34.vtx[0].rehash()
b_cb34.hashMerkleRoot = b_cb34.calc_merkle_root() b_cb34.hashMerkleRoot = b_cb34.calc_merkle_root()
@ -1345,7 +1345,7 @@ class FullBlockTest(BitcoinTestFramework):
tx.rehash() tx.rehash()
return tx return tx
def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), *, version=1): def next_block(self, number, spend=None, additional_coinbase_value=0, script=CScript([OP_TRUE]), *, version=4):
if self.tip is None: if self.tip is None:
base_block_hash = self.genesis_hash base_block_hash = self.genesis_hash
block_time = self.mocktime + 1 block_time = self.mocktime + 1

View File

@ -151,7 +151,6 @@ class BIP68_112_113Test(BitcoinTestFramework):
# and '-dip3params=2000:2000' to create pre-dip3 blocks only # and '-dip3params=2000:2000' to create pre-dip3 blocks only
self.extra_args = [[ self.extra_args = [[
'-whitelist=noban@127.0.0.1', '-whitelist=noban@127.0.0.1',
'-blockversion=4',
'-maxtipage=600100', '-dip3params=2000:2000', '-maxtipage=600100', '-dip3params=2000:2000',
'-par=1', # Use only one script thread to get the exact reject reason for testing '-par=1', # Use only one script thread to get the exact reject reason for testing
]] ]]

View File

@ -44,7 +44,6 @@ class NotificationsTest(DashTestFramework):
# -alertnotify and -blocknotify on node0, walletnotify on node1 # -alertnotify and -blocknotify on node0, walletnotify on node1
self.extra_args[0].append("-alertnotify=echo > {}".format(os.path.join(self.alertnotify_dir, '%s'))) self.extra_args[0].append("-alertnotify=echo > {}".format(os.path.join(self.alertnotify_dir, '%s')))
self.extra_args[0].append("-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s'))) self.extra_args[0].append("-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s')))
self.extra_args[1].append("-blockversion=211")
self.extra_args[1].append("-rescan") self.extra_args[1].append("-rescan")
self.extra_args[1].append("-walletnotify=echo > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s')))) self.extra_args[1].append("-walletnotify=echo > {}".format(os.path.join(self.walletnotify_dir, notify_outputname('%w', '%s'))))

View File

@ -13,7 +13,7 @@ Generate 427 more blocks.
[Policy/Consensus] Check that the new NULLDUMMY rules are enforced on the 432nd block. [Policy/Consensus] Check that the new NULLDUMMY rules are enforced on the 432nd block.
""" """
from test_framework.blocktools import create_coinbase, create_block, create_transaction from test_framework.blocktools import NORMAL_GBT_REQUEST_PARAMS, create_block, create_transaction
from test_framework.messages import CTransaction from test_framework.messages import CTransaction
from test_framework.script import CScript from test_framework.script import CScript
from test_framework.test_framework import BitcoinTestFramework from test_framework.test_framework import BitcoinTestFramework
@ -37,9 +37,10 @@ def trueDummy(tx):
class NULLDUMMYTest(BitcoinTestFramework): class NULLDUMMYTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
self.num_nodes = 1 # Need two nodes only so GBT doesn't complain that it's not connected
self.num_nodes = 2
self.setup_clean_chain = True self.setup_clean_chain = True
self.extra_args = [['-whitelist=127.0.0.1']] self.extra_args = [['-whitelist=127.0.0.1']] * 2
def skip_test_if_missing_module(self): def skip_test_if_missing_module(self):
self.skip_if_no_wallet() self.skip_if_no_wallet()
@ -54,7 +55,6 @@ class NULLDUMMYTest(BitcoinTestFramework):
coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0]) coinbase_txid.append(self.nodes[0].getblock(i)['tx'][0])
self.nodes[0].generate(427) # Block 429 self.nodes[0].generate(427) # Block 429
self.lastblockhash = self.nodes[0].getbestblockhash() self.lastblockhash = self.nodes[0].getbestblockhash()
self.tip = int("0x" + self.lastblockhash, 0)
self.lastblockheight = 429 self.lastblockheight = 429
self.lastblocktime = self.mocktime + 429 self.lastblocktime = self.mocktime + 429
@ -88,8 +88,10 @@ class NULLDUMMYTest(BitcoinTestFramework):
def block_submit(self, node, txs, accept = False): def block_submit(self, node, txs, accept = False):
dip4_activated = self.lastblockheight + 1 >= 432 dip4_activated = self.lastblockheight + 1 >= 432
block = create_block(self.tip, create_coinbase(self.lastblockheight + 1, dip4_activated=dip4_activated), self.lastblocktime + 1) tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
block.nVersion = 4 assert_equal(tmpl['previousblockhash'], self.lastblockhash)
assert_equal(tmpl['height'], self.lastblockheight + 1)
block = create_block(tmpl=tmpl, ntime=self.lastblocktime + 1, dip4_activated=dip4_activated)
for tx in txs: for tx in txs:
tx.rehash() tx.rehash()
block.vtx.append(tx) block.vtx.append(tx)
@ -99,7 +101,6 @@ class NULLDUMMYTest(BitcoinTestFramework):
assert_equal(None if accept else 'block-validation-failed', node.submitblock(block.serialize().hex())) assert_equal(None if accept else 'block-validation-failed', node.submitblock(block.serialize().hex()))
if (accept): if (accept):
assert_equal(node.getbestblockhash(), block.hash) assert_equal(node.getbestblockhash(), block.hash)
self.tip = block.sha256
self.lastblockhash = block.hash self.lastblockhash = block.hash
self.lastblocktime += 1 self.lastblocktime += 1
self.lastblockheight += 1 self.lastblockheight += 1

View File

@ -13,6 +13,8 @@ from decimal import Decimal
from test_framework.blocktools import ( from test_framework.blocktools import (
create_coinbase, create_coinbase,
NORMAL_GBT_REQUEST_PARAMS,
TIME_GENESIS_BLOCK,
) )
from test_framework.messages import ( from test_framework.messages import (
CBlock, CBlock,
@ -26,6 +28,8 @@ from test_framework.util import (
assert_raises_rpc_error, assert_raises_rpc_error,
) )
VERSIONBITS_TOP_BITS = 0x20000000
VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT = 28
def assert_template(node, block, expect, rehash=True): def assert_template(node, block, expect, rehash=True):
if rehash: if rehash:
@ -45,14 +49,23 @@ class MiningTest(BitcoinTestFramework):
def mine_chain(self): def mine_chain(self):
self.log.info('Create some old blocks') self.log.info('Create some old blocks')
for _ in range(200): for t in range(TIME_GENESIS_BLOCK, TIME_GENESIS_BLOCK + 200 * 156, 156):
self.bump_mocktime(156) self.bump_mocktime(156)
self.nodes[0].generate(1) self.nodes[0].generate(1)
mining_info = self.nodes[0].getmininginfo() mining_info = self.nodes[0].getmininginfo()
assert_equal(mining_info['blocks'], 200) assert_equal(mining_info['blocks'], 200)
assert_equal(mining_info['currentblocktx'], 0) assert_equal(mining_info['currentblocktx'], 0)
assert_equal(mining_info['currentblocksize'], 1000) assert_equal(mining_info['currentblocksize'], 1000)
self.log.info('test blockversion')
self.restart_node(0, extra_args=['-mocktime={}'.format(t), '-blockversion=1337'])
self.connect_nodes(0, 1)
assert_equal(1337, self.nodes[0].getblocktemplate()['version'])
self.restart_node(0, extra_args=['-mocktime={}'.format(t)])
self.connect_nodes(0, 1)
assert_equal(VERSIONBITS_TOP_BITS + (1 << VERSIONBITS_DEPLOYMENT_TESTDUMMY_BIT), self.nodes[0].getblocktemplate()['version'])
self.restart_node(0) self.restart_node(0)
# TODO: replace with connect_nodes_bi
self.connect_nodes(0, 1) self.connect_nodes(0, 1)
self.connect_nodes(1, 0) self.connect_nodes(1, 0)
@ -78,7 +91,7 @@ class MiningTest(BitcoinTestFramework):
# Mine a block to leave initial block download # Mine a block to leave initial block download
node.generatetoaddress(1, node.get_deterministic_priv_key().address) node.generatetoaddress(1, node.get_deterministic_priv_key().address)
tmpl = node.getblocktemplate() tmpl = node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)
self.log.info("getblocktemplate: Test capability advertised") self.log.info("getblocktemplate: Test capability advertised")
assert 'proposal' in tmpl['capabilities'] assert 'proposal' in tmpl['capabilities']
assert 'coinbasetxn' not in tmpl assert 'coinbasetxn' not in tmpl

View File

@ -7,7 +7,7 @@
import random import random
from test_framework.blocktools import COINBASE_MATURITY, create_block, create_coinbase from test_framework.blocktools import COINBASE_MATURITY, create_block, NORMAL_GBT_REQUEST_PARAMS
from test_framework.messages import BlockTransactions, BlockTransactionsRequest, calculate_shortid, CBlock, CBlockHeader, CInv, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, HeaderAndShortIDs, msg_block, msg_blocktxn, msg_cmpctblock, msg_getblocktxn, msg_getdata, msg_getheaders, msg_headers, msg_inv, msg_sendcmpct, msg_sendheaders, msg_tx, MSG_BLOCK, MSG_CMPCT_BLOCK, NODE_NETWORK, P2PHeaderAndShortIDs, PrefilledTransaction, ToHex, NODE_HEADERS_COMPRESSED from test_framework.messages import BlockTransactions, BlockTransactionsRequest, calculate_shortid, CBlock, CBlockHeader, CInv, COutPoint, CTransaction, CTxIn, CTxOut, FromHex, HeaderAndShortIDs, msg_block, msg_blocktxn, msg_cmpctblock, msg_getblocktxn, msg_getdata, msg_getheaders, msg_headers, msg_inv, msg_sendcmpct, msg_sendheaders, msg_tx, MSG_BLOCK, MSG_CMPCT_BLOCK, NODE_NETWORK, P2PHeaderAndShortIDs, PrefilledTransaction, ToHex, NODE_HEADERS_COMPRESSED
from test_framework.p2p import p2p_lock, P2PInterface from test_framework.p2p import p2p_lock, P2PInterface
from test_framework.script import CScript, OP_TRUE, OP_DROP from test_framework.script import CScript, OP_TRUE, OP_DROP
@ -103,10 +103,7 @@ class CompactBlocksTest(BitcoinTestFramework):
self.skip_if_no_wallet() self.skip_if_no_wallet()
def build_block_on_tip(self, node): def build_block_on_tip(self, node):
height = node.getblockcount() block = create_block(tmpl=node.getblocktemplate(NORMAL_GBT_REQUEST_PARAMS))
tip = node.getbestblockhash()
mtp = node.getblockheader(tip)['mediantime']
block = create_block(int(tip, 16), create_coinbase(height + 1), mtp + 1)
block.solve() block.solve()
return block return block
@ -718,6 +715,9 @@ class CompactBlocksTest(BitcoinTestFramework):
assert_equal(int(node.getbestblockhash(), 16), block.sha256) assert_equal(int(node.getbestblockhash(), 16), block.sha256)
def run_test(self): def run_test(self):
# Get the nodes out of IBD
self.nodes[0].generate(1)
# Setup the p2p connections # Setup the p2p connections
self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=1)) self.test_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=1))
self.old_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=1), services=NODE_NETWORK | NODE_HEADERS_COMPRESSED) self.old_node = self.nodes[0].add_p2p_connection(TestP2PConn(cmpct_version=1), services=NODE_NETWORK | NODE_HEADERS_COMPRESSED)

View File

@ -4,7 +4,11 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Utilities for manipulating blocks and transactions.""" """Utilities for manipulating blocks and transactions."""
from binascii import a2b_hex
from decimal import Decimal from decimal import Decimal
import io
import struct
import time
import unittest import unittest
from .messages import ( from .messages import (
@ -30,18 +34,30 @@ TIME_GENESIS_BLOCK = 1417713337
# Coinbase transaction outputs can only be spent after this number of new blocks (network rule) # Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
COINBASE_MATURITY = 100 COINBASE_MATURITY = 100
def create_block(hashprev, coinbase, ntime=None, *, version=1): NORMAL_GBT_REQUEST_PARAMS = {"rules": []} # type: ignore[var-annotated]
def create_block(hashprev=None, coinbase=None, ntime=None, *, version=None, tmpl=None, txlist=None, dip4_activated=False, v20_activated=False):
"""Create a block (with regtest difficulty).""" """Create a block (with regtest difficulty)."""
block = CBlock() block = CBlock()
block.nVersion = version if tmpl is None:
if ntime is None: tmpl = {}
import time block.nVersion = version or tmpl.get('version') or 1
block.nTime = int(time.time() + 600) block.nTime = ntime or tmpl.get('curtime') or int(time.time() + 600)
block.hashPrevBlock = hashprev or int(tmpl['previousblockhash'], 0x10)
if tmpl and not tmpl.get('bits') is None:
block.nBits = struct.unpack('>I', a2b_hex(tmpl['bits']))[0]
else: else:
block.nTime = ntime block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams
block.hashPrevBlock = hashprev if coinbase is None:
block.nBits = 0x207fffff # difficulty retargeting is disabled in REGTEST chainparams coinbase = create_coinbase(height=tmpl['height'], dip4_activated=dip4_activated, v20_activated=v20_activated)
block.vtx.append(coinbase) block.vtx.append(coinbase)
if txlist:
for tx in txlist:
if not hasattr(tx, 'calc_sha256'):
txo = CTransaction()
txo.deserialize(io.BytesIO(tx))
tx = txo
block.vtx.append(tx)
block.hashMerkleRoot = block.calc_merkle_root() block.hashMerkleRoot = block.calc_merkle_root()
block.calc_sha256() block.calc_sha256()
return block return block