mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-09 10:43:19 -05:00
Merge bitcoin/bitcoin#28956: Nuke adjusted time from validation (attempt 2)
ff9039f6ea
Remove GetAdjustedTime (dergoegge) Pull request description: This picks up parts of #25908. The use of adjusted time is removed from validation code while the warning to users if their clock is out of sync with the rest of the network remains. ACKs for top commit: naumenkogs: ACKff9039f6ea
achow101: ACKff9039f6ea
maflcko: lgtm ACKff9039f6ea
🤽 stickies-v: ACKff9039f6ea
Tree-SHA512: d1f6b9445c236915503fd2ea828f0d3b92285a5dbc677b168453276115e349972edbad37194d8becd9136d8e7219b576af64ec51c72bdb1923e57e405c0483fc
This commit is contained in:
commit
3c13f5d612
13 changed files with 15 additions and 29 deletions
|
@ -117,7 +117,6 @@ int main(int argc, char* argv[])
|
||||||
const ChainstateManager::Options chainman_opts{
|
const ChainstateManager::Options chainman_opts{
|
||||||
.chainparams = *chainparams,
|
.chainparams = *chainparams,
|
||||||
.datadir = abs_datadir,
|
.datadir = abs_datadir,
|
||||||
.adjusted_time_callback = NodeClock::now,
|
|
||||||
.notifications = *notifications,
|
.notifications = *notifications,
|
||||||
};
|
};
|
||||||
const node::BlockManager::Options blockman_opts{
|
const node::BlockManager::Options blockman_opts{
|
||||||
|
|
|
@ -41,7 +41,7 @@ HeadersSyncState::HeadersSyncState(NodeId id, const Consensus::Params& consensus
|
||||||
// exceeds this bound, because it's not possible for a consensus-valid
|
// exceeds this bound, because it's not possible for a consensus-valid
|
||||||
// chain to be longer than this (at the current time -- in the future we
|
// chain to be longer than this (at the current time -- in the future we
|
||||||
// could try again, if necessary, to sync a longer chain).
|
// could try again, if necessary, to sync a longer chain).
|
||||||
m_max_commitments = 6*(Ticks<std::chrono::seconds>(GetAdjustedTime() - NodeSeconds{std::chrono::seconds{chain_start->GetMedianTimePast()}}) + MAX_FUTURE_BLOCK_TIME) / HEADER_COMMITMENT_PERIOD;
|
m_max_commitments = 6*(Ticks<std::chrono::seconds>(NodeClock::now() - NodeSeconds{std::chrono::seconds{chain_start->GetMedianTimePast()}}) + MAX_FUTURE_BLOCK_TIME) / HEADER_COMMITMENT_PERIOD;
|
||||||
|
|
||||||
LogPrint(BCLog::NET, "Initial headers sync started with peer=%d: height=%i, max_commitments=%i, min_work=%s\n", m_id, m_current_height, m_max_commitments, m_minimum_required_work.ToString());
|
LogPrint(BCLog::NET, "Initial headers sync started with peer=%d: height=%i, max_commitments=%i, min_work=%s\n", m_id, m_current_height, m_max_commitments, m_minimum_required_work.ToString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1448,7 +1448,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||||
ChainstateManager::Options chainman_opts{
|
ChainstateManager::Options chainman_opts{
|
||||||
.chainparams = chainparams,
|
.chainparams = chainparams,
|
||||||
.datadir = args.GetDataDirNet(),
|
.datadir = args.GetDataDirNet(),
|
||||||
.adjusted_time_callback = GetAdjustedTime,
|
|
||||||
.notifications = *node.notifications,
|
.notifications = *node.notifications,
|
||||||
};
|
};
|
||||||
Assert(ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction
|
Assert(ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction
|
||||||
|
|
|
@ -32,7 +32,6 @@ namespace kernel {
|
||||||
struct ChainstateManagerOpts {
|
struct ChainstateManagerOpts {
|
||||||
const CChainParams& chainparams;
|
const CChainParams& chainparams;
|
||||||
fs::path datadir;
|
fs::path datadir;
|
||||||
const std::function<NodeClock::time_point()> adjusted_time_callback{nullptr};
|
|
||||||
std::optional<bool> check_block_index{};
|
std::optional<bool> check_block_index{};
|
||||||
bool checkpoints_enabled{DEFAULT_CHECKPOINTS_ENABLED};
|
bool checkpoints_enabled{DEFAULT_CHECKPOINTS_ENABLED};
|
||||||
//! If set, it will override the minimum work we will assume exists on some valid chain.
|
//! If set, it will override the minimum work we will assume exists on some valid chain.
|
||||||
|
|
|
@ -1334,7 +1334,7 @@ int64_t PeerManagerImpl::ApproximateBestBlockDepth() const
|
||||||
|
|
||||||
bool PeerManagerImpl::CanDirectFetch()
|
bool PeerManagerImpl::CanDirectFetch()
|
||||||
{
|
{
|
||||||
return m_chainman.ActiveChain().Tip()->Time() > GetAdjustedTime() - m_chainparams.GetConsensus().PowTargetSpacing() * 20;
|
return m_chainman.ActiveChain().Tip()->Time() > NodeClock::now() - m_chainparams.GetConsensus().PowTargetSpacing() * 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
static bool PeerHasHeader(CNodeState *state, const CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
|
@ -5592,7 +5592,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||||
|
|
||||||
if (!state.fSyncStarted && CanServeBlocks(*peer) && !m_chainman.m_blockman.LoadingBlocks()) {
|
if (!state.fSyncStarted && CanServeBlocks(*peer) && !m_chainman.m_blockman.LoadingBlocks()) {
|
||||||
// Only actively request headers from a single peer, unless we're close to today.
|
// Only actively request headers from a single peer, unless we're close to today.
|
||||||
if ((nSyncStarted == 0 && sync_blocks_and_headers_from_peer) || m_chainman.m_best_header->Time() > GetAdjustedTime() - 24h) {
|
if ((nSyncStarted == 0 && sync_blocks_and_headers_from_peer) || m_chainman.m_best_header->Time() > NodeClock::now() - 24h) {
|
||||||
const CBlockIndex* pindexStart = m_chainman.m_best_header;
|
const CBlockIndex* pindexStart = m_chainman.m_best_header;
|
||||||
/* If possible, start at the block preceding the currently
|
/* If possible, start at the block preceding the currently
|
||||||
best known header. This ensures that we always get a
|
best known header. This ensures that we always get a
|
||||||
|
@ -5612,7 +5612,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||||
// Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling
|
// Convert HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER to microseconds before scaling
|
||||||
// to maintain precision
|
// to maintain precision
|
||||||
std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} *
|
std::chrono::microseconds{HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER} *
|
||||||
Ticks<std::chrono::seconds>(GetAdjustedTime() - m_chainman.m_best_header->Time()) / consensusParams.nPowTargetSpacing
|
Ticks<std::chrono::seconds>(NodeClock::now() - m_chainman.m_best_header->Time()) / consensusParams.nPowTargetSpacing
|
||||||
);
|
);
|
||||||
nSyncStarted++;
|
nSyncStarted++;
|
||||||
}
|
}
|
||||||
|
@ -5916,7 +5916,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
||||||
// Check for headers sync timeouts
|
// Check for headers sync timeouts
|
||||||
if (state.fSyncStarted && peer->m_headers_sync_timeout < std::chrono::microseconds::max()) {
|
if (state.fSyncStarted && peer->m_headers_sync_timeout < std::chrono::microseconds::max()) {
|
||||||
// Detect whether this is a stalling initial-headers-sync peer
|
// Detect whether this is a stalling initial-headers-sync peer
|
||||||
if (m_chainman.m_best_header->Time() <= GetAdjustedTime() - 24h) {
|
if (m_chainman.m_best_header->Time() <= NodeClock::now() - 24h) {
|
||||||
if (current_time > peer->m_headers_sync_timeout && nSyncStarted == 1 && (m_num_preferred_download_peers - state.fPreferredDownload >= 1)) {
|
if (current_time > peer->m_headers_sync_timeout && nSyncStarted == 1 && (m_num_preferred_download_peers - state.fPreferredDownload >= 1)) {
|
||||||
// Disconnect a peer (without NetPermissionFlags::NoBan permission) if it is our only sync peer,
|
// Disconnect a peer (without NetPermissionFlags::NoBan permission) if it is our only sync peer,
|
||||||
// and we have others we could be using instead.
|
// and we have others we could be using instead.
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace node {
|
||||||
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
|
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
|
||||||
{
|
{
|
||||||
int64_t nOldTime = pblock->nTime;
|
int64_t nOldTime = pblock->nTime;
|
||||||
int64_t nNewTime{std::max<int64_t>(pindexPrev->GetMedianTimePast() + 1, TicksSinceEpoch<std::chrono::seconds>(GetAdjustedTime()))};
|
int64_t nNewTime{std::max<int64_t>(pindexPrev->GetMedianTimePast() + 1, TicksSinceEpoch<std::chrono::seconds>(NodeClock::now()))};
|
||||||
|
|
||||||
if (nOldTime < nNewTime) {
|
if (nOldTime < nNewTime) {
|
||||||
pblock->nTime = nNewTime;
|
pblock->nTime = nNewTime;
|
||||||
|
@ -133,7 +133,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
||||||
pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion);
|
pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
pblock->nTime = TicksSinceEpoch<std::chrono::seconds>(GetAdjustedTime());
|
pblock->nTime = TicksSinceEpoch<std::chrono::seconds>(NodeClock::now());
|
||||||
m_lock_time_cutoff = pindexPrev->GetMedianTimePast();
|
m_lock_time_cutoff = pindexPrev->GetMedianTimePast();
|
||||||
|
|
||||||
int nPackagesSelected = 0;
|
int nPackagesSelected = 0;
|
||||||
|
@ -171,7 +171,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
||||||
|
|
||||||
BlockValidationState state;
|
BlockValidationState state;
|
||||||
if (m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev,
|
if (m_options.test_block_validity && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev,
|
||||||
GetAdjustedTime, /*fCheckPOW=*/false, /*fCheckMerkleRoot=*/false)) {
|
/*fCheckPOW=*/false, /*fCheckMerkleRoot=*/false)) {
|
||||||
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
|
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
|
||||||
}
|
}
|
||||||
const auto time_2{SteadyClock::now()};
|
const auto time_2{SteadyClock::now()};
|
||||||
|
|
|
@ -383,7 +383,7 @@ static RPCHelpMan generateblock()
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
|
|
||||||
BlockValidationState state;
|
BlockValidationState state;
|
||||||
if (!TestBlockValidity(state, chainman.GetParams(), chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), GetAdjustedTime, false, false)) {
|
if (!TestBlockValidity(state, chainman.GetParams(), chainman.ActiveChainstate(), block, chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) {
|
||||||
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString()));
|
throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.ToString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -697,7 +697,7 @@ static RPCHelpMan getblocktemplate()
|
||||||
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
|
if (block.hashPrevBlock != pindexPrev->GetBlockHash())
|
||||||
return "inconclusive-not-best-prevblk";
|
return "inconclusive-not-best-prevblk";
|
||||||
BlockValidationState state;
|
BlockValidationState state;
|
||||||
TestBlockValidity(state, chainman.GetParams(), active_chainstate, block, pindexPrev, GetAdjustedTime, false, true);
|
TestBlockValidity(state, chainman.GetParams(), active_chainstate, block, pindexPrev, false, true);
|
||||||
return BIP22ValidationResult(state);
|
return BIP22ValidationResult(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -184,7 +184,6 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto
|
||||||
const ChainstateManager::Options chainman_opts{
|
const ChainstateManager::Options chainman_opts{
|
||||||
.chainparams = chainparams,
|
.chainparams = chainparams,
|
||||||
.datadir = m_args.GetDataDirNet(),
|
.datadir = m_args.GetDataDirNet(),
|
||||||
.adjusted_time_callback = GetAdjustedTime,
|
|
||||||
.check_block_index = true,
|
.check_block_index = true,
|
||||||
.notifications = *m_node.notifications,
|
.notifications = *m_node.notifications,
|
||||||
.worker_threads_num = 2,
|
.worker_threads_num = 2,
|
||||||
|
|
|
@ -383,7 +383,6 @@ struct SnapshotTestSetup : TestChain100Setup {
|
||||||
const ChainstateManager::Options chainman_opts{
|
const ChainstateManager::Options chainman_opts{
|
||||||
.chainparams = ::Params(),
|
.chainparams = ::Params(),
|
||||||
.datadir = chainman.m_options.datadir,
|
.datadir = chainman.m_options.datadir,
|
||||||
.adjusted_time_callback = GetAdjustedTime,
|
|
||||||
.notifications = *m_node.notifications,
|
.notifications = *m_node.notifications,
|
||||||
};
|
};
|
||||||
const BlockManager::Options blockman_opts{
|
const BlockManager::Options blockman_opts{
|
||||||
|
|
|
@ -33,11 +33,6 @@ int64_t GetTimeOffset()
|
||||||
return nTimeOffset;
|
return nTimeOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeClock::time_point GetAdjustedTime()
|
|
||||||
{
|
|
||||||
return NodeClock::now() + std::chrono::seconds{GetTimeOffset()};
|
|
||||||
}
|
|
||||||
|
|
||||||
#define BITCOIN_TIMEDATA_MAX_SAMPLES 200
|
#define BITCOIN_TIMEDATA_MAX_SAMPLES 200
|
||||||
|
|
||||||
static std::set<CNetAddr> g_sources;
|
static std::set<CNetAddr> g_sources;
|
||||||
|
|
|
@ -75,11 +75,10 @@ public:
|
||||||
|
|
||||||
/** Functions to keep track of adjusted P2P time */
|
/** Functions to keep track of adjusted P2P time */
|
||||||
int64_t GetTimeOffset();
|
int64_t GetTimeOffset();
|
||||||
NodeClock::time_point GetAdjustedTime();
|
|
||||||
void AddTimeData(const CNetAddr& ip, int64_t nTime);
|
void AddTimeData(const CNetAddr& ip, int64_t nTime);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the internal state of GetTimeOffset(), GetAdjustedTime() and AddTimeData().
|
* Reset the internal state of GetTimeOffset() and AddTimeData().
|
||||||
*/
|
*/
|
||||||
void TestOnlyResetTimeData();
|
void TestOnlyResetTimeData();
|
||||||
|
|
||||||
|
|
|
@ -3777,7 +3777,7 @@ arith_uint256 CalculateHeadersWork(const std::vector<CBlockHeader>& headers)
|
||||||
* in ConnectBlock().
|
* in ConnectBlock().
|
||||||
* Note that -reindex-chainstate skips the validation that happens here!
|
* Note that -reindex-chainstate skips the validation that happens here!
|
||||||
*/
|
*/
|
||||||
static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const ChainstateManager& chainman, const CBlockIndex* pindexPrev, NodeClock::time_point now) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidationState& state, BlockManager& blockman, const ChainstateManager& chainman, const CBlockIndex* pindexPrev) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
|
||||||
{
|
{
|
||||||
AssertLockHeld(::cs_main);
|
AssertLockHeld(::cs_main);
|
||||||
assert(pindexPrev != nullptr);
|
assert(pindexPrev != nullptr);
|
||||||
|
@ -3805,7 +3805,7 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
||||||
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", "block's timestamp is too early");
|
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", "block's timestamp is too early");
|
||||||
|
|
||||||
// Check timestamp
|
// Check timestamp
|
||||||
if (block.Time() > now + std::chrono::seconds{MAX_FUTURE_BLOCK_TIME}) {
|
if (block.Time() > NodeClock::now() + std::chrono::seconds{MAX_FUTURE_BLOCK_TIME}) {
|
||||||
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future");
|
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3945,7 +3945,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
|
||||||
LogPrint(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
|
LogPrint(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
|
||||||
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
|
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
|
||||||
}
|
}
|
||||||
if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev, m_options.adjusted_time_callback())) {
|
if (!ContextualCheckBlockHeader(block, state, m_blockman, *this, pindexPrev)) {
|
||||||
LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
|
LogPrint(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -4230,7 +4230,6 @@ bool TestBlockValidity(BlockValidationState& state,
|
||||||
Chainstate& chainstate,
|
Chainstate& chainstate,
|
||||||
const CBlock& block,
|
const CBlock& block,
|
||||||
CBlockIndex* pindexPrev,
|
CBlockIndex* pindexPrev,
|
||||||
const std::function<NodeClock::time_point()>& adjusted_time_callback,
|
|
||||||
bool fCheckPOW,
|
bool fCheckPOW,
|
||||||
bool fCheckMerkleRoot)
|
bool fCheckMerkleRoot)
|
||||||
{
|
{
|
||||||
|
@ -4244,7 +4243,7 @@ bool TestBlockValidity(BlockValidationState& state,
|
||||||
indexDummy.phashBlock = &block_hash;
|
indexDummy.phashBlock = &block_hash;
|
||||||
|
|
||||||
// NOTE: CheckBlockHeader is called by CheckBlock
|
// NOTE: CheckBlockHeader is called by CheckBlock
|
||||||
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, pindexPrev, adjusted_time_callback()))
|
if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainstate.m_chainman, pindexPrev))
|
||||||
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
|
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
|
||||||
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
|
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
|
||||||
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
|
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
|
||||||
|
@ -5780,7 +5779,6 @@ static ChainstateManager::Options&& Flatten(ChainstateManager::Options&& opts)
|
||||||
if (!opts.check_block_index.has_value()) opts.check_block_index = opts.chainparams.DefaultConsistencyChecks();
|
if (!opts.check_block_index.has_value()) opts.check_block_index = opts.chainparams.DefaultConsistencyChecks();
|
||||||
if (!opts.minimum_chain_work.has_value()) opts.minimum_chain_work = UintToArith256(opts.chainparams.GetConsensus().nMinimumChainWork);
|
if (!opts.minimum_chain_work.has_value()) opts.minimum_chain_work = UintToArith256(opts.chainparams.GetConsensus().nMinimumChainWork);
|
||||||
if (!opts.assumed_valid_block.has_value()) opts.assumed_valid_block = opts.chainparams.GetConsensus().defaultAssumeValid;
|
if (!opts.assumed_valid_block.has_value()) opts.assumed_valid_block = opts.chainparams.GetConsensus().defaultAssumeValid;
|
||||||
Assert(opts.adjusted_time_callback);
|
|
||||||
return std::move(opts);
|
return std::move(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -377,7 +377,6 @@ bool TestBlockValidity(BlockValidationState& state,
|
||||||
Chainstate& chainstate,
|
Chainstate& chainstate,
|
||||||
const CBlock& block,
|
const CBlock& block,
|
||||||
CBlockIndex* pindexPrev,
|
CBlockIndex* pindexPrev,
|
||||||
const std::function<NodeClock::time_point()>& adjusted_time_callback,
|
|
||||||
bool fCheckPOW = true,
|
bool fCheckPOW = true,
|
||||||
bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue