Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/evo/assetlocktx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,34 @@ std::string CAssetLockPayload::ToString() const

const std::string ASSETUNLOCK_REQUESTID_PREFIX = "plwdtx";

bool CheckAssetUnlockTxBasic(const CTransaction& tx, TxValidationState& state)
{
// Context-free basic validation - no chain state, UTXO, or signatures
if (tx.nType != TRANSACTION_ASSET_UNLOCK) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlocktx-type");
}

if (!tx.vin.empty()) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlocktx-have-input");
}

if (tx.vout.size() > CAssetUnlockPayload::MAXIMUM_WITHDRAWALS) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlocktx-too-many-outs");
}

const auto opt_assetUnlockTx = GetTxPayload<CAssetUnlockPayload>(tx);
if (!opt_assetUnlockTx) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlocktx-payload");
}
auto& assetUnlockTx = *opt_assetUnlockTx;

if (assetUnlockTx.getVersion() == 0 || assetUnlockTx.getVersion() > CAssetUnlockPayload::CURRENT_VERSION) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlocktx-version");
}

return true;
}

bool CAssetUnlockPayload::VerifySig(const llmq::CQuorumManager& qman, const uint256& msgHash, gsl::not_null<const CBlockIndex*> pindexTip, TxValidationState& state) const
{
// That quourm hash must be active at `requestHeight`,
Expand Down
1 change: 1 addition & 0 deletions src/evo/assetlocktx.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class CAssetUnlockPayload
};

bool CheckAssetLockTx(const CTransaction& tx, TxValidationState& state);
bool CheckAssetUnlockTxBasic(const CTransaction& tx, TxValidationState& state);
bool CheckAssetUnlockTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, const std::optional<CRangesSet>& indexes, TxValidationState& state);
bool CheckAssetLockUnlockTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, const std::optional<CRangesSet>& indexes, TxValidationState& state);
bool GetAssetUnlockFee(const CTransaction& tx, CAmount& txfee, TxValidationState& state);
Expand Down
24 changes: 24 additions & 0 deletions src/evo/cbtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@

using node::ReadBlockFromDisk;

bool CheckCbTxBasic(const CTransaction& tx, TxValidationState& state)
{
// Context-free basic validation - no chain state
if (!tx.IsCoinBase()) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-invalid");
}

if (tx.nType != TRANSACTION_COINBASE) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-type");
}

const auto opt_cbTx = GetTxPayload<CCbTx>(tx);
if (!opt_cbTx) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-payload");
}

auto& cbTx = *opt_cbTx;
if (cbTx.nVersion == CCbTx::Version::INVALID || cbTx.nVersion >= CCbTx::Version::UNKNOWN) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cbtx-version");
}

return true;
}

bool CheckCbTx(const CCbTx& cbTx, const CBlockIndex* pindexPrev, TxValidationState& state)
{
if (cbTx.nVersion == CCbTx::Version::INVALID || cbTx.nVersion >= CCbTx::Version::UNKNOWN) {
Expand Down
1 change: 1 addition & 0 deletions src/evo/cbtx.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class CCbTx
};
template<> struct is_serializable_enum<CCbTx::Version> : std::true_type {};

bool CheckCbTxBasic(const CTransaction& tx, TxValidationState& state);
bool CheckCbTx(const CCbTx& cbTx, const CBlockIndex* pindexPrev, TxValidationState& state);

bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev,
Expand Down
60 changes: 60 additions & 0 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,21 @@ bool IsVersionChangeValid(gsl::not_null<const CBlockIndex*> pindexPrev, const ui
return true;
}

bool CheckProRegTxBasic(const CTransaction& tx, TxValidationState& state)
{
// Context-free basic validation - no chain state
if (tx.nType != CProRegTx::SPECIALTX_TYPE) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type");
}

auto opt_ptx = GetTxPayload<CProRegTx>(tx);
if (!opt_ptx) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload");
}

return true;
}

bool CheckProRegTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs)
{
const auto opt_ptx = GetValidatedPayload<CProRegTx>(tx, pindexPrev, state);
Expand Down Expand Up @@ -1223,6 +1238,21 @@ bool CheckProRegTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl:
return true;
}

bool CheckProUpServTxBasic(const CTransaction& tx, TxValidationState& state)
{
// Context-free basic validation - no chain state
if (tx.nType != CProUpServTx::SPECIALTX_TYPE) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type");
}

auto opt_ptx = GetTxPayload<CProUpServTx>(tx);
if (!opt_ptx) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload");
}

return true;
}

bool CheckProUpServTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state, bool check_sigs)
{
const auto opt_ptx = GetValidatedPayload<CProUpServTx>(tx, pindexPrev, state);
Expand Down Expand Up @@ -1299,6 +1329,21 @@ bool CheckProUpServTx(CDeterministicMNManager& dmnman, const CTransaction& tx, g
return true;
}

bool CheckProUpRegTxBasic(const CTransaction& tx, TxValidationState& state)
{
// Context-free basic validation - no chain state
if (tx.nType != CProUpRegTx::SPECIALTX_TYPE) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type");
}

auto opt_ptx = GetTxPayload<CProUpRegTx>(tx);
if (!opt_ptx) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload");
}

return true;
}

bool CheckProUpRegTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs)
{
const auto opt_ptx = GetValidatedPayload<CProUpRegTx>(tx, pindexPrev, state);
Expand Down Expand Up @@ -1369,6 +1414,21 @@ bool CheckProUpRegTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gs
return true;
}

bool CheckProUpRevTxBasic(const CTransaction& tx, TxValidationState& state)
{
// Context-free basic validation - no chain state
if (tx.nType != CProUpRevTx::SPECIALTX_TYPE) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-type");
}

auto opt_ptx = GetTxPayload<CProUpRevTx>(tx);
if (!opt_ptx) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-payload");
}

return true;
}

bool CheckProUpRevTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state, bool check_sigs)
{
const auto opt_ptx = GetValidatedPayload<CProUpRevTx>(tx, pindexPrev, state);
Expand Down
4 changes: 4 additions & 0 deletions src/evo/deterministicmns.h
Original file line number Diff line number Diff line change
Expand Up @@ -767,9 +767,13 @@ class CDeterministicMNManager
RecalcDiffsResult& result) EXCLUSIVE_LOCKS_REQUIRED(!cs);
};

bool CheckProRegTxBasic(const CTransaction& tx, TxValidationState& state);
bool CheckProRegTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs);
bool CheckProUpServTxBasic(const CTransaction& tx, TxValidationState& state);
bool CheckProUpServTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state, bool check_sigs);
bool CheckProUpRegTxBasic(const CTransaction& tx, TxValidationState& state);
bool CheckProUpRegTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state, const CCoinsViewCache& view, bool check_sigs);
bool CheckProUpRevTxBasic(const CTransaction& tx, TxValidationState& state);
bool CheckProUpRevTx(CDeterministicMNManager& dmnman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state, bool check_sigs);

#endif // BITCOIN_EVO_DETERMINISTICMNS_H
19 changes: 19 additions & 0 deletions src/evo/mnhftx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,25 @@ bool MNHFTx::Verify(const llmq::CQuorumManager& qman, const uint256& quorumHash,
return true;
}

bool CheckMNHFTxBasic(const CTransaction& tx, TxValidationState& state)
{
// Context-free basic validation - no chain state
if (!tx.IsSpecialTxVersion() || tx.nType != TRANSACTION_MNHF_SIGNAL) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-type");
}

const auto opt_mnhfTx = GetTxPayload<MNHFTxPayload>(tx);
if (!opt_mnhfTx) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-payload");
}
auto& mnhfTx = *opt_mnhfTx;
if (mnhfTx.nVersion == 0 || mnhfTx.nVersion > MNHFTxPayload::CURRENT_VERSION) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-version");
}

return true;
}

bool CheckMNHFTx(const ChainstateManager& chainman, const llmq::CQuorumManager& qman, const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state)
{
if (!tx.IsSpecialTxVersion() || tx.nType != TRANSACTION_MNHF_SIGNAL) {
Expand Down
1 change: 1 addition & 0 deletions src/evo/mnhftx.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class CMNHFManager : public AbstractEHFManager
};

std::optional<uint8_t> extractEHFSignal(const CTransaction& tx);
bool CheckMNHFTxBasic(const CTransaction& tx, TxValidationState& state);
bool CheckMNHFTx(const ChainstateManager& chainman, const llmq::CQuorumManager& qman, const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state);

#endif // BITCOIN_EVO_MNHFTX_H
39 changes: 39 additions & 0 deletions src/evo/specialtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,47 @@
#include <evo/specialtx.h>

#include <clientversion.h>
#include <consensus/validation.h>
#include <hash.h>

#include <evo/assetlocktx.h>
#include <evo/cbtx.h>
#include <evo/deterministicmns.h>
#include <evo/mnhftx.h>
#include <llmq/commitment.h>

bool CheckSpecialTxBasic(const CTransaction& tx, TxValidationState& state)
{
// Context-free basic validation for special transactions - no chain state required
if (!tx.HasExtraPayloadField()) {
// Not a special transaction, nothing to check
return true;
}

switch (tx.nType) {
case TRANSACTION_PROVIDER_REGISTER:
return CheckProRegTxBasic(tx, state);
case TRANSACTION_PROVIDER_UPDATE_SERVICE:
return CheckProUpServTxBasic(tx, state);
case TRANSACTION_PROVIDER_UPDATE_REGISTRAR:
return CheckProUpRegTxBasic(tx, state);
case TRANSACTION_PROVIDER_UPDATE_REVOKE:
return CheckProUpRevTxBasic(tx, state);
case TRANSACTION_COINBASE:
return CheckCbTxBasic(tx, state);
case TRANSACTION_QUORUM_COMMITMENT:
return llmq::CheckLLMQCommitmentBasic(tx, state);
case TRANSACTION_MNHF_SIGNAL:
return CheckMNHFTxBasic(tx, state);
case TRANSACTION_ASSET_LOCK:
return CheckAssetLockTx(tx, state); // Already context-free
case TRANSACTION_ASSET_UNLOCK:
return CheckAssetUnlockTxBasic(tx, state);
default:
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-tx-type");
}
}

uint256 CalcTxInputsHash(const CTransaction& tx)
{
CHashWriter hw(SER_GETHASH, CLIENT_VERSION);
Expand Down
4 changes: 4 additions & 0 deletions src/evo/specialtx.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
#include <optional>
#include <vector>

class TxValidationState;

bool CheckSpecialTxBasic(const CTransaction& tx, TxValidationState& state);

template <typename T>
std::optional<T> GetTxPayload(const std::vector<unsigned char>& payload)
{
Expand Down
30 changes: 30 additions & 0 deletions src/llmq/commitment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,36 @@ bool CFinalCommitment::VerifySizes(const Consensus::LLMQParams& params) const
return true;
}

bool CheckLLMQCommitmentBasic(const CTransaction& tx, TxValidationState& state)
{
// Context-free basic validation - no chain state
if (tx.nType != TRANSACTION_QUORUM_COMMITMENT) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-type");
}

const auto opt_qcTx = GetTxPayload<CFinalCommitmentTxPayload>(tx);
if (!opt_qcTx) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-payload");
}
auto& qcTx = *opt_qcTx;

const auto& llmq_params_opt = Params().GetLLMQ(qcTx.commitment.llmqType);
if (!llmq_params_opt.has_value()) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-commitment-type");
}

if (qcTx.nVersion == 0 || qcTx.nVersion > CFinalCommitmentTxPayload::CURRENT_VERSION) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-version");
}

// Basic size validation (context-free)
if (!qcTx.commitment.IsNull() && !qcTx.commitment.VerifySizes(llmq_params_opt.value())) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-qc-invalid-sizes");
}

return true;
}

bool CheckLLMQCommitment(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman,
const ChainstateManager& chainman, const CTransaction& tx,
gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state)
Expand Down
1 change: 1 addition & 0 deletions src/llmq/commitment.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ class CFinalCommitmentTxPayload
[[nodiscard]] UniValue ToJson() const;
};

bool CheckLLMQCommitmentBasic(const CTransaction& tx, TxValidationState& state);
bool CheckLLMQCommitment(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman,
const ChainstateManager& chainman, const CTransaction& tx,
gsl::not_null<const CBlockIndex*> pindexPrev, TxValidationState& state);
Expand Down
11 changes: 11 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4026,6 +4026,17 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), tx_state.GetDebugMessage()));
}
}
// Check special transactions (context-independent basic validation)
for (const auto& tx : block.vtx) {
TxValidationState tx_state;
if (!CheckSpecialTxBasic(*tx, tx_state)) {
// CheckBlock() does context-free validation checks. The only
// possible failures are consensus failures.
assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS);
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(),
strprintf("Special transaction basic check failed (tx hash %s) %s", tx->GetHash().ToString(), tx_state.GetDebugMessage()));
}
}
unsigned int nSigOps = 0;
for (const auto& tx : block.vtx)
{
Expand Down
Loading