mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 11:32:46 +01:00
Merge #6284: backport: bitcoin#21713, #21856, #22061, #22122, #22172, #22261, #22381, #22445, #22447
8f06ac9dfa
Merge bitcoin/bitcoin#22172: doc: update tor.md, release notes with removal of tor v2 support (W. J. van der Laan)9b22501a4d
Merge bitcoin/bitcoin#22122: ci: Bump macOS image to big-sur-xcode-12.5 (MarcoFalke)3b05a99b50
Merge bitcoin/bitcoin#22106: refactor: address ProcessNewBlock comments from #21713 (fanquake)c8725560c9
Merge bitcoin/bitcoin#21856: doc: add OSS-Fuzz section to fuzzing.md doc (MarcoFalke)facf685285
Merge bitcoin/bitcoin#22261: [p2p/mempool] Two small fixes to node broadcast logic (fanquake)1430897fc4
Merge bitcoin/bitcoin#22445: fuzz: Move implementations of non-template fuzz helpers from util.h to util.cpp (MarcoFalke)f0c62d50a5
Merge bitcoin/bitcoin#22447: test: whitelist rpc_rawtransaction peers to speed up tests (fanquake)b609514142
Merge #22381: guix: Test security-check sanity before performing them (Carl Dong)9ef68d1905
Merge bitcoin/bitcoin#22061: ci: Bump multiprocess memory (fanquake) Pull request description: ## Issue being fixed or feature implemented Regular backports from bitcoin v22 ## What was done? See commits ## How Has This Been Tested? Run unit and functional tests ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone ACKs for top commit: UdjinM6: utACK8f06ac9dfa
PastaPastaPasta: utACK8f06ac9dfa
Tree-SHA512: f800b7ca8d357f2d02ce5cb1fc4951c2765242676c5494efd5e22e8f6d41d889e1fa2f888930f72aded75813413c6488d8a7d96baa2cf4820e3461464708658e
This commit is contained in:
commit
bd27f65601
@ -139,7 +139,10 @@ task:
|
|||||||
<< : *GLOBAL_TASK_TEMPLATE
|
<< : *GLOBAL_TASK_TEMPLATE
|
||||||
container:
|
container:
|
||||||
image: ubuntu:focal
|
image: ubuntu:focal
|
||||||
|
cpu: 4
|
||||||
|
memory: 16G # The default memory is sometimes just a bit too small, so double everything
|
||||||
env:
|
env:
|
||||||
|
MAKEJOBS: "-j8"
|
||||||
FILE_ENV: "./ci/test/00_setup_env_native_multiprocess.sh"
|
FILE_ENV: "./ci/test/00_setup_env_native_multiprocess.sh"
|
||||||
|
|
||||||
task:
|
task:
|
||||||
@ -161,12 +164,11 @@ task:
|
|||||||
task:
|
task:
|
||||||
name: 'macOS 11 native [gui] [no depends]'
|
name: 'macOS 11 native [gui] [no depends]'
|
||||||
brew_install_script:
|
brew_install_script:
|
||||||
- brew update
|
|
||||||
- brew install boost libevent berkeley-db4 qt@5 miniupnpc ccache zeromq qrencode sqlite libtool automake pkg-config gnu-getopt
|
- brew install boost libevent berkeley-db4 qt@5 miniupnpc ccache zeromq qrencode sqlite libtool automake pkg-config gnu-getopt
|
||||||
<< : *GLOBAL_TASK_TEMPLATE
|
<< : *GLOBAL_TASK_TEMPLATE
|
||||||
macos_instance:
|
macos_instance:
|
||||||
# Use latest image, but hardcode version to avoid silent upgrades (and breaks)
|
# Use latest image, but hardcode version to avoid silent upgrades (and breaks)
|
||||||
image: big-sur-xcode-12.4 # https://cirrus-ci.org/guide/macOS
|
image: big-sur-xcode-12.5 # https://cirrus-ci.org/guide/macOS
|
||||||
env:
|
env:
|
||||||
DANGER_RUN_CI_ON_HOST: "true"
|
DANGER_RUN_CI_ON_HOST: "true"
|
||||||
CI_USE_APT_INSTALL: "no"
|
CI_USE_APT_INSTALL: "no"
|
||||||
|
@ -259,3 +259,9 @@ $ honggfuzz/honggfuzz --exit_upon_crash --quiet --timeout 4 -n 1 -Q \
|
|||||||
-nodebuglogfile -bind=127.0.0.1:18444 -logthreadnames \
|
-nodebuglogfile -bind=127.0.0.1:18444 -logthreadnames \
|
||||||
-debug
|
-debug
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# OSS-Fuzz
|
||||||
|
|
||||||
|
Bitcoin Core participates in Google's [OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/bitcoin-core)
|
||||||
|
program, which includes a dashboard of [publicly disclosed vulnerabilities](https://bugs.chromium.org/p/oss-fuzz/issues/list).
|
||||||
|
For more details, see [Bitcoin's OSS-fuzz](https://github.com/bitcoin/bitcoin/tree/master/doc/fuzzing.md)
|
||||||
|
@ -8,6 +8,14 @@ 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.
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
- Starting with version 20.0, Dash Core only supports Tor version 3 hidden
|
||||||
|
services (Tor v3). Tor v2 addresses are ignored by Dash Core and neither
|
||||||
|
relayed nor stored.
|
||||||
|
|
||||||
|
- Tor removed v2 support beginning with version 0.4.6.
|
||||||
|
|
||||||
## How to see information about your Tor configuration via Dash Core
|
## How to see information about your Tor configuration via Dash Core
|
||||||
|
|
||||||
There are several ways to see your local onion address in Dash Core:
|
There are several ways to see your local onion address in Dash Core:
|
||||||
|
@ -936,7 +936,8 @@ private:
|
|||||||
|
|
||||||
void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(peer.m_getdata_requests_mutex);
|
void ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(peer.m_getdata_requests_mutex);
|
||||||
|
|
||||||
void ProcessBlock(CNode& pfrom, const std::shared_ptr<const CBlock>& pblock, bool fForceProcessing);
|
/** Process a new block. Perform any post-processing housekeeping */
|
||||||
|
void ProcessBlock(CNode& from, const std::shared_ptr<const CBlock>& pblock, bool force_processing);
|
||||||
|
|
||||||
/** Relay map (txid -> CTransactionRef) */
|
/** Relay map (txid -> CTransactionRef) */
|
||||||
typedef std::map<uint256, CTransactionRef> MapRelay;
|
typedef std::map<uint256, CTransactionRef> MapRelay;
|
||||||
@ -3291,15 +3292,15 @@ std::pair<bool /*ret*/, bool /*do_return*/> static ValidateDSTX(CDeterministicMN
|
|||||||
return {true, false};
|
return {true, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerManagerImpl::ProcessBlock(CNode& pfrom, const std::shared_ptr<const CBlock>& pblock, bool fForceProcessing)
|
void PeerManagerImpl::ProcessBlock(CNode& node, const std::shared_ptr<const CBlock>& block, bool force_processing)
|
||||||
{
|
{
|
||||||
bool fNewBlock = false;
|
bool new_block{false};
|
||||||
m_chainman.ProcessNewBlock(m_chainparams, pblock, fForceProcessing, &fNewBlock);
|
m_chainman.ProcessNewBlock(m_chainparams, block, force_processing, &new_block);
|
||||||
if (fNewBlock) {
|
if (new_block) {
|
||||||
pfrom.m_last_block_time = GetTime<std::chrono::seconds>();
|
node.m_last_block_time = GetTime<std::chrono::seconds>();
|
||||||
} else {
|
} else {
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
mapBlockSource.erase(pblock->GetHash());
|
mapBlockSource.erase(block->GetHash());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4491,7 +4492,7 @@ void PeerManagerImpl::ProcessMessage(
|
|||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom.GetId(), false));
|
mapBlockSource.emplace(pblock->GetHash(), std::make_pair(pfrom.GetId(), false));
|
||||||
}
|
}
|
||||||
// Setting fForceProcessing to true means that we bypass some of
|
// Setting force_processing to true means that we bypass some of
|
||||||
// our anti-DoS protections in AcceptBlock, which filters
|
// our anti-DoS protections in AcceptBlock, which filters
|
||||||
// unrequested blocks that might be trying to waste our resources
|
// unrequested blocks that might be trying to waste our resources
|
||||||
// (eg disk space). Because we only try to reconstruct blocks when
|
// (eg disk space). Because we only try to reconstruct blocks when
|
||||||
@ -4500,7 +4501,7 @@ void PeerManagerImpl::ProcessMessage(
|
|||||||
// we have a chain with at least nMinimumChainWork), and we ignore
|
// we have a chain with at least nMinimumChainWork), and we ignore
|
||||||
// compact blocks with less work than our tip, it is safe to treat
|
// compact blocks with less work than our tip, it is safe to treat
|
||||||
// reconstructed compact blocks as having been requested.
|
// reconstructed compact blocks as having been requested.
|
||||||
ProcessBlock(pfrom, pblock, /*fForceProcessing=*/true);
|
ProcessBlock(pfrom, pblock, /*force_processing=*/true);
|
||||||
LOCK(cs_main); // hold cs_main for CBlockIndex::IsValid()
|
LOCK(cs_main); // hold cs_main for CBlockIndex::IsValid()
|
||||||
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) {
|
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS)) {
|
||||||
// Clear download state for this block, which is in
|
// Clear download state for this block, which is in
|
||||||
@ -4583,7 +4584,7 @@ void PeerManagerImpl::ProcessMessage(
|
|||||||
// disk-space attacks), but this should be safe due to the
|
// disk-space attacks), but this should be safe due to the
|
||||||
// protections in the compact block handler -- see related comment
|
// protections in the compact block handler -- see related comment
|
||||||
// in compact block optimistic reconstruction handling.
|
// in compact block optimistic reconstruction handling.
|
||||||
ProcessBlock(pfrom, pblock, /*fForceProcessing=*/true);
|
ProcessBlock(pfrom, pblock, /*force_processing=*/true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -31,28 +31,36 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
|
|||||||
|
|
||||||
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, bilingual_str& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback, bool bypass_limits)
|
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, bilingual_str& err_string, const CAmount& max_tx_fee, bool relay, bool wait_callback, bool bypass_limits)
|
||||||
{
|
{
|
||||||
// BroadcastTransaction can be called by either sendrawtransaction RPC or wallet RPCs.
|
// BroadcastTransaction can be called by either sendrawtransaction RPC or the wallet.
|
||||||
// node.peerman is assigned both before chain clients and before RPC server is accepting calls,
|
// chainman, mempool and peerman are initialized before the RPC server and wallet are started
|
||||||
// and reset after chain clients and RPC sever are stopped. node.peerman should never be null here.
|
// and reset after the RPC sever and wallet are stopped.
|
||||||
assert(node.peerman);
|
assert(node.chainman);
|
||||||
assert(node.mempool);
|
assert(node.mempool);
|
||||||
|
assert(node.peerman);
|
||||||
|
|
||||||
std::promise<void> promise;
|
std::promise<void> promise;
|
||||||
uint256 hashTx = tx->GetHash();
|
uint256 txid = tx->GetHash();
|
||||||
bool callback_set = false;
|
bool callback_set = false;
|
||||||
|
|
||||||
{ // cs_main scope
|
{
|
||||||
assert(node.chainman);
|
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
// If the transaction is already confirmed in the chain, don't do anything
|
// If the transaction is already confirmed in the chain, don't do anything
|
||||||
// and return early.
|
// and return early.
|
||||||
CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
|
CCoinsViewCache &view = node.chainman->ActiveChainstate().CoinsTip();
|
||||||
for (size_t o = 0; o < tx->vout.size(); o++) {
|
for (size_t o = 0; o < tx->vout.size(); o++) {
|
||||||
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
|
const Coin& existingCoin = view.AccessCoin(COutPoint(txid, o));
|
||||||
// IsSpent does not mean the coin is spent, it means the output does not exist.
|
// IsSpent does not mean the coin is spent, it means the output does not exist.
|
||||||
// So if the output does exist, then this transaction exists in the chain.
|
// So if the output does exist, then this transaction exists in the chain.
|
||||||
if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
|
if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
|
||||||
}
|
}
|
||||||
if (!node.mempool->exists(hashTx)) {
|
if (auto mempool_tx = node.mempool->get(txid); mempool_tx) {
|
||||||
|
// There's already a transaction in the mempool with this txid. Don't
|
||||||
|
// try to submit this transaction to the mempool (since it'll be
|
||||||
|
// rejected as a TX_CONFLICT), but do attempt to reannounce the mempool
|
||||||
|
// transaction if relay=true.
|
||||||
|
//
|
||||||
|
} else {
|
||||||
// Transaction is not already in the mempool.
|
// Transaction is not already in the mempool.
|
||||||
if (max_tx_fee > 0) {
|
if (max_tx_fee > 0) {
|
||||||
// First, call ATMP with test_accept and check the fee. If ATMP
|
// First, call ATMP with test_accept and check the fee. If ATMP
|
||||||
@ -74,6 +82,12 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
|||||||
|
|
||||||
// Transaction was accepted to the mempool.
|
// Transaction was accepted to the mempool.
|
||||||
|
|
||||||
|
if (relay) {
|
||||||
|
// the mempool tracks locally submitted transactions to make a
|
||||||
|
// best-effort of initial broadcast
|
||||||
|
node.mempool->AddUnbroadcastTx(txid);
|
||||||
|
}
|
||||||
|
|
||||||
if (wait_callback) {
|
if (wait_callback) {
|
||||||
// For transactions broadcast from outside the wallet, make sure
|
// For transactions broadcast from outside the wallet, make sure
|
||||||
// that the wallet has been notified of the transaction before
|
// that the wallet has been notified of the transaction before
|
||||||
@ -89,7 +103,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
|||||||
callback_set = true;
|
callback_set = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // cs_main
|
} // cs_main
|
||||||
|
|
||||||
if (callback_set) {
|
if (callback_set) {
|
||||||
@ -99,12 +112,8 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (relay) {
|
if (relay) {
|
||||||
// the mempool tracks locally submitted transactions to make a
|
|
||||||
// best-effort of initial broadcast
|
|
||||||
node.mempool->AddUnbroadcastTx(hashTx);
|
|
||||||
|
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
node.peerman->RelayTransaction(hashTx);
|
node.peerman->RelayTransaction(txid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TransactionError::OK;
|
return TransactionError::OK;
|
||||||
|
@ -361,3 +361,159 @@ CKey ConsumePrivateKey(FuzzedDataProvider& fuzzed_data_provider, std::optional<b
|
|||||||
key.Set(key_data.begin(), key_data.end(), compressed_value);
|
key.Set(key_data.begin(), key_data.end(), compressed_value);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept
|
||||||
|
{
|
||||||
|
// Avoid:
|
||||||
|
// policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long'
|
||||||
|
//
|
||||||
|
// Reproduce using CFeeRate(348732081484775, 10).GetFeePerK()
|
||||||
|
const CAmount fee = std::min<CAmount>(ConsumeMoney(fuzzed_data_provider), std::numeric_limits<CAmount>::max() / static_cast<CAmount>(100000));
|
||||||
|
assert(MoneyRange(fee));
|
||||||
|
const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
|
||||||
|
const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
||||||
|
const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
|
||||||
|
const bool dip1_status = fuzzed_data_provider.ConsumeBool();
|
||||||
|
const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MaxBlockSigOps(dip1_status));
|
||||||
|
return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept
|
||||||
|
{
|
||||||
|
for (const CTxIn& tx_in : tx.vin) {
|
||||||
|
const Coin& coin = inputs.AccessCoin(tx_in.prevout);
|
||||||
|
if (coin.IsSpent()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
||||||
|
{
|
||||||
|
const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
|
||||||
|
CNetAddr net_addr;
|
||||||
|
if (network == Network::NET_IPV4) {
|
||||||
|
in_addr v4_addr = {};
|
||||||
|
v4_addr.s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
|
||||||
|
net_addr = CNetAddr{v4_addr};
|
||||||
|
} else if (network == Network::NET_IPV6) {
|
||||||
|
if (fuzzed_data_provider.remaining_bytes() >= 16) {
|
||||||
|
in6_addr v6_addr = {};
|
||||||
|
memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
|
||||||
|
net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
||||||
|
}
|
||||||
|
} else if (network == Network::NET_INTERNAL) {
|
||||||
|
net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
|
||||||
|
} else if (network == Network::NET_ONION) {
|
||||||
|
net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
|
||||||
|
}
|
||||||
|
return net_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* FuzzedFileProvider::open()
|
||||||
|
{
|
||||||
|
SetFuzzedErrNo(m_fuzzed_data_provider);
|
||||||
|
if (m_fuzzed_data_provider.ConsumeBool()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
std::string mode;
|
||||||
|
CallOneOf(
|
||||||
|
m_fuzzed_data_provider,
|
||||||
|
[&] {
|
||||||
|
mode = "r";
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
mode = "r+";
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
mode = "w";
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
mode = "w+";
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
mode = "a";
|
||||||
|
},
|
||||||
|
[&] {
|
||||||
|
mode = "a+";
|
||||||
|
});
|
||||||
|
#if defined _GNU_SOURCE && !defined __ANDROID__
|
||||||
|
const cookie_io_functions_t io_hooks = {
|
||||||
|
FuzzedFileProvider::read,
|
||||||
|
FuzzedFileProvider::write,
|
||||||
|
FuzzedFileProvider::seek,
|
||||||
|
FuzzedFileProvider::close,
|
||||||
|
};
|
||||||
|
return fopencookie(this, mode.c_str(), io_hooks);
|
||||||
|
#else
|
||||||
|
(void)mode;
|
||||||
|
return nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t FuzzedFileProvider::read(void* cookie, char* buf, size_t size)
|
||||||
|
{
|
||||||
|
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
|
||||||
|
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
|
||||||
|
if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
|
||||||
|
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
|
||||||
|
}
|
||||||
|
const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
|
||||||
|
if (random_bytes.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::memcpy(buf, random_bytes.data(), random_bytes.size());
|
||||||
|
if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)random_bytes.size())) {
|
||||||
|
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
|
||||||
|
}
|
||||||
|
fuzzed_file->m_offset += random_bytes.size();
|
||||||
|
return random_bytes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t FuzzedFileProvider::write(void* cookie, const char* buf, size_t size)
|
||||||
|
{
|
||||||
|
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
|
||||||
|
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
|
||||||
|
const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
|
||||||
|
if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) {
|
||||||
|
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
|
||||||
|
}
|
||||||
|
fuzzed_file->m_offset += n;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FuzzedFileProvider::seek(void* cookie, int64_t* offset, int whence)
|
||||||
|
{
|
||||||
|
assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
|
||||||
|
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
|
||||||
|
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
|
||||||
|
int64_t new_offset = 0;
|
||||||
|
if (whence == SEEK_SET) {
|
||||||
|
new_offset = *offset;
|
||||||
|
} else if (whence == SEEK_CUR) {
|
||||||
|
if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
new_offset = fuzzed_file->m_offset + *offset;
|
||||||
|
} else if (whence == SEEK_END) {
|
||||||
|
const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096);
|
||||||
|
if (AdditionOverflow(n, *offset)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
new_offset = n + *offset;
|
||||||
|
}
|
||||||
|
if (new_offset < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
fuzzed_file->m_offset = new_offset;
|
||||||
|
*offset = new_offset;
|
||||||
|
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int FuzzedFileProvider::close(void* cookie)
|
||||||
|
{
|
||||||
|
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
|
||||||
|
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
|
||||||
|
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
|
||||||
|
}
|
||||||
|
@ -218,21 +218,7 @@ template <typename WeakEnumType, size_t size>
|
|||||||
return UintToArith256(ConsumeUInt256(fuzzed_data_provider));
|
return UintToArith256(ConsumeUInt256(fuzzed_data_provider));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept
|
[[nodiscard]] CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept;
|
||||||
{
|
|
||||||
// Avoid:
|
|
||||||
// policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long'
|
|
||||||
//
|
|
||||||
// Reproduce using CFeeRate(348732081484775, 10).GetFeePerK()
|
|
||||||
const CAmount fee = std::min<CAmount>(ConsumeMoney(fuzzed_data_provider), std::numeric_limits<CAmount>::max() / static_cast<CAmount>(100000));
|
|
||||||
assert(MoneyRange(fee));
|
|
||||||
const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
|
|
||||||
const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
|
|
||||||
const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
|
|
||||||
const bool dip1_status = fuzzed_data_provider.ConsumeBool();
|
|
||||||
const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MaxBlockSigOps(dip1_status));
|
|
||||||
return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
[[nodiscard]] inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
||||||
{
|
{
|
||||||
@ -276,16 +262,7 @@ template <typename T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept
|
[[nodiscard]] bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept;
|
||||||
{
|
|
||||||
for (const CTxIn& tx_in : tx.vin) {
|
|
||||||
const Coin& coin = inputs.AccessCoin(tx_in.prevout);
|
|
||||||
if (coin.IsSpent()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets errno to a value selected from the given std::array `errnos`.
|
* Sets errno to a value selected from the given std::array `errnos`.
|
||||||
@ -319,27 +296,7 @@ template<typename B = uint8_t>
|
|||||||
return random_bytes;
|
return random_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept;
|
||||||
{
|
|
||||||
const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
|
|
||||||
CNetAddr net_addr;
|
|
||||||
if (network == Network::NET_IPV4) {
|
|
||||||
in_addr v4_addr = {};
|
|
||||||
v4_addr.s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
|
|
||||||
net_addr = CNetAddr{v4_addr};
|
|
||||||
} else if (network == Network::NET_IPV6) {
|
|
||||||
if (fuzzed_data_provider.remaining_bytes() >= 16) {
|
|
||||||
in6_addr v6_addr = {};
|
|
||||||
memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
|
|
||||||
net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
|
|
||||||
}
|
|
||||||
} else if (network == Network::NET_INTERNAL) {
|
|
||||||
net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
|
|
||||||
} else if (network == Network::NET_ONION) {
|
|
||||||
net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
|
|
||||||
}
|
|
||||||
return net_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
inline CSubNet ConsumeSubNet(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
||||||
{
|
{
|
||||||
@ -408,112 +365,15 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* open()
|
FILE* open();
|
||||||
{
|
|
||||||
SetFuzzedErrNo(m_fuzzed_data_provider);
|
|
||||||
if (m_fuzzed_data_provider.ConsumeBool()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
std::string mode;
|
|
||||||
CallOneOf(
|
|
||||||
m_fuzzed_data_provider,
|
|
||||||
[&] {
|
|
||||||
mode = "r";
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
mode = "r+";
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
mode = "w";
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
mode = "w+";
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
mode = "a";
|
|
||||||
},
|
|
||||||
[&] {
|
|
||||||
mode = "a+";
|
|
||||||
});
|
|
||||||
#if defined _GNU_SOURCE && !defined __ANDROID__
|
|
||||||
const cookie_io_functions_t io_hooks = {
|
|
||||||
FuzzedFileProvider::read,
|
|
||||||
FuzzedFileProvider::write,
|
|
||||||
FuzzedFileProvider::seek,
|
|
||||||
FuzzedFileProvider::close,
|
|
||||||
};
|
|
||||||
return fopencookie(this, mode.c_str(), io_hooks);
|
|
||||||
#else
|
|
||||||
(void)mode;
|
|
||||||
return nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t read(void* cookie, char* buf, size_t size)
|
static ssize_t read(void* cookie, char* buf, size_t size);
|
||||||
{
|
|
||||||
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
|
|
||||||
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
|
|
||||||
if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
|
|
||||||
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
|
|
||||||
}
|
|
||||||
const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
|
|
||||||
if (random_bytes.empty()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
std::memcpy(buf, random_bytes.data(), random_bytes.size());
|
|
||||||
if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)random_bytes.size())) {
|
|
||||||
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
|
|
||||||
}
|
|
||||||
fuzzed_file->m_offset += random_bytes.size();
|
|
||||||
return random_bytes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t write(void* cookie, const char* buf, size_t size)
|
static ssize_t write(void* cookie, const char* buf, size_t size);
|
||||||
{
|
|
||||||
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
|
|
||||||
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
|
|
||||||
const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
|
|
||||||
if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) {
|
|
||||||
return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
|
|
||||||
}
|
|
||||||
fuzzed_file->m_offset += n;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int seek(void* cookie, int64_t* offset, int whence)
|
static int seek(void* cookie, int64_t* offset, int whence);
|
||||||
{
|
|
||||||
assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
|
|
||||||
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
|
|
||||||
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
|
|
||||||
int64_t new_offset = 0;
|
|
||||||
if (whence == SEEK_SET) {
|
|
||||||
new_offset = *offset;
|
|
||||||
} else if (whence == SEEK_CUR) {
|
|
||||||
if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
new_offset = fuzzed_file->m_offset + *offset;
|
|
||||||
} else if (whence == SEEK_END) {
|
|
||||||
const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096);
|
|
||||||
if (AdditionOverflow(n, *offset)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
new_offset = n + *offset;
|
|
||||||
}
|
|
||||||
if (new_offset < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
fuzzed_file->m_offset = new_offset;
|
|
||||||
*offset = new_offset;
|
|
||||||
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int close(void* cookie)
|
static int close(void* cookie);
|
||||||
{
|
|
||||||
FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
|
|
||||||
SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
|
|
||||||
return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] inline FuzzedFileProvider ConsumeFile(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
[[nodiscard]] inline FuzzedFileProvider ConsumeFile(FuzzedDataProvider& fuzzed_data_provider) noexcept
|
||||||
|
@ -3948,13 +3948,13 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock)
|
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
|
||||||
{
|
{
|
||||||
AssertLockNotHeld(cs_main);
|
AssertLockNotHeld(cs_main);
|
||||||
|
|
||||||
{
|
{
|
||||||
CBlockIndex *pindex = nullptr;
|
CBlockIndex *pindex = nullptr;
|
||||||
if (fNewBlock) *fNewBlock = false;
|
if (new_block) *new_block = false;
|
||||||
BlockValidationState state;
|
BlockValidationState state;
|
||||||
|
|
||||||
// CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
|
// CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
|
||||||
@ -3966,13 +3966,13 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
|
|||||||
// malleability that cause CheckBlock() to fail; see e.g. CVE-2012-2459 and
|
// malleability that cause CheckBlock() to fail; see e.g. CVE-2012-2459 and
|
||||||
// https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html. Because CheckBlock() is
|
// https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html. Because CheckBlock() is
|
||||||
// not very expensive, the anti-DoS benefits of caching failure (of a definitely-invalid block) are not substantial.
|
// not very expensive, the anti-DoS benefits of caching failure (of a definitely-invalid block) are not substantial.
|
||||||
bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus());
|
bool ret = CheckBlock(*block, state, chainparams.GetConsensus());
|
||||||
if (ret) {
|
if (ret) {
|
||||||
// Store to disk
|
// Store to disk
|
||||||
ret = ActiveChainstate().AcceptBlock(pblock, state, &pindex, fForceProcessing, nullptr, fNewBlock);
|
ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block);
|
||||||
}
|
}
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
GetMainSignals().BlockChecked(*pblock, state);
|
GetMainSignals().BlockChecked(*block, state);
|
||||||
return error("%s: AcceptBlock FAILED: %s", __func__, state.ToString());
|
return error("%s: AcceptBlock FAILED: %s", __func__, state.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3980,7 +3980,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
|
|||||||
NotifyHeaderTip(ActiveChainstate());
|
NotifyHeaderTip(ActiveChainstate());
|
||||||
|
|
||||||
BlockValidationState state; // Only used to report errors, not invalidity - ignore it
|
BlockValidationState state; // Only used to report errors, not invalidity - ignore it
|
||||||
if (!ActiveChainstate().ActivateBestChain(state, pblock))
|
if (!ActiveChainstate().ActivateBestChain(state, block))
|
||||||
return error("%s: ActivateBestChain failed: %s", __func__, state.ToString());
|
return error("%s: ActivateBestChain failed: %s", __func__, state.ToString());
|
||||||
|
|
||||||
LogPrintf("%s : ACCEPTED\n", __func__);
|
LogPrintf("%s : ACCEPTED\n", __func__);
|
||||||
|
@ -995,22 +995,21 @@ public:
|
|||||||
* block is made active. Note that it does not, however, guarantee that the
|
* block is made active. Note that it does not, however, guarantee that the
|
||||||
* specific block passed to it has been checked for validity!
|
* specific block passed to it has been checked for validity!
|
||||||
*
|
*
|
||||||
* If you want to *possibly* get feedback on whether pblock is valid, you must
|
* If you want to *possibly* get feedback on whether block is valid, you must
|
||||||
* install a CValidationInterface (see validationinterface.h) - this will have
|
* install a CValidationInterface (see validationinterface.h) - this will have
|
||||||
* its BlockChecked method called whenever *any* block completes validation.
|
* its BlockChecked method called whenever *any* block completes validation.
|
||||||
*
|
*
|
||||||
* Note that we guarantee that either the proof-of-work is valid on pblock, or
|
* Note that we guarantee that either the proof-of-work is valid on block, or
|
||||||
* (and possibly also) BlockChecked will have been called.
|
* (and possibly also) BlockChecked will have been called.
|
||||||
*
|
*
|
||||||
* May not be called in a
|
* May not be called in a validationinterface callback.
|
||||||
* validationinterface callback.
|
|
||||||
*
|
*
|
||||||
* @param[in] pblock The block we want to process.
|
* @param[in] block The block we want to process.
|
||||||
* @param[in] fForceProcessing Process this block even if unrequested; used for non-network block sources.
|
* @param[in] force_processing Process this block even if unrequested; used for non-network block sources.
|
||||||
* @param[out] fNewBlock A boolean which is set to indicate if the block was first received via this call
|
* @param[out] new_block A boolean which is set to indicate if the block was first received via this call
|
||||||
* @returns If the block was processed, independently of block validity
|
* @returns If the block was processed, independently of block validity
|
||||||
*/
|
*/
|
||||||
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock> pblock, bool fForceProcessing, bool* fNewBlock) LOCKS_EXCLUDED(cs_main);
|
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block) LOCKS_EXCLUDED(cs_main);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process incoming block headers.
|
* Process incoming block headers.
|
||||||
|
@ -92,6 +92,12 @@ class MempoolUnbroadcastTest(BitcoinTestFramework):
|
|||||||
self.disconnect_nodes(0, 1)
|
self.disconnect_nodes(0, 1)
|
||||||
node.disconnect_p2ps()
|
node.disconnect_p2ps()
|
||||||
|
|
||||||
|
self.log.info("Rebroadcast transaction and ensure it is not added to unbroadcast set when already in mempool")
|
||||||
|
rpc_tx_hsh = node.sendrawtransaction(txFS["hex"])
|
||||||
|
mempool = node.getrawmempool(True)
|
||||||
|
assert rpc_tx_hsh in mempool
|
||||||
|
assert not mempool[rpc_tx_hsh]['unbroadcast']
|
||||||
|
|
||||||
def test_txn_removal(self):
|
def test_txn_removal(self):
|
||||||
self.log.info("Test that transactions removed from mempool are removed from unbroadcast set")
|
self.log.info("Test that transactions removed from mempool are removed from unbroadcast set")
|
||||||
node = self.nodes[0]
|
node = self.nodes[0]
|
||||||
|
@ -56,6 +56,10 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
["-txindex"],
|
["-txindex"],
|
||||||
["-txindex"],
|
["-txindex"],
|
||||||
]
|
]
|
||||||
|
# whitelist all peers to speed up tx relay / mempool sync
|
||||||
|
for args in self.extra_args:
|
||||||
|
args.append("-whitelist=noban@127.0.0.1")
|
||||||
|
|
||||||
self.supports_cli = False
|
self.supports_cli = False
|
||||||
|
|
||||||
def skip_test_if_missing_module(self):
|
def skip_test_if_missing_module(self):
|
||||||
|
@ -148,8 +148,6 @@ BASE_SCRIPTS = [
|
|||||||
'wallet_abandonconflict.py --legacy-wallet',
|
'wallet_abandonconflict.py --legacy-wallet',
|
||||||
'wallet_abandonconflict.py --descriptors',
|
'wallet_abandonconflict.py --descriptors',
|
||||||
'feature_csv_activation.py',
|
'feature_csv_activation.py',
|
||||||
'rpc_rawtransaction.py --legacy-wallet',
|
|
||||||
'rpc_rawtransaction.py --descriptors',
|
|
||||||
'feature_reindex.py',
|
'feature_reindex.py',
|
||||||
'feature_abortnode.py',
|
'feature_abortnode.py',
|
||||||
# vv Tests less than 30s vv
|
# vv Tests less than 30s vv
|
||||||
@ -200,6 +198,8 @@ BASE_SCRIPTS = [
|
|||||||
'feature_proxy.py',
|
'feature_proxy.py',
|
||||||
'rpc_signrawtransaction.py --legacy-wallet',
|
'rpc_signrawtransaction.py --legacy-wallet',
|
||||||
'rpc_signrawtransaction.py --descriptors',
|
'rpc_signrawtransaction.py --descriptors',
|
||||||
|
'rpc_rawtransaction.py --legacy-wallet',
|
||||||
|
'rpc_rawtransaction.py --descriptors',
|
||||||
'p2p_addrv2_relay.py',
|
'p2p_addrv2_relay.py',
|
||||||
'wallet_groups.py --legacy-wallet',
|
'wallet_groups.py --legacy-wallet',
|
||||||
'wallet_groups.py --descriptors',
|
'wallet_groups.py --descriptors',
|
||||||
|
@ -111,7 +111,7 @@ if ! PYTHONWARNINGS="ignore" $FLAKECMD --ignore=B,C,E,F,I,N,W --select=$(IFS=","
|
|||||||
EXIT_CODE=1
|
EXIT_CODE=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! mypy --ignore-missing-imports $(git ls-files "test/functional/*.py" "contrib/devtools/*.py" | grep -v contrib/devtools/github-merge.py) ; then
|
if ! mypy --ignore-missing-imports --show-error-codes $(git ls-files "test/functional/*.py" "contrib/devtools/*.py" | grep -v contrib/devtools/github-merge.py) ; then
|
||||||
EXIT_CODE=1
|
EXIT_CODE=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user