Changeset View
Standalone View
src/validation.h
Show First 20 Lines • Show All 460 Lines • ▼ Show 20 Lines | bool AcceptToMemoryPool(const Config &config, CTxMemPool &pool, | ||||
bool *pfMissingInputs, bool bypass_limits, | bool *pfMissingInputs, bool bypass_limits, | ||||
const Amount nAbsurdFee, bool test_accept = false) | const Amount nAbsurdFee, bool test_accept = false) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | EXCLUSIVE_LOCKS_REQUIRED(cs_main); | ||||
/** Convert CValidationState to a human-readable message for logging */ | /** Convert CValidationState to a human-readable message for logging */ | ||||
std::string FormatStateMessage(const CValidationState &state); | std::string FormatStateMessage(const CValidationState &state); | ||||
/** | /** | ||||
* Simple class for regulating resource usage during CheckInputs (and | |||||
* CScriptCheck), using atomics so as to be compatible with parallel validation. | |||||
*/ | |||||
class CheckInputsLimiter { | |||||
std::atomic<int_fast64_t> remaining; | |||||
deadalnix: Just use `int64_t` . We don't support any machine for which `int_fast64_t` is needed and I'd… | |||||
public: | |||||
CheckInputsLimiter(int_fast64_t limit) : remaining(limit) {} | |||||
bool consume_and_check(int consumed) { | |||||
auto oldvalue = | |||||
remaining.fetch_sub(consumed, std::memory_order_relaxed); | |||||
deadalnixUnsubmitted Not Done Inline ActionsIf you care about the result, then relaxed is not what you want. I would strongly suggest you just leave the default unless you have a benchmark showing this is winning something, because getting this wrong is both easy and hard to debug. deadalnix: If you care about the result, then relaxed is not what you want. I would strongly suggest you… | |||||
markblundebergAuthorUnsubmitted Done Inline ActionsI do only need relaxed here because I merely want an atomic RMW operation on this counter, without synchronizing, i.e., without enforcing memory ordering on other loads/stores. (the meaning of the counter is not representing something about the state of memory) And as far as I understand, whether one uses the result or not is orthogonal to the question of memory ordering. That said, aside from possible caller inlining optimizations it's no difference on x86, only changes things on ARMv8. https://godbolt.org/z/nmBv9T ... and it seems anyway the compilers don't even take advantage of the possible optimizations. Anyway, I'm happy to leave it as seq_cst for the paranoia reason. markblundeberg: I do only need relaxed here because I merely want an atomic RMW operation on this counter… | |||||
deadalnixUnsubmitted Not Done Inline ActionsIf there is no ordering, then there is no guarantee you ever see the counter going bellow the value you are checking for because decrements can appear to be done in different orders on different threads, none of them being the last one in its own world view. If you want to understand some of the details of this, then I'll refer you to Herb's atomic weapon talk, especially the part about smart pointer - and how almost everybody in the audience get it wrong. But also take a step back. The fact that we are having this discussion show that there is a cost to this, and that absolutely no benefit has been demonstrated with a benchmark. This is just the wrong tradeof. The reality is that you could indeed get away with acq_rel consistency rather than sequential, but even that is the wrong tradeof, because there are literally zero measurable upside at this time. deadalnix: If there is no ordering, then there is no guarantee you ever see the counter going bellow the… | |||||
markblundebergAuthorUnsubmitted Done Inline ActionsI've seen those Herb Sutter talks, they're great. :-) Yes with smart pointers there is a need to synchronize other memory, my point is that this is not anything like a smart pointer. There is no other memory location to worry about and all we need is 1) atomicity of the RMW, and 2) modification order consistency of this one location. I.e., we only need that the update is not torn and that there is an objective ordering to this one memory location (and other memory may be totally desynchronized, doesn't matter). That's exactly what relaxed ordering gives us, and from what I understand this kind of thing is the classic use case for it. https://en.cppreference.com/w/cpp/atomic/memory_order
In other words: if you have a 0-initialized variable and fetch_add(1) in three threads, it is guaranteed that one thread sees 0 and one thread sees 1 and one thread sees 2; and the final value they all see (after eventually synchronizing later on) is 3. All of this is not influenced by memory ordering options, and how could it be, since this program only has one memory location. markblundeberg: I've seen those Herb Sutter talks, they're great. :-) Yes with smart pointers there is a need… | |||||
return oldvalue >= consumed; | |||||
} | |||||
bool check() { return remaining.load(std::memory_order_relaxed) >= 0; } | |||||
deadalnixUnsubmitted Not Done Inline ActionsRelaxed is not appropriate here. Relaxed load rarely ever make sense. deadalnix: Relaxed is not appropriate here. Relaxed load rarely ever make sense. | |||||
markblundebergAuthorUnsubmitted Done Inline ActionsLikewise this just needs to be a simple load without a data race -- nothing is being synchronized. markblundeberg: Likewise this just needs to be a simple load without a data race -- nothing is being… | |||||
}; | |||||
/** | |||||
* Check whether all inputs of this transaction are valid (no double spends, | * Check whether all inputs of this transaction are valid (no double spends, | ||||
* scripts & sigs, amounts). This does not modify the UTXO set. | * scripts & sigs, amounts). This does not modify the UTXO set. | ||||
* | * | ||||
* If pvChecks is not nullptr, script checks are pushed onto it instead of being | * If pvChecks is not nullptr, script checks are pushed onto it instead of being | ||||
* performed inline. Any script checks which are not necessary (eg due to script | * performed inline. Any script checks which are not necessary (eg due to script | ||||
* execution cache hits) are, obviously, not pushed onto pvChecks/run. | * execution cache hits) are, obviously, not pushed onto pvChecks/run. | ||||
* | * | ||||
* Upon success nSigChecksOut will be filled in with either: | * Upon success nSigChecksOut will be filled in with either: | ||||
* - correct total for all inputs, or, | * - correct total for all inputs, or, | ||||
* - 0, in the case when checks were pushed onto pvChecks (i.e., a cache miss | * - 0, in the case when checks were pushed onto pvChecks (i.e., a cache miss | ||||
* with pvChecks non-null), in which case the total can be found by executing | * with pvChecks non-null), in which case the total can be found by executing | ||||
* pvChecks and adding the results. | * pvChecks and adding the results. | ||||
* | * | ||||
* Setting sigCacheStore/scriptCacheStore to false will remove elements from the | * Setting sigCacheStore/scriptCacheStore to false will remove elements from the | ||||
* corresponding cache which are matched. This is useful for checking blocks | * corresponding cache which are matched. This is useful for checking blocks | ||||
* where we will likely never need the cache entry again. | * where we will likely never need the cache entry again. | ||||
* | |||||
* pLimitSigChecks can be passed to limit the sigchecks count either in parallel | |||||
* or serial validation. With pvChecks null (serial validation), breaking the | |||||
* pLimitSigChecks limit will abort evaluation early and return false. With | |||||
* pvChecks not-null (parallel validation): the cached nSigChecks may itself | |||||
* break the limit in which case false is returned, OR, each entry in the | |||||
* returned pvChecks must be executed exactly once in order to probe the limit | |||||
* accurately. | |||||
*/ | */ | ||||
bool CheckInputs(const CTransaction &tx, CValidationState &state, | bool CheckInputs(const CTransaction &tx, CValidationState &state, | ||||
const CCoinsViewCache &view, bool fScriptChecks, | const CCoinsViewCache &view, bool fScriptChecks, | ||||
const uint32_t flags, bool sigCacheStore, | const uint32_t flags, bool sigCacheStore, | ||||
bool scriptCacheStore, | bool scriptCacheStore, | ||||
const PrecomputedTransactionData &txdata, int &nSigChecksOut, | const PrecomputedTransactionData &txdata, int &nSigChecksOut, | ||||
std::vector<CScriptCheck> *pvChecks = nullptr) | std::vector<CScriptCheck> *pvChecks = nullptr, | ||||
CheckInputsLimiter *pLimitSigChecks = nullptr) | |||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | EXCLUSIVE_LOCKS_REQUIRED(cs_main); | ||||
/** | /** | ||||
* Mark all the coins corresponding to a given transaction inputs as spent. | * Mark all the coins corresponding to a given transaction inputs as spent. | ||||
*/ | */ | ||||
void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, | void SpendCoins(CCoinsViewCache &view, const CTransaction &tx, CTxUndo &txundo, | ||||
int nHeight); | int nHeight); | ||||
Show All 25 Lines | |||||
bool CheckSequenceLocks(const CTxMemPool &pool, const CTransaction &tx, | bool CheckSequenceLocks(const CTxMemPool &pool, const CTransaction &tx, | ||||
int flags, LockPoints *lp = nullptr, | int flags, LockPoints *lp = nullptr, | ||||
bool useExistingLockPoints = false) | bool useExistingLockPoints = false) | ||||
EXCLUSIVE_LOCKS_REQUIRED(cs_main); | EXCLUSIVE_LOCKS_REQUIRED(cs_main); | ||||
/** | /** | ||||
* Closure representing one script verification. | * Closure representing one script verification. | ||||
* Note that this stores references to the spending transaction. | * Note that this stores references to the spending transaction. | ||||
* | |||||
* Note that if pLimitSigChecks is passed, then failure does not imply that | |||||
* scripts have failed. | |||||
*/ | */ | ||||
class CScriptCheck { | class CScriptCheck { | ||||
private: | private: | ||||
CScript scriptPubKey; | CScript scriptPubKey; | ||||
Amount amount; | Amount amount; | ||||
const CTransaction *ptxTo; | const CTransaction *ptxTo; | ||||
unsigned int nIn; | unsigned int nIn; | ||||
uint32_t nFlags; | uint32_t nFlags; | ||||
bool cacheStore; | bool cacheStore; | ||||
ScriptError error; | ScriptError error; | ||||
ScriptExecutionMetrics metrics; | ScriptExecutionMetrics metrics; | ||||
PrecomputedTransactionData txdata; | PrecomputedTransactionData txdata; | ||||
CheckInputsLimiter *pLimitSigChecks; | |||||
public: | public: | ||||
CScriptCheck() | CScriptCheck() | ||||
: amount(), ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), | : amount(), ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), | ||||
error(ScriptError::UNKNOWN), txdata() {} | error(ScriptError::UNKNOWN), txdata(), pLimitSigChecks(nullptr) {} | ||||
CScriptCheck(const CScript &scriptPubKeyIn, const Amount amountIn, | CScriptCheck(const CScript &scriptPubKeyIn, const Amount amountIn, | ||||
const CTransaction &txToIn, unsigned int nInIn, | const CTransaction &txToIn, unsigned int nInIn, | ||||
uint32_t nFlagsIn, bool cacheIn, | uint32_t nFlagsIn, bool cacheIn, | ||||
const PrecomputedTransactionData &txdataIn) | const PrecomputedTransactionData &txdataIn, | ||||
CheckInputsLimiter *pLimitSigChecksIn = nullptr) | |||||
: scriptPubKey(scriptPubKeyIn), amount(amountIn), ptxTo(&txToIn), | : scriptPubKey(scriptPubKeyIn), amount(amountIn), ptxTo(&txToIn), | ||||
nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), | nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), | ||||
error(ScriptError::UNKNOWN), txdata(txdataIn) {} | error(ScriptError::UNKNOWN), txdata(txdataIn), | ||||
pLimitSigChecks(pLimitSigChecksIn) {} | |||||
bool operator()(); | bool operator()(); | ||||
void swap(CScriptCheck &check) { | void swap(CScriptCheck &check) { | ||||
scriptPubKey.swap(check.scriptPubKey); | scriptPubKey.swap(check.scriptPubKey); | ||||
std::swap(ptxTo, check.ptxTo); | std::swap(ptxTo, check.ptxTo); | ||||
std::swap(amount, check.amount); | std::swap(amount, check.amount); | ||||
std::swap(nIn, check.nIn); | std::swap(nIn, check.nIn); | ||||
std::swap(nFlags, check.nFlags); | std::swap(nFlags, check.nFlags); | ||||
std::swap(cacheStore, check.cacheStore); | std::swap(cacheStore, check.cacheStore); | ||||
std::swap(error, check.error); | std::swap(error, check.error); | ||||
std::swap(txdata, check.txdata); | std::swap(txdata, check.txdata); | ||||
std::swap(pLimitSigChecks, check.pLimitSigChecks); | |||||
} | } | ||||
ScriptError GetScriptError() const { return error; } | ScriptError GetScriptError() const { return error; } | ||||
ScriptExecutionMetrics GetScriptExecutionMetrics() const { return metrics; } | ScriptExecutionMetrics GetScriptExecutionMetrics() const { return metrics; } | ||||
}; | }; | ||||
/** Functions for disk access for blocks */ | /** Functions for disk access for blocks */ | ||||
▲ Show 20 Lines • Show All 159 Lines • Show Last 20 Lines |
Just use int64_t . We don't support any machine for which int_fast64_t is needed and I'd rather have thing always the same on all architectures regardless of perf in the consensus layer anyways. If an architecture doesn't do 64 bits atomic well, then fuck them.