Line data Source code
1 : // Copyright (c) 2018-2025 The Dash Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <evo/specialtxman.h>
6 :
7 : #include <chainlock/chainlock.h>
8 : #include <chainlock/clsig.h>
9 : #include <chainlock/handler.h>
10 : #include <evo/assetlocktx.h>
11 : #include <evo/cbtx.h>
12 : #include <evo/creditpool.h>
13 : #include <evo/deterministicmns.h>
14 : #include <evo/mnhftx.h>
15 : #include <evo/netinfo.h>
16 : #include <evo/simplifiedmns.h>
17 : #include <llmq/blockprocessor.h>
18 : #include <llmq/commitment.h>
19 : #include <llmq/quorumsman.h>
20 : #include <llmq/utils.h>
21 : #include <messagesigner.h>
22 : #include <util/helpers.h>
23 :
24 : #include <chainparams.h>
25 : #include <consensus/amount.h>
26 : #include <consensus/validation.h>
27 : #include <deploymentstatus.h>
28 : #include <hash.h>
29 : #include <primitives/block.h>
30 : #include <util/system.h>
31 : #include <validation.h>
32 :
33 167509 : static bool CheckCbTxBestChainlock(const CCbTx& cbTx, const CBlockIndex* pindex, const Consensus::Params& consensus_params,
34 : const CChain& chain, const llmq::CQuorumManager& qman,
35 : const chainlock::Chainlocks& chainlocks, BlockValidationState& state)
36 : {
37 167509 : if (cbTx.nVersion < CCbTx::Version::CLSIG_AND_BALANCE) {
38 56027 : return true;
39 : }
40 :
41 111482 : static Mutex cached_mutex;
42 : static const CBlockIndex* cached_pindex GUARDED_BY(cached_mutex){nullptr};
43 111482 : static std::optional<std::pair<CBLSSignature, uint32_t>> cached_chainlock GUARDED_BY(cached_mutex){std::nullopt};
44 :
45 111482 : auto best_clsig = chainlocks.GetBestChainLock();
46 122124 : if (best_clsig.getHeight() == pindex->nHeight - 1 && cbTx.bestCLHeightDiff == 0 &&
47 10642 : cbTx.bestCLSignature == best_clsig.getSig()) {
48 : // matches our best clsig which still hold values for the previous block
49 10562 : LOCK(cached_mutex);
50 10562 : cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
51 10562 : cached_pindex = pindex;
52 10562 : return true;
53 10562 : }
54 :
55 100920 : std::optional<std::pair<CBLSSignature, uint32_t>> prevBlockCoinbaseChainlock{std::nullopt};
56 125129 : if (LOCK(cached_mutex); cached_pindex == pindex->pprev) {
57 24209 : prevBlockCoinbaseChainlock = cached_chainlock;
58 24209 : }
59 100920 : if (!prevBlockCoinbaseChainlock.has_value()) {
60 76711 : prevBlockCoinbaseChainlock = GetNonNullCoinbaseChainlock(pindex->pprev);
61 76711 : }
62 : // If std::optional prevBlockCoinbaseChainlock is empty, then up to the previous block, coinbase Chainlock is null.
63 100920 : if (prevBlockCoinbaseChainlock.has_value()) {
64 : // Previous block Coinbase has a non-null Chainlock: current block's Chainlock must be non-null and at least as new as the previous one
65 27401 : if (!cbTx.bestCLSignature.IsValid()) {
66 : // IsNull() doesn't exist for CBLSSignature: we assume that a non valid BLS sig is null
67 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-null-clsig");
68 : }
69 27401 : if (cbTx.bestCLHeightDiff > prevBlockCoinbaseChainlock.value().second + 1) {
70 6 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-older-clsig");
71 : }
72 27395 : }
73 :
74 : // IsNull() doesn't exist for CBLSSignature: we assume that a valid BLS sig is non-null
75 100914 : if (cbTx.bestCLSignature.IsValid()) {
76 27446 : int curBlockCoinbaseCLHeight = pindex->nHeight - static_cast<int>(cbTx.bestCLHeightDiff) - 1;
77 27446 : if (best_clsig.getHeight() == curBlockCoinbaseCLHeight && best_clsig.getSig() == cbTx.bestCLSignature) {
78 : // matches our best (but outdated) clsig, no need to verify it again
79 22906 : LOCK(cached_mutex);
80 22906 : cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
81 22906 : cached_pindex = pindex;
82 22906 : return true;
83 22906 : }
84 4540 : uint256 curBlockCoinbaseCLBlockHash = pindex->GetAncestor(curBlockCoinbaseCLHeight)->GetBlockHash();
85 4540 : chainlock::ChainLockSig clsig{curBlockCoinbaseCLHeight, curBlockCoinbaseCLBlockHash, cbTx.bestCLSignature};
86 4540 : llmq::VerifyRecSigStatus ret = chainlock::VerifyChainLock(consensus_params, chain, qman, clsig);
87 4540 : if (ret != llmq::VerifyRecSigStatus::Valid) {
88 3 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid-clsig");
89 : }
90 4537 : LOCK(cached_mutex);
91 4537 : cached_chainlock = std::make_pair(cbTx.bestCLSignature, cbTx.bestCLHeightDiff);
92 4537 : cached_pindex = pindex;
93 78008 : } else if (cbTx.bestCLHeightDiff != 0) {
94 : // Null bestCLSignature is allowed only with bestCLHeightDiff = 0
95 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-cldiff");
96 : }
97 :
98 78005 : return true;
99 167509 : }
100 :
101 584795 : static bool CheckSpecialTxInner(CDeterministicMNManager& dmnman, llmq::CQuorumSnapshotManager& qsnapman,
102 : const ChainstateManager& chainman, const llmq::CQuorumManager& qman,
103 : const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache& view,
104 : const std::optional<CRangesSet>& indexes, bool check_sigs, TxValidationState& state)
105 : EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
106 : {
107 584795 : AssertLockHeld(::cs_main);
108 :
109 584795 : if (!tx.HasExtraPayloadField())
110 339309 : return true;
111 :
112 245486 : if (!DeploymentActiveAfter(pindexPrev, chainman.GetConsensus(), Consensus::DEPLOYMENT_DIP0003)) {
113 1 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type-dip3-inactive");
114 : }
115 :
116 : try {
117 245485 : switch (tx.nType) {
118 : case TRANSACTION_PROVIDER_REGISTER:
119 3831 : return CheckProRegTx(tx, pindexPrev, dmnman, view, chainman, state, check_sigs);
120 : case TRANSACTION_PROVIDER_UPDATE_SERVICE:
121 3786 : return CheckProUpServTx(tx, pindexPrev, dmnman, chainman, state, check_sigs);
122 : case TRANSACTION_PROVIDER_UPDATE_REGISTRAR:
123 139 : return CheckProUpRegTx(tx, pindexPrev, dmnman, view, chainman, state, check_sigs);
124 : case TRANSACTION_PROVIDER_UPDATE_REVOKE:
125 97 : return CheckProUpRevTx(tx, pindexPrev, dmnman, chainman, state, check_sigs);
126 : case TRANSACTION_COINBASE: {
127 0 : if (!tx.IsCoinBase()) {
128 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-invalid");
129 : }
130 0 : if (const auto opt_cbTx = GetTxPayload<CCbTx>(tx)) {
131 0 : return CheckCbTx(*opt_cbTx, pindexPrev, state);
132 : } else {
133 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-payload");
134 : }
135 : }
136 : case TRANSACTION_QUORUM_COMMITMENT:
137 233399 : return llmq::CheckLLMQCommitment({dmnman, qsnapman, chainman, pindexPrev}, tx, state);
138 : case TRANSACTION_MNHF_SIGNAL:
139 733 : return CheckMNHFTx(chainman, qman, tx, pindexPrev, state);
140 : case TRANSACTION_ASSET_LOCK:
141 2992 : return CheckAssetLockTx(tx, state);
142 : case TRANSACTION_ASSET_UNLOCK:
143 508 : return CheckAssetUnlockTx(chainman.m_blockman, qman, tx, pindexPrev, indexes, state);
144 : }
145 0 : } catch (const std::exception& e) {
146 0 : LogPrintf("%s -- failed: %s\n", __func__, e.what());
147 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "failed-check-special-tx");
148 0 : }
149 :
150 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-tx-type-check");
151 584795 : }
152 :
153 46947 : bool CSpecialTxProcessor::CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, const CCoinsViewCache& view, bool check_sigs, TxValidationState& state)
154 : {
155 46947 : AssertLockHeld(::cs_main);
156 93894 : return CheckSpecialTxInner(m_dmnman, m_qsnapman, m_chainman, m_qman, tx, pindexPrev, view, std::nullopt, check_sigs,
157 46947 : state);
158 0 : }
159 :
160 5851 : static void HandleQuorumCommitment(const llmq::CFinalCommitment& qc, const std::vector<CDeterministicMNCPtr>& members,
161 : bool debugLogs, CDeterministicMNList& mnList)
162 : {
163 26879 : for (size_t i = 0; i < members.size(); i++) {
164 21028 : if (!mnList.HasMN(members[i]->proTxHash)) {
165 0 : continue;
166 : }
167 21028 : if (!qc.validMembers[i]) {
168 : // punish MN for failed DKG participation
169 : // The idea is to immediately ban a MN when it fails 2 DKG sessions with only a few blocks in-between
170 : // If there were enough blocks between failures, the MN has a chance to recover as he reduces his penalty by 1 for every block
171 : // If it however fails 3 times in the timespan of a single payment cycle, it should definitely get banned
172 354 : mnList.PoSePunish(members[i]->proTxHash, mnList.CalcPenalty(66), debugLogs);
173 354 : }
174 21028 : }
175 5851 : }
176 :
177 214461 : bool CSpecialTxProcessor::BuildNewListFromBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindexPrev,
178 : const CCoinsViewCache& view, bool debugLogs,
179 : BlockValidationState& state, CDeterministicMNList& mnListRet)
180 : {
181 214461 : AssertLockHeld(cs_main);
182 214461 : CDeterministicMNList oldList = m_dmnman.GetListForBlock(pindexPrev);
183 214461 : return RebuildListFromBlock(block, pindexPrev, oldList, view, debugLogs, state, mnListRet);
184 214461 : }
185 :
186 214461 : bool CSpecialTxProcessor::RebuildListFromBlock(const CBlock& block, gsl::not_null<const CBlockIndex*> pindexPrev,
187 : const CDeterministicMNList& prevList, const CCoinsViewCache& view,
188 : bool debugLogs, BlockValidationState& state,
189 : CDeterministicMNList& mnListRet)
190 : {
191 : // Verify that prevList either represents an empty/initial state (default-constructed),
192 : // or it matches the previous block's hash.
193 214461 : assert(prevList == CDeterministicMNList() || prevList.GetBlockHash() == pindexPrev->GetBlockHash());
194 :
195 214461 : int nHeight = pindexPrev->nHeight + 1;
196 :
197 214461 : CDeterministicMNList newList = prevList;
198 214461 : newList.SetBlockHash(uint256()); // we can't know the final block hash, so better not return a (invalid) block hash
199 214461 : newList.SetHeight(nHeight);
200 :
201 214461 : auto payee = prevList.GetMNPayee(pindexPrev);
202 :
203 : // we iterate the prevList here and update the newList
204 : // this is only valid as long these have not diverged at this point, which is the case as long as we don't add
205 : // code above this loop that modifies newList
206 998504 : prevList.ForEachMN(/*onlyValid=*/false, [&pindexPrev, &newList, this](const auto& dmn) {
207 784043 : if (!dmn.pdmnState->confirmedHash.IsNull()) {
208 : // already confirmed
209 777337 : return;
210 : }
211 : // this works on the previous block, so confirmation will happen one block after nMasternodeMinimumConfirmations
212 : // has been reached, but the block hash will then point to the block at nMasternodeMinimumConfirmations
213 6706 : int nConfirmations = pindexPrev->nHeight - dmn.pdmnState->nRegisteredHeight;
214 6706 : if (nConfirmations >= this->m_consensus_params.nMasternodeMinimumConfirmations) {
215 3427 : auto newState = std::make_shared<CDeterministicMNState>(*dmn.pdmnState);
216 3427 : newState->UpdateConfirmedHash(dmn.proTxHash, pindexPrev->GetBlockHash());
217 3427 : newList.UpdateMN(dmn.proTxHash, newState);
218 3427 : }
219 784043 : });
220 :
221 214461 : newList.DecreaseScores();
222 :
223 214461 : const bool isMNRewardReallocation{
224 214461 : DeploymentActiveAfter(pindexPrev, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_MN_RR)};
225 214461 : const bool is_v24_deployed{DeploymentActiveAfter(pindexPrev, m_chainman, Consensus::DEPLOYMENT_V24)};
226 :
227 : // we skip the coinbase
228 533772 : for (int i = 1; i < (int)block.vtx.size(); i++) {
229 319311 : const CTransaction& tx = *block.vtx[i];
230 :
231 319311 : if (!tx.IsSpecialTxVersion()) {
232 : // only interested in special TXs
233 6895 : continue;
234 : }
235 :
236 312416 : if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
237 2789 : const auto opt_proTx = GetTxPayload<CProRegTx>(tx);
238 2789 : if (!opt_proTx) {
239 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
240 : }
241 2789 : auto& proTx = *opt_proTx;
242 :
243 2789 : auto dmn = std::make_shared<CDeterministicMN>(newList.GetTotalRegisteredCount(), proTx.nType);
244 2789 : dmn->proTxHash = tx.GetHash();
245 :
246 : // collateralOutpoint is either pointing to an external collateral or to the ProRegTx itself
247 2789 : if (proTx.collateralOutpoint.hash.IsNull()) {
248 1076 : dmn->collateralOutpoint = COutPoint(tx.GetHash(), proTx.collateralOutpoint.n);
249 1076 : } else {
250 1713 : dmn->collateralOutpoint = proTx.collateralOutpoint;
251 : }
252 :
253 : // Complain about spent collaterals only when we process the tip.
254 : // This is safe because blocks below the tip were verified
255 : // when they were connected initially.
256 2789 : if (!view.GetBestBlock().IsNull()) {
257 2789 : Coin coin;
258 2789 : CAmount expectedCollateral = GetMnType(proTx.nType).collat_amount;
259 4502 : if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) ||
260 1713 : coin.IsSpent() || coin.out.nValue != expectedCollateral)) {
261 : // should actually never get to this point as CheckProRegTx should have handled this case.
262 : // We do this additional check nevertheless to be 100% sure
263 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-collateral");
264 : }
265 2789 : }
266 :
267 2789 : auto replacedDmn = newList.GetMNByCollateral(dmn->collateralOutpoint);
268 2789 : if (replacedDmn != nullptr) {
269 : // This might only happen with a ProRegTx that refers an external collateral
270 : // In that case the new ProRegTx will replace the old one. This means the old one is removed
271 : // and the new one is added like a completely fresh one, which is also at the bottom of the payment list
272 300 : newList.RemoveMN(replacedDmn->proTxHash);
273 300 : if (debugLogs) {
274 300 : LogPrintf("%s -- MN %s removed from list because collateral was used for " /* Continued */
275 : "a new ProRegTx. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
276 : __func__, replacedDmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(),
277 : nHeight, newList.GetCounts().total());
278 300 : }
279 300 : }
280 :
281 5588 : for (const auto& entry : proTx.netInfo->GetEntries()) {
282 5598 : if (const auto service_opt{entry.GetAddrPort()}) {
283 2799 : if (newList.HasUniqueProperty(*service_opt)) {
284 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
285 : }
286 2799 : } else if (const auto domain_opt{entry.GetDomainPort()}) {
287 0 : if (newList.HasUniqueProperty(*domain_opt)) {
288 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
289 : }
290 0 : } else {
291 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-netinfo-entry");
292 : }
293 : }
294 2789 : if (newList.HasUniqueProperty(proTx.keyIDOwner) || newList.HasUniqueProperty(proTx.pubKeyOperator)) {
295 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-key");
296 : }
297 :
298 2789 : dmn->nOperatorReward = proTx.nOperatorReward;
299 :
300 2789 : auto dmnState = std::make_shared<CDeterministicMNState>(proTx);
301 2789 : dmnState->nRegisteredHeight = nHeight;
302 2789 : if (proTx.netInfo->IsEmpty()) {
303 : // start in banned pdmnState as we need to wait for a ProUpServTx
304 40 : dmnState->BanIfNotBanned(nHeight);
305 40 : }
306 2789 : dmn->pdmnState = dmnState;
307 :
308 2789 : newList.AddMN(dmn);
309 :
310 2789 : if (debugLogs) {
311 2789 : LogPrintf("%s -- MN %s added at height %d: %s\n", __func__, tx.GetHash().ToString(), nHeight,
312 : proTx.ToString());
313 2789 : }
314 312416 : } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
315 2897 : const auto opt_proTx = GetTxPayload<CProUpServTx>(tx);
316 2897 : if (!opt_proTx) {
317 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
318 : }
319 :
320 5934 : for (const auto& entry : opt_proTx->netInfo->GetEntries()) {
321 6074 : if (const auto service_opt{entry.GetAddrPort()}) {
322 4423 : if (newList.HasUniqueProperty(*service_opt) &&
323 1406 : newList.GetUniquePropertyMN(*service_opt)->proTxHash != opt_proTx->proTxHash) {
324 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
325 : }
326 3057 : } else if (const auto domain_opt{entry.GetDomainPort()}) {
327 20 : if (newList.HasUniqueProperty(*domain_opt) &&
328 0 : newList.GetUniquePropertyMN(*domain_opt)->proTxHash != opt_proTx->proTxHash) {
329 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-dup-netinfo-entry");
330 : }
331 20 : } else {
332 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-netinfo-entry");
333 : }
334 : }
335 :
336 2897 : auto dmn = newList.GetMN(opt_proTx->proTxHash);
337 2897 : if (!dmn) {
338 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash");
339 : }
340 2897 : if (opt_proTx->nType != dmn->nType) {
341 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-type-mismatch");
342 : }
343 2897 : if (!IsValidMnType(opt_proTx->nType)) {
344 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-type");
345 : }
346 :
347 2897 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
348 2897 : if (is_v24_deployed) {
349 : // Extended addresses support in v24 means that the version can be updated
350 30 : newState->nVersion = opt_proTx->nVersion;
351 30 : }
352 2897 : newState->netInfo = opt_proTx->netInfo;
353 2897 : newState->scriptOperatorPayout = opt_proTx->scriptOperatorPayout;
354 2897 : if (opt_proTx->nType == MnType::Evo) {
355 162 : newState->platformNodeID = opt_proTx->platformNodeID;
356 162 : if (opt_proTx->nVersion < ProTxVersion::ExtAddr) {
357 132 : newState->platformP2PPort = opt_proTx->platformP2PPort;
358 132 : newState->platformHTTPPort = opt_proTx->platformHTTPPort;
359 132 : } else {
360 : // From ExtAddr onwards the Platform ports are stored in netInfo. Clear the
361 : // legacy scalar fields (which a legacy registration may have left set) so the
362 : // in-memory state matches its serialized form, which omits them for ExtAddr
363 : // (see CDeterministicMNState serialization). Otherwise a stale value would
364 : // survive in diff-reconstructed lists but vanish through a snapshot round-trip.
365 30 : newState->platformP2PPort = 0;
366 30 : newState->platformHTTPPort = 0;
367 : }
368 162 : }
369 2897 : if (newState->IsBanned()) {
370 : // only revive when all keys are set
371 244 : if (newState->pubKeyOperator != CBLSLazyPublicKey() && !newState->keyIDVoting.IsNull() &&
372 122 : !newState->keyIDOwner.IsNull()) {
373 122 : newState->Revive(nHeight);
374 122 : if (debugLogs) {
375 122 : LogPrintf("%s -- MN %s revived at height %d\n", __func__, opt_proTx->proTxHash.ToString(), nHeight);
376 122 : }
377 122 : }
378 122 : }
379 :
380 2897 : newList.UpdateMN(opt_proTx->proTxHash, newState);
381 2897 : if (debugLogs) {
382 2897 : LogPrintf("%s -- MN %s updated at height %d: %s\n", __func__, opt_proTx->proTxHash.ToString(), nHeight,
383 : opt_proTx->ToString());
384 2897 : }
385 309627 : } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
386 126 : const auto opt_proTx = GetTxPayload<CProUpRegTx>(tx);
387 126 : if (!opt_proTx) {
388 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
389 : }
390 :
391 126 : auto dmn = newList.GetMN(opt_proTx->proTxHash);
392 126 : if (!dmn) {
393 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash");
394 : }
395 126 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
396 126 : if (newState->pubKeyOperator != opt_proTx->pubKeyOperator) {
397 : // reset all operator related fields and put MN into PoSe-banned state in case the operator key changes
398 6 : newState->ResetOperatorFields();
399 6 : newState->BanIfNotBanned(nHeight);
400 : // we update pubKeyOperator here, make sure state version matches
401 : // Make sure we don't accidentally downgrade the state version if using version after basic BLS
402 6 : newState->nVersion = newState->nVersion > ProTxVersion::BasicBLS ? newState->nVersion : opt_proTx->nVersion;
403 6 : newState->netInfo = NetInfoInterface::MakeNetInfo(newState->nVersion);
404 6 : newState->pubKeyOperator = opt_proTx->pubKeyOperator;
405 6 : }
406 126 : newState->keyIDVoting = opt_proTx->keyIDVoting;
407 126 : newState->scriptPayout = opt_proTx->scriptPayout;
408 :
409 126 : newList.UpdateMN(opt_proTx->proTxHash, newState);
410 :
411 126 : if (debugLogs) {
412 126 : LogPrintf("%s -- MN %s updated at height %d: %s\n", __func__, opt_proTx->proTxHash.ToString(), nHeight,
413 : opt_proTx->ToString());
414 126 : }
415 306730 : } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) {
416 73 : const auto opt_proTx = GetTxPayload<CProUpRevTx>(tx);
417 73 : if (!opt_proTx) {
418 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-payload");
419 : }
420 :
421 73 : auto dmn = newList.GetMN(opt_proTx->proTxHash);
422 73 : if (!dmn) {
423 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash");
424 : }
425 73 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
426 73 : newState->ResetOperatorFields();
427 73 : newState->BanIfNotBanned(nHeight);
428 73 : newState->nRevocationReason = opt_proTx->nReason;
429 :
430 73 : newList.UpdateMN(opt_proTx->proTxHash, newState);
431 :
432 73 : if (debugLogs) {
433 73 : LogPrintf("%s -- MN %s revoked operator key at height %d: %s\n", __func__,
434 : opt_proTx->proTxHash.ToString(), nHeight, opt_proTx->ToString());
435 73 : }
436 306604 : } else if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) {
437 303858 : const auto opt_qc = GetTxPayload<llmq::CFinalCommitmentTxPayload>(tx);
438 303858 : if (!opt_qc) {
439 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-payload");
440 : }
441 303858 : if (!opt_qc->commitment.IsNull()) {
442 5851 : const auto& llmq_params_opt = Params().GetLLMQ(opt_qc->commitment.llmqType);
443 5851 : if (!llmq_params_opt.has_value()) {
444 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-commitment-type");
445 : }
446 5851 : int qcnHeight = int(opt_qc->nHeight);
447 11702 : int quorumHeight = qcnHeight - (qcnHeight % llmq_params_opt->dkgInterval) +
448 5851 : int(opt_qc->commitment.quorumIndex);
449 5851 : auto pQuorumBaseBlockIndex = pindexPrev->GetAncestor(quorumHeight);
450 5851 : if (!pQuorumBaseBlockIndex || pQuorumBaseBlockIndex->GetBlockHash() != opt_qc->commitment.quorumHash) {
451 : // we should actually never get into this case as validation should have caught it...but let's be sure
452 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-qc-quorum-hash");
453 : }
454 :
455 : // The commitment has already been validated at this point, so it's safe to use members of it
456 :
457 5851 : const auto members = llmq::utils::GetAllQuorumMembers(opt_qc->commitment.llmqType,
458 5851 : {m_dmnman, m_qsnapman, m_chainman,
459 5851 : pQuorumBaseBlockIndex});
460 5851 : HandleQuorumCommitment(opt_qc->commitment, members, debugLogs, newList);
461 5851 : }
462 303858 : }
463 312416 : }
464 :
465 : // we skip the coinbase
466 533772 : for (int i = 1; i < (int)block.vtx.size(); i++) {
467 319311 : const CTransaction& tx = *block.vtx[i];
468 :
469 : // check if any existing MN collateral is spent by this transaction
470 348499 : for (const auto& in : tx.vin) {
471 29188 : auto dmn = newList.GetMNByCollateral(in.prevout);
472 29188 : if (dmn && dmn->collateralOutpoint == in.prevout) {
473 261 : newList.RemoveMN(dmn->proTxHash);
474 :
475 261 : if (debugLogs) {
476 261 : LogPrintf("%s -- MN %s removed from list because collateral was spent. " /* Continued */
477 : "collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n",
478 : __func__, dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight,
479 : newList.GetCounts().total());
480 261 : }
481 261 : }
482 29188 : }
483 319311 : }
484 :
485 : // The payee for the current block was determined by the previous block's list, but it might have disappeared in the
486 : // current block. We still pay that MN one last time, however.
487 348930 : if (auto dmn = payee ? newList.GetMN(payee->proTxHash) : nullptr) {
488 134469 : auto newState = std::make_shared<CDeterministicMNState>(*dmn->pdmnState);
489 134469 : newState->nLastPaidHeight = nHeight;
490 : // Starting from v19 and until MNRewardReallocation, EvoNodes will be paid 4 blocks in a row
491 : // No need to check if v19 is active, since EvoNode ProRegTxes are allowed only after v19 activation
492 : // Note: If the payee wasn't found in the current block that's fine
493 134469 : if (dmn->nType == MnType::Evo && !isMNRewardReallocation) {
494 11689 : ++newState->nConsecutivePayments;
495 11689 : if (debugLogs) {
496 11689 : LogPrint(BCLog::MNPAYMENTS, "%s -- MN %s is an EvoNode, bumping nConsecutivePayments to %d\n", __func__,
497 : dmn->proTxHash.ToString(), newState->nConsecutivePayments);
498 11689 : }
499 11689 : }
500 134469 : newList.UpdateMN(payee->proTxHash, newState);
501 134469 : }
502 :
503 : // reset nConsecutivePayments on non-paid EvoNodes
504 214461 : auto newList2 = newList;
505 1000732 : newList2.ForEachMN(/*onlyValid=*/false, [&](const auto& dmn) {
506 786271 : if (dmn.nType != MnType::Evo) return;
507 66094 : if (payee != nullptr && dmn.proTxHash == payee->proTxHash && !isMNRewardReallocation) return;
508 54405 : if (dmn.pdmnState->nConsecutivePayments == 0) return;
509 2937 : if (debugLogs) {
510 2937 : LogPrint(BCLog::MNPAYMENTS, "%s -- MN %s, reset nConsecutivePayments %d->0\n", __func__,
511 : dmn.proTxHash.ToString(), dmn.pdmnState->nConsecutivePayments);
512 2937 : }
513 2937 : auto newState = std::make_shared<CDeterministicMNState>(*dmn.pdmnState);
514 2937 : newState->nConsecutivePayments = 0;
515 2937 : newList.UpdateMN(dmn.proTxHash, newState);
516 786271 : });
517 :
518 214461 : mnListRet = newList;
519 :
520 214461 : return true;
521 214461 : }
522 :
523 341271 : bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, const CCoinsViewCache& view, bool fJustCheck,
524 : bool fCheckCbTxMerkleRoots, BlockValidationState& state, std::optional<MNListUpdates>& updatesRet)
525 : {
526 341271 : AssertLockHeld(::cs_main);
527 :
528 : try {
529 : static int64_t nTimeLoop = 0;
530 : static int64_t nTimeQuorum = 0;
531 : static int64_t nTimeDMN = 0;
532 : static int64_t nTimeMerkleMNL = 0;
533 : static int64_t nTimeMerkleQuorums = 0;
534 : static int64_t nTimeCbTxCL = 0;
535 : static int64_t nTimeMnehf = 0;
536 : static int64_t nTimePayload = 0;
537 : static int64_t nTimeCreditPool = 0;
538 :
539 341271 : int64_t nTime1 = GetTimeMicros();
540 :
541 341271 : std::optional<CCbTx> opt_cbTx{std::nullopt};
542 341271 : if (fCheckCbTxMerkleRoots && block.vtx.size() > 0 && block.vtx[0]->nType == TRANSACTION_COINBASE) {
543 167555 : const auto& tx = block.vtx[0];
544 167555 : if (!tx->IsCoinBase()) {
545 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-invalid");
546 : }
547 167555 : if (opt_cbTx = GetTxPayload<CCbTx>(*tx); opt_cbTx) {
548 167555 : TxValidationState tx_state;
549 167555 : if (!CheckCbTx(*opt_cbTx, pindex->pprev, tx_state)) {
550 0 : assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS ||
551 : tx_state.GetResult() == TxValidationResult::TX_BAD_SPECIAL);
552 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(),
553 0 : strprintf("Special Transaction check failed (tx hash %s) %s",
554 0 : tx->GetHash().ToString(), tx_state.GetDebugMessage()));
555 : }
556 167555 : } else {
557 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-payload");
558 : }
559 167555 : }
560 341271 : if (fCheckCbTxMerkleRoots) {
561 : // To ensure that opt_cbTx is not missing when it's supposed to be
562 341067 : if (DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0003) && !opt_cbTx.has_value()) {
563 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-version");
564 : }
565 341067 : }
566 :
567 341271 : int64_t nTime2 = GetTimeMicros();
568 341271 : nTimePayload += nTime2 - nTime1;
569 341271 : LogPrint(BCLog::BENCHMARK, " - GetTxPayload: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1),
570 : nTimePayload * 0.000001);
571 :
572 341271 : CRangesSet indexes;
573 341271 : if (DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V20)) {
574 111528 : CCreditPool creditPool{m_cpoolman.GetCreditPool(pindex->pprev)};
575 111528 : LogPrint(BCLog::CREDITPOOL, "CSpecialTxProcessor::%s -- CCreditPool is %s\n", __func__, creditPool.ToString());
576 111528 : indexes = std::move(creditPool.indexes);
577 111528 : }
578 :
579 1046655 : for (size_t i = 0; i < block.vtx.size(); ++i) {
580 : // we validated CCbTx above, starts from the 2nd transaction
581 705403 : if (i == 0 && block.vtx[i]->nType == TRANSACTION_COINBASE) continue;
582 :
583 537848 : const auto ptr_tx = block.vtx[i];
584 537848 : TxValidationState tx_state;
585 : // At this moment CheckSpecialTx() may fail by 2 possible ways:
586 : // consensus failures and "TX_BAD_SPECIAL"
587 537848 : if (!CheckSpecialTxInner(m_dmnman, m_qsnapman, m_chainman, m_qman, *ptr_tx, pindex->pprev, view, indexes,
588 537848 : fCheckCbTxMerkleRoots, tx_state)) {
589 19 : assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS || tx_state.GetResult() == TxValidationResult::TX_BAD_SPECIAL);
590 38 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(),
591 19 : strprintf("Special Transaction check failed (tx hash %s) %s", ptr_tx->GetHash().ToString(), tx_state.GetDebugMessage()));
592 : }
593 537848 : }
594 :
595 341252 : int64_t nTime3 = GetTimeMicros();
596 341252 : nTimeLoop += nTime3 - nTime2;
597 341252 : LogPrint(BCLog::BENCHMARK, " - Loop: %.2fms [%.2fs]\n", 0.001 * (nTime3 - nTime2), nTimeLoop * 0.000001);
598 :
599 341252 : if (opt_cbTx.has_value()) {
600 167537 : if (!CheckCreditPoolDiffForBlock(block, pindex, *opt_cbTx, state)) {
601 24 : return error("CSpecialTxProcessor: CheckCreditPoolDiffForBlock for block %s failed with %s",
602 24 : pindex->GetBlockHash().ToString(), state.ToString());
603 : }
604 167513 : }
605 :
606 341228 : int64_t nTime4 = GetTimeMicros();
607 341228 : nTimeCreditPool += nTime4 - nTime3;
608 341228 : LogPrint(BCLog::BENCHMARK, " - CheckCreditPoolDiffForBlock: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3),
609 : nTimeCreditPool * 0.000001);
610 :
611 341228 : if (!m_qblockman.ProcessBlock(block, pindex, state, fJustCheck, fCheckCbTxMerkleRoots)) {
612 : // pass the state returned by the function above
613 4 : return false;
614 : }
615 :
616 341224 : int64_t nTime5 = GetTimeMicros();
617 341224 : nTimeQuorum += nTime5 - nTime4;
618 341224 : LogPrint(BCLog::BENCHMARK, " - m_qblockman.ProcessBlock: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4),
619 : nTimeQuorum * 0.000001);
620 :
621 341224 : CDeterministicMNList mn_list;
622 341224 : if (DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0003)) {
623 167509 : if (!BuildNewListFromBlock(block, pindex->pprev, view, true, state, mn_list)) {
624 : // pass the state returned by the function above
625 0 : return false;
626 : }
627 167509 : mn_list.SetBlockHash(pindex->GetBlockHash());
628 :
629 167509 : if (!fJustCheck && !m_dmnman.ProcessBlock(block, pindex, state, mn_list, updatesRet)) {
630 : // pass the state returned by the function above
631 0 : return false;
632 : }
633 167509 : }
634 :
635 341224 : int64_t nTime6 = GetTimeMicros();
636 341224 : nTimeDMN += nTime6 - nTime5;
637 341224 : LogPrint(BCLog::BENCHMARK, " - m_dmnman.ProcessBlock: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5),
638 : nTimeDMN * 0.000001);
639 :
640 341224 : if (opt_cbTx.has_value()) {
641 167509 : uint256 calculatedMerkleRootMNL;
642 167509 : if (!CalcCbTxMerkleRootMNList(calculatedMerkleRootMNL, mn_list.to_sml(), state)) {
643 : // pass the state returned by the function above
644 0 : return false;
645 : }
646 167509 : if (calculatedMerkleRootMNL != opt_cbTx->merkleRootMNList) {
647 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-mnmerkleroot");
648 : }
649 :
650 167509 : int64_t nTime6_1 = GetTimeMicros();
651 167509 : nTimeMerkleMNL += nTime6_1 - nTime6;
652 167509 : LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootMNList: %.2fms [%.2fs]\n",
653 : 0.001 * (nTime6_1 - nTime6), nTimeMerkleMNL * 0.000001);
654 :
655 167509 : if (opt_cbTx->nVersion >= CCbTx::Version::MERKLE_ROOT_QUORUMS) {
656 160829 : uint256 calculatedMerkleRootQuorums;
657 160829 : if (!CalcCbTxMerkleRootQuorums(block, pindex->pprev, m_qblockman, calculatedMerkleRootQuorums, state)) {
658 : // pass the state returned by the function above
659 0 : return false;
660 : }
661 160829 : if (calculatedMerkleRootQuorums != opt_cbTx->merkleRootQuorums) {
662 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-quorummerkleroot");
663 : }
664 160829 : }
665 :
666 167509 : int64_t nTime6_2 = GetTimeMicros();
667 167509 : nTimeMerkleQuorums += nTime6_2 - nTime6_1;
668 :
669 167509 : LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootQuorums: %.2fms [%.2fs]\n",
670 : 0.001 * (nTime6_2 - nTime6_1), nTimeMerkleQuorums * 0.000001);
671 :
672 167509 : if (!CheckCbTxBestChainlock(*opt_cbTx, pindex, m_consensus_params, m_chainman.ActiveChain(), m_qman,
673 167509 : m_chainlocks, state)) {
674 : // pass the state returned by the function above
675 9 : return false;
676 : }
677 :
678 167500 : int64_t nTime6_3 = GetTimeMicros();
679 167500 : nTimeCbTxCL += nTime6_3 - nTime6_2;
680 167500 : LogPrint(BCLog::BENCHMARK, " - CheckCbTxBestChainlock: %.2fms [%.2fs]\n",
681 : 0.001 * (nTime6_3 - nTime6_2), nTimeCbTxCL * 0.000001);
682 167500 : }
683 :
684 341215 : int64_t nTime7 = GetTimeMicros();
685 :
686 341215 : if (!m_mnhfman.ProcessBlock(block, pindex, fJustCheck, state)) {
687 : // pass the state returned by the function above
688 0 : return false;
689 : }
690 :
691 341215 : int64_t nTime8 = GetTimeMicros();
692 341215 : nTimeMnehf += nTime8 - nTime7;
693 341215 : LogPrint(BCLog::BENCHMARK, " - m_mnhfman.ProcessBlock: %.2fms [%.2fs]\n", 0.001 * (nTime8 - nTime7),
694 : nTimeMnehf * 0.000001);
695 :
696 341215 : if (DeploymentActiveAfter(pindex, m_consensus_params, Consensus::DEPLOYMENT_V19) && bls::bls_legacy_scheme.load()) {
697 : // NOTE: The block next to the activation is the one that is using new rules.
698 : // V19 activated just activated, so we must switch to the new rules here.
699 1258 : bls::bls_legacy_scheme.store(false);
700 1258 : LogPrintf("CSpecialTxProcessor::%s -- bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
701 1258 : }
702 341271 : } catch (const std::exception& e) {
703 0 : LogPrintf("CSpecialTxProcessor::%s -- FAILURE! %s\n", __func__, e.what());
704 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-procspectxsinblock");
705 0 : }
706 :
707 341215 : return true;
708 341271 : }
709 :
710 26566 : bool CSpecialTxProcessor::UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, std::optional<MNListUpdates>& updatesRet)
711 : {
712 26566 : AssertLockHeld(::cs_main);
713 :
714 26566 : auto bls_legacy_scheme = bls::bls_legacy_scheme.load();
715 :
716 : try {
717 26566 : if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V19) && !bls_legacy_scheme) {
718 : // NOTE: The block next to the activation is the one that is using new rules.
719 : // Removing the activation block here, so we must switch back to the old rules.
720 7 : bls::bls_legacy_scheme.store(true);
721 7 : LogPrintf("CSpecialTxProcessor::%s -- bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
722 7 : }
723 :
724 26566 : if (!m_mnhfman.UndoBlock(block, pindex)) {
725 0 : return false;
726 : }
727 :
728 26566 : if (!m_dmnman.UndoBlock(pindex, updatesRet)) {
729 0 : return false;
730 : }
731 :
732 26566 : if (!m_qblockman.UndoBlock(block, pindex)) {
733 0 : return false;
734 : }
735 26566 : } catch (const std::exception& e) {
736 0 : bls::bls_legacy_scheme.store(bls_legacy_scheme);
737 0 : LogPrintf("CSpecialTxProcessor::%s -- bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load());
738 0 : return error(strprintf("CSpecialTxProcessor::%s -- FAILURE! %s\n", __func__, e.what()).c_str());
739 0 : }
740 :
741 26566 : return true;
742 26566 : }
743 :
744 167537 : bool CSpecialTxProcessor::CheckCreditPoolDiffForBlock(const CBlock& block, const CBlockIndex* pindex, const CCbTx& cbTx,
745 : BlockValidationState& state)
746 : {
747 167537 : AssertLockHeld(::cs_main);
748 :
749 167537 : if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_DIP0008)) return true;
750 160857 : if (!DeploymentActiveAt(*pindex, m_consensus_params, Consensus::DEPLOYMENT_V20)) return true;
751 :
752 : try {
753 111510 : const CAmount blockSubsidy = GetBlockSubsidy(pindex, m_consensus_params);
754 223020 : const auto creditPoolDiff = GetCreditPoolDiffForBlock(m_cpoolman, block,
755 111510 : pindex->pprev, m_consensus_params, blockSubsidy, state);
756 111510 : if (!creditPoolDiff.has_value()) return false;
757 :
758 111498 : const CAmount target_balance{cbTx.creditPoolBalance};
759 : // But it maybe not included yet in previous block yet; in this case value must be 0
760 111498 : const CAmount locked_calculated{creditPoolDiff->GetTotalLocked()};
761 111498 : if (target_balance != locked_calculated) {
762 12 : LogPrintf("CSpecialTxProcessor::%s -- mismatched locked amount in CbTx: %lld against re-calculated: %lld\n", __func__, target_balance, locked_calculated);
763 12 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-assetlocked-amount");
764 : }
765 :
766 111510 : } catch (const std::exception& e) {
767 0 : LogPrintf("CSpecialTxProcessor::%s -- FAILURE! %s\n", __func__, e.what());
768 0 : return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-checkcreditpooldiff");
769 0 : }
770 :
771 111486 : return true;
772 167537 : }
773 :
774 : template <typename ProTx>
775 7577 : static bool CheckService(const ProTx& proTx, TxValidationState& state)
776 : {
777 7577 : switch (proTx.netInfo->Validate()) {
778 : case NetInfoStatus::BadAddress:
779 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr");
780 : case NetInfoStatus::BadPort:
781 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-port");
782 : case NetInfoStatus::BadType:
783 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr-type");
784 : case NetInfoStatus::NotRoutable:
785 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-addr-unroutable");
786 : case NetInfoStatus::Malformed:
787 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-bad");
788 : case NetInfoStatus::Success:
789 7577 : return true;
790 : // Shouldn't be possible during self-checks
791 : case NetInfoStatus::BadInput:
792 : case NetInfoStatus::Duplicate:
793 : case NetInfoStatus::MaxLimit:
794 0 : assert(false);
795 : } // no default case, so the compiler can warn about missing cases
796 0 : assert(false);
797 7577 : }
798 :
799 : template <typename ProTx>
800 654 : static bool CheckPlatformFields(const ProTx& proTx, bool is_extended_addr, TxValidationState& state)
801 : {
802 654 : if (proTx.platformNodeID.IsNull()) {
803 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-nodeid");
804 : }
805 :
806 654 : if (is_extended_addr) {
807 : // platformHTTPPort and platformP2PPort have been subsumed by netInfo. They should always be zero.
808 158 : if (proTx.platformP2PPort != 0) {
809 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port");
810 : }
811 158 : if (proTx.platformHTTPPort != 0) {
812 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
813 : }
814 158 : return true;
815 : }
816 :
817 496 : if (::IsNodeOnMainnet()) {
818 0 : if (proTx.platformP2PPort != ::MainParams().GetDefaultPlatformP2PPort()) {
819 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port");
820 : }
821 0 : if (proTx.platformHTTPPort != ::MainParams().GetDefaultPlatformHTTPPort()) {
822 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
823 : }
824 0 : }
825 496 : if (proTx.platformP2PPort == ::MainParams().GetDefaultPort()) {
826 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-p2p-port");
827 : }
828 496 : if (proTx.platformHTTPPort == ::MainParams().GetDefaultPort()) {
829 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-http-port");
830 : }
831 :
832 496 : const uint16_t core_port{proTx.netInfo->GetPrimary().GetPort()};
833 496 : if (proTx.platformP2PPort == proTx.platformHTTPPort || proTx.platformP2PPort == core_port ||
834 496 : proTx.platformHTTPPort == core_port) {
835 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-platform-dup-ports");
836 : }
837 :
838 496 : return true;
839 654 : }
840 :
841 : template <typename ProTx>
842 143 : static bool CheckHashSig(const ProTx& proTx, const PKHash& pkhash, TxValidationState& state)
843 : {
844 145 : if (std::string strError; !CHashSigner::VerifyHash(::SerializeHash(proTx), ToKeyID(pkhash), proTx.vchSig, strError)) {
845 2 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig");
846 : }
847 141 : return true;
848 143 : }
849 :
850 : template <typename ProTx>
851 2376 : static bool CheckStringSig(const ProTx& proTx, const PKHash& pkhash, TxValidationState& state)
852 : {
853 4752 : if (std::string strError;
854 2376 : !CMessageSigner::VerifyMessage(ToKeyID(pkhash), proTx.vchSig, proTx.MakeSignString(), strError)) {
855 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig");
856 : }
857 2376 : return true;
858 2376 : }
859 :
860 : template <typename ProTx>
861 3872 : static bool CheckHashSig(const ProTx& proTx, const CBLSPublicKey& pubKey, TxValidationState& state)
862 : {
863 3872 : if (!proTx.sig.VerifyInsecure(pubKey, ::SerializeHash(proTx))) {
864 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig");
865 : }
866 3872 : return true;
867 3872 : }
868 :
869 : template <typename ProTx>
870 7881 : static std::optional<ProTx> GetValidatedPayload(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev,
871 : const ChainstateManager& chainman, TxValidationState& state)
872 : {
873 7881 : if (tx.nType != ProTx::SPECIALTX_TYPE) {
874 0 : state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type");
875 0 : return std::nullopt;
876 : }
877 :
878 7881 : auto opt_ptx = GetTxPayload<ProTx>(tx);
879 7881 : if (!opt_ptx) {
880 0 : state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload");
881 0 : return std::nullopt;
882 : }
883 7881 : if (!opt_ptx->IsTriviallyValid(pindexPrev, chainman, state)) {
884 : // pass the state returned by the function above
885 0 : return std::nullopt;
886 : }
887 7881 : return opt_ptx;
888 7881 : }
889 :
890 : /**
891 : * Validates potential changes to masternode state version by ProTx transaction version
892 : * @param[in] pindexPrev Previous block index to validate DEPLOYMENT_V24 activation
893 : * @param[in] tx_type Special transaction type
894 : * @param[in] state_version Current masternode state version
895 : * @param[in] tx_version Proposed transaction version
896 : * @param[out] state This may be set to an Error state if any error occurred processing them
897 : * @returns true if version change is valid or DEPLOYMENT_V24 is not active
898 : */
899 4026 : static bool IsVersionChangeValid(gsl::not_null<const CBlockIndex*> pindexPrev, const uint16_t tx_type,
900 : const uint16_t state_version, const uint16_t tx_version,
901 : const ChainstateManager& chainman, TxValidationState& state)
902 : {
903 4026 : if (!DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)) {
904 : // New restrictions only apply after v24 deployment
905 3964 : return true;
906 : }
907 :
908 62 : if (state_version >= ProTxVersion::BasicBLS && tx_version == ProTxVersion::LegacyBLS) {
909 : // Don't allow legacy scheme versioned transactions after upgrading to basic scheme
910 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-downgrade");
911 : }
912 :
913 62 : if (state_version == ProTxVersion::LegacyBLS && tx_version > ProTxVersion::BasicBLS) {
914 : // Nodes using the legacy scheme must first upgrade to the basic scheme before upgrading further
915 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-upgrade");
916 : }
917 :
918 62 : if (tx_type != TRANSACTION_PROVIDER_UPDATE_SERVICE && tx_version == ProTxVersion::ExtAddr) {
919 : // Only new entries (ProRegTx) and service updates (ProUpServTx) can use ExtAddr versioning
920 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-tx-type");
921 : }
922 :
923 62 : return true;
924 4026 : }
925 :
926 3855 : bool CheckProRegTx(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev,
927 : CDeterministicMNManager& dmnman, const CCoinsViewCache& view, const ChainstateManager& chainman,
928 : TxValidationState& state, bool check_sigs)
929 : {
930 3855 : const auto opt_ptx = GetValidatedPayload<CProRegTx>(tx, pindexPrev, chainman, state);
931 3855 : if (!opt_ptx) {
932 : // pass the state returned by the function above
933 0 : return false;
934 : }
935 :
936 3855 : const bool is_v24_active{DeploymentActiveAfter(pindexPrev, chainman, Consensus::DEPLOYMENT_V24)};
937 :
938 : // No longer allow legacy scheme masternode registration
939 3855 : if (is_v24_active && opt_ptx->nVersion < ProTxVersion::BasicBLS) {
940 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-version-disallowed");
941 : }
942 :
943 : // It's allowed to set addr to 0, which will put the MN into PoSe-banned state and require a ProUpServTx to be
944 : // issues later. If any of both is set, it must be valid however
945 3855 : if (!opt_ptx->netInfo->IsEmpty() && !CheckService(*opt_ptx, state)) {
946 : // pass the state returned by the function above
947 0 : return false;
948 : }
949 :
950 3855 : if (opt_ptx->nType == MnType::Evo) {
951 396 : if (!CheckPlatformFields(*opt_ptx, opt_ptx->nVersion >= ProTxVersion::ExtAddr, state)) {
952 0 : return false;
953 : }
954 396 : }
955 :
956 3855 : CTxDestination collateralTxDest;
957 3855 : const PKHash* keyForPayloadSig = nullptr;
958 3855 : COutPoint collateralOutpoint;
959 :
960 3855 : CAmount expectedCollateral = GetMnType(opt_ptx->nType).collat_amount;
961 :
962 3855 : if (!opt_ptx->collateralOutpoint.hash.IsNull()) {
963 2382 : Coin coin;
964 2382 : if (!view.GetCoin(opt_ptx->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral) {
965 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral");
966 : }
967 :
968 2382 : if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) {
969 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-dest");
970 : }
971 :
972 : // Extract key from collateral. This only works for P2PK and P2PKH collaterals and will fail for P2SH.
973 : // Issuer of this ProRegTx must prove ownership with this key by signing the ProRegTx
974 2382 : keyForPayloadSig = std::get_if<PKHash>(&collateralTxDest);
975 2382 : if (!keyForPayloadSig) {
976 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-pkh");
977 : }
978 :
979 2382 : collateralOutpoint = opt_ptx->collateralOutpoint;
980 2382 : } else {
981 1473 : if (opt_ptx->collateralOutpoint.n >= tx.vout.size()) {
982 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-index");
983 : }
984 1473 : if (tx.vout[opt_ptx->collateralOutpoint.n].nValue != expectedCollateral) {
985 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral");
986 : }
987 :
988 1473 : if (!ExtractDestination(tx.vout[opt_ptx->collateralOutpoint.n].scriptPubKey, collateralTxDest)) {
989 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-dest");
990 : }
991 :
992 1473 : collateralOutpoint = COutPoint(tx.GetHash(), opt_ptx->collateralOutpoint.n);
993 : }
994 :
995 : // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
996 : // this check applies to internal and external collateral, but internal collaterals are not necessarily a P2PKH
997 7710 : if (collateralTxDest == CTxDestination(PKHash(opt_ptx->keyIDOwner)) ||
998 3855 : collateralTxDest == CTxDestination(PKHash(opt_ptx->keyIDVoting))) {
999 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-reuse");
1000 : }
1001 :
1002 3855 : if (pindexPrev) {
1003 3855 : auto mnList = dmnman.GetListForBlock(pindexPrev);
1004 :
1005 : // only allow reusing of addresses when it's for the same collateral (which replaces the old MN)
1006 7832 : for (const auto& entry : opt_ptx->netInfo->GetEntries()) {
1007 7954 : if (const auto service_opt{entry.GetAddrPort()}) {
1008 4313 : if (mnList.HasUniqueProperty(*service_opt) &&
1009 340 : mnList.GetUniquePropertyMN(*service_opt)->collateralOutpoint != collateralOutpoint) {
1010 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
1011 : }
1012 3981 : } else if (const auto domain_opt{entry.GetDomainPort()}) {
1013 4 : if (mnList.HasUniqueProperty(*domain_opt) &&
1014 0 : mnList.GetUniquePropertyMN(*domain_opt)->collateralOutpoint != collateralOutpoint) {
1015 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
1016 : }
1017 4 : } else {
1018 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-entry");
1019 : }
1020 : }
1021 :
1022 : // never allow duplicate keys, even if this ProTx would replace an existing MN
1023 3855 : if (mnList.HasUniqueProperty(opt_ptx->keyIDOwner) || mnList.HasUniqueProperty(opt_ptx->pubKeyOperator)) {
1024 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-key");
1025 : }
1026 :
1027 : // never allow duplicate platformNodeIds for EvoNodes
1028 3855 : if (opt_ptx->nType == MnType::Evo) {
1029 396 : if (mnList.HasUniqueProperty(opt_ptx->platformNodeID)) {
1030 6 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-platformnodeid");
1031 : }
1032 390 : }
1033 :
1034 3849 : if (!DeploymentDIP0003Enforced(pindexPrev->nHeight, Params().GetConsensus())) {
1035 25 : if (opt_ptx->keyIDOwner != opt_ptx->keyIDVoting) {
1036 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-not-same");
1037 : }
1038 25 : }
1039 3855 : }
1040 :
1041 3849 : if (!CheckInputsHash(tx, *opt_ptx, state)) {
1042 : // pass the state returned by the function above
1043 0 : return false;
1044 : }
1045 :
1046 3849 : if (keyForPayloadSig) {
1047 : // collateral is not part of this ProRegTx, so we must verify ownership of the collateral
1048 2376 : if (check_sigs && !CheckStringSig(*opt_ptx, *keyForPayloadSig, state)) {
1049 : // pass the state returned by the function above
1050 0 : return false;
1051 : }
1052 2376 : } else {
1053 : // collateral is part of this ProRegTx, so we know the collateral is owned by the issuer
1054 1473 : if (!opt_ptx->vchSig.empty()) {
1055 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-sig");
1056 : }
1057 : }
1058 :
1059 3849 : return true;
1060 3855 : }
1061 :
1062 3786 : bool CheckProUpServTx(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, CDeterministicMNManager& dmnman,
1063 : const ChainstateManager& chainman, TxValidationState& state, bool check_sigs)
1064 : {
1065 3786 : const auto opt_ptx = GetValidatedPayload<CProUpServTx>(tx, pindexPrev, chainman, state);
1066 3786 : if (!opt_ptx) {
1067 : // pass the state returned by the function above
1068 0 : return false;
1069 : }
1070 :
1071 3786 : if (!CheckService(*opt_ptx, state)) {
1072 : // pass the state returned by the function above
1073 0 : return false;
1074 : }
1075 :
1076 3786 : if (opt_ptx->nType == MnType::Evo) {
1077 258 : if (!CheckPlatformFields(*opt_ptx, opt_ptx->nVersion >= ProTxVersion::ExtAddr, state)) {
1078 0 : return false;
1079 : }
1080 258 : }
1081 :
1082 3786 : auto mnList = dmnman.GetListForBlock(pindexPrev);
1083 3786 : auto dmn = mnList.GetMN(opt_ptx->proTxHash);
1084 3786 : if (!dmn) {
1085 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash");
1086 : }
1087 :
1088 3786 : if (!IsVersionChangeValid(pindexPrev, tx.nType, dmn->pdmnState->nVersion, opt_ptx->nVersion, chainman, state)) {
1089 : // pass the state returned by the function above
1090 0 : return false;
1091 : }
1092 :
1093 : // don't allow updating to addresses already used by other MNs
1094 7826 : for (const auto& entry : opt_ptx->netInfo->GetEntries()) {
1095 8080 : if (const auto service_opt{entry.GetAddrPort()}) {
1096 6073 : if (mnList.HasUniqueProperty(*service_opt) &&
1097 2063 : mnList.GetUniquePropertyMN(*service_opt)->proTxHash != opt_ptx->proTxHash) {
1098 6 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
1099 : }
1100 4064 : } else if (const auto domain_opt{entry.GetDomainPort()}) {
1101 32 : if (mnList.HasUniqueProperty(*domain_opt) &&
1102 2 : mnList.GetUniquePropertyMN(*domain_opt)->proTxHash != opt_ptx->proTxHash) {
1103 2 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-netinfo-entry");
1104 : }
1105 28 : } else {
1106 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-netinfo-entry");
1107 : }
1108 : }
1109 :
1110 : // don't allow updating to platformNodeIds already used by other EvoNodes
1111 3778 : if (opt_ptx->nType == MnType::Evo) {
1112 353 : if (mnList.HasUniqueProperty(opt_ptx->platformNodeID) &&
1113 103 : mnList.GetUniquePropertyMN(opt_ptx->platformNodeID)->proTxHash != opt_ptx->proTxHash) {
1114 3 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-platformnodeid");
1115 : }
1116 247 : }
1117 :
1118 3775 : if (opt_ptx->scriptOperatorPayout != CScript()) {
1119 1984 : if (dmn->nOperatorReward == 0) {
1120 : // don't allow setting operator reward payee in case no operatorReward was set
1121 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-payee");
1122 : }
1123 1984 : if (!opt_ptx->scriptOperatorPayout.IsPayToPublicKeyHash() && !opt_ptx->scriptOperatorPayout.IsPayToScriptHash()) {
1124 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-operator-payee");
1125 : }
1126 1984 : }
1127 :
1128 : // we can only check the signature if pindexPrev != nullptr and the MN is known
1129 3775 : if (!CheckInputsHash(tx, *opt_ptx, state)) {
1130 : // pass the state returned by the function above
1131 0 : return false;
1132 : }
1133 3775 : if (check_sigs && !CheckHashSig(*opt_ptx, dmn->pdmnState->pubKeyOperator.Get(), state)) {
1134 : // pass the state returned by the function above
1135 0 : return false;
1136 : }
1137 :
1138 3775 : return true;
1139 3786 : }
1140 :
1141 143 : bool CheckProUpRegTx(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev,
1142 : CDeterministicMNManager& dmnman, const CCoinsViewCache& view, const ChainstateManager& chainman,
1143 : TxValidationState& state, bool check_sigs)
1144 : {
1145 143 : const auto opt_ptx = GetValidatedPayload<CProUpRegTx>(tx, pindexPrev, chainman, state);
1146 143 : if (!opt_ptx) {
1147 : // pass the state returned by the function above
1148 0 : return false;
1149 : }
1150 :
1151 143 : CTxDestination payoutDest;
1152 143 : if (!ExtractDestination(opt_ptx->scriptPayout, payoutDest)) {
1153 : // should not happen as we checked script types before
1154 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-dest");
1155 : }
1156 :
1157 143 : auto mnList = dmnman.GetListForBlock(pindexPrev);
1158 143 : auto dmn = mnList.GetMN(opt_ptx->proTxHash);
1159 143 : if (!dmn) {
1160 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash");
1161 : }
1162 :
1163 143 : if (!IsVersionChangeValid(pindexPrev, tx.nType, dmn->pdmnState->nVersion, opt_ptx->nVersion, chainman, state)) {
1164 : // pass the state returned by the function above
1165 0 : return false;
1166 : }
1167 :
1168 : // don't allow reuse of payee key for other keys (don't allow people to put the payee key onto an online server)
1169 286 : if (payoutDest == CTxDestination(PKHash(dmn->pdmnState->keyIDOwner)) ||
1170 143 : payoutDest == CTxDestination(PKHash(opt_ptx->keyIDVoting))) {
1171 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-payee-reuse");
1172 : }
1173 :
1174 143 : Coin coin;
1175 143 : if (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent()) {
1176 : // this should never happen (there would be no dmn otherwise)
1177 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-collateral");
1178 : }
1179 :
1180 : // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server)
1181 143 : CTxDestination collateralTxDest;
1182 143 : if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) {
1183 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-collateral-dest");
1184 : }
1185 286 : if (collateralTxDest == CTxDestination(PKHash(dmn->pdmnState->keyIDOwner)) ||
1186 143 : collateralTxDest == CTxDestination(PKHash(opt_ptx->keyIDVoting))) {
1187 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-collateral-reuse");
1188 : }
1189 :
1190 143 : if (mnList.HasUniqueProperty(opt_ptx->pubKeyOperator)) {
1191 136 : auto otherDmn = mnList.GetUniquePropertyMN(opt_ptx->pubKeyOperator);
1192 136 : if (opt_ptx->proTxHash != otherDmn->proTxHash) {
1193 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-dup-key");
1194 : }
1195 136 : }
1196 :
1197 143 : if (!DeploymentDIP0003Enforced(pindexPrev->nHeight, Params().GetConsensus())) {
1198 1 : if (dmn->pdmnState->keyIDOwner != opt_ptx->keyIDVoting) {
1199 0 : return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-not-same");
1200 : }
1201 1 : }
1202 :
1203 143 : if (!CheckInputsHash(tx, *opt_ptx, state)) {
1204 : // pass the state returned by the function above
1205 0 : return false;
1206 : }
1207 143 : if (check_sigs && !CheckHashSig(*opt_ptx, PKHash(dmn->pdmnState->keyIDOwner), state)) {
1208 : // pass the state returned by the function above
1209 2 : return false;
1210 : }
1211 :
1212 141 : return true;
1213 143 : }
1214 :
1215 97 : bool CheckProUpRevTx(const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, CDeterministicMNManager& dmnman,
1216 : const ChainstateManager& chainman, TxValidationState& state, bool check_sigs)
1217 : {
1218 97 : const auto opt_ptx = GetValidatedPayload<CProUpRevTx>(tx, pindexPrev, chainman, state);
1219 97 : if (!opt_ptx) {
1220 : // pass the state returned by the function above
1221 0 : return false;
1222 : }
1223 :
1224 97 : auto mnList = dmnman.GetListForBlock(pindexPrev);
1225 97 : auto dmn = mnList.GetMN(opt_ptx->proTxHash);
1226 97 : if (!dmn) {
1227 0 : return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-hash");
1228 : }
1229 :
1230 97 : if (!IsVersionChangeValid(pindexPrev, tx.nType, dmn->pdmnState->nVersion, opt_ptx->nVersion, chainman, state)) {
1231 : // pass the state returned by the function above
1232 0 : return false;
1233 : }
1234 :
1235 97 : if (!CheckInputsHash(tx, *opt_ptx, state)) {
1236 : // pass the state returned by the function above
1237 0 : return false;
1238 : }
1239 97 : if (check_sigs && !CheckHashSig(*opt_ptx, dmn->pdmnState->pubKeyOperator.Get(), state)) {
1240 : // pass the state returned by the function above
1241 0 : return false;
1242 : }
1243 :
1244 97 : return true;
1245 97 : }
1246 :
1247 47752 : bool IsStandardSpecialTx(const CTransaction& tx, std::string& reason)
1248 : {
1249 47752 : if (!tx.IsSpecialTxVersion()) return true;
1250 :
1251 3951 : if (tx.nType != TRANSACTION_ASSET_LOCK) return true;
1252 :
1253 : // Each input is referenced by Platform's funding state transition; beyond this
1254 : // many inputs that state transition exceeds Platform's ~20 kB size limit.
1255 : static constexpr size_t MAX_STANDARD_ASSET_LOCK_INPUTS{100};
1256 1326 : if (tx.vin.size() > MAX_STANDARD_ASSET_LOCK_INPUTS) {
1257 3 : reason = "assetlocktx-too-many-inputs";
1258 3 : return false;
1259 : }
1260 :
1261 1323 : constexpr int max_tx_size_for_platform = 20480;
1262 1323 : if (tx.GetTotalSize() > max_tx_size_for_platform) {
1263 0 : reason = "assetlocktx-too-big";
1264 0 : return false;
1265 : }
1266 :
1267 2646 : if (const auto opt_assetLockTx = GetTxPayload<CAssetLockPayload>(tx);
1268 1323 : opt_assetLockTx.has_value() && opt_assetLockTx->getVersion() >= 2) {
1269 0 : reason = "assetlocktx-version-2";
1270 0 : return false;
1271 : }
1272 :
1273 1323 : return true;
1274 47752 : }
|