diff --git a/src/Makefile.am b/src/Makefile.am index ec127ae95b285..6c88d8e533049 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -137,7 +137,8 @@ BITCOIN_CORE_H = \ betting/bet_v2.h \ betting/bet_v3.h \ betting/bet_v4.h \ - betting/oracles.h \ + betting/events.h \ + betting/oracles.h \ betting/quickgames/dice.h \ betting/quickgames/qgview.h \ bip39.h \ @@ -784,6 +785,7 @@ libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ base58.cpp \ bech32.cpp \ + betting/events.cpp \ betting/quickgames/dice.cpp \ bip39.cpp \ wagerraddrenc.cpp \ diff --git a/src/betting/events.cpp b/src/betting/events.cpp new file mode 100644 index 0000000000000..ce63d53a6d6f4 --- /dev/null +++ b/src/betting/events.cpp @@ -0,0 +1,61 @@ +// Copyright (c) 2023 The Wagerr developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include + +bool CreateBetEventFromDB(const CBettingsView& bettingsViewCache, const uint32_t nEventId, CBetEvent& event) { + EventKey eventKey{nEventId}; + CPeerlessExtendedEventDB peerlessEventDBItem; + if (bettingsViewCache.events->Read(eventKey, peerlessEventDBItem)) { + event = CBetEvent(BetEventType::PEERLESS, nEventId); + return true; + } + CFieldEventDB fieldEventDBItem; + if (bettingsViewCache.fieldEvents->Read(eventKey, fieldEventDBItem)) { + event = CBetEvent(BetEventType::FIELD, nEventId); + return true; + } + return false; +} + +bool CBetEvent::IsOpen(const CBettingsView& bettingsViewCache, uint32_t nTime) { + switch (type) + { + case BetEventType::PEERLESS: + { + if (bettingsViewCache.results->Exists(ResultKey{nEventId})) { + return error("result for event already posted"); + } + EventKey eventKey{nEventId}; + CPeerlessExtendedEventDB eventDBItem; + if (!bettingsViewCache.events->Read(eventKey, eventDBItem)) { + return false; + } + if (eventDBItem.nStartTime >= nTime) { + return error("past event start time"); + } + break; + } + case BetEventType::FIELD: + { + if (bettingsViewCache.fieldResults->Exists(FieldResultKey{nEventId})) { + return error("result for event already posted"); + } + EventKey eventKey{nEventId}; + CFieldEventDB eventDBItem; + if (!bettingsViewCache.fieldEvents->Read(eventKey, eventDBItem)) { + return false; + } + if (eventDBItem.nStartTime >= nTime) { + return error("past event start time"); + } + break; + } + default: + return false; + } + return true; +} \ No newline at end of file diff --git a/src/betting/events.h b/src/betting/events.h new file mode 100644 index 0000000000000..9b7b371cc8de9 --- /dev/null +++ b/src/betting/events.h @@ -0,0 +1,51 @@ +// Copyright (c) 2023 The Wagerr developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef WAGERR_BET_EVENT_H +#define WAGERR_BET_EVENT_H + +#include +#include + +#include +#include + +class CBettingsView; + +enum class BetEventType : uint8_t { + UNKNOWN = 0x00, + PEERLESS = 0x01, + FIELD = 0x02, + LAST = FIELD +}; +template<> struct is_serializable_enum : std::true_type {}; + +constexpr std::array(BetEventType::LAST)+1> makeBetEventTypeDefs() { + std::array(BetEventType::LAST)+1> arr = { + "UNKNOWN", + "PEERLESS", + "FIELD" + }; + return arr; +} +[[maybe_unused]] static constexpr auto betEventTypeDefs = makeBetEventTypeDefs(); + +class CBetEvent +{ +public: + BetEventType type; + uint32_t nEventId; + + CBetEvent() : type(BetEventType::UNKNOWN), nEventId(0) {}; + CBetEvent(const CPeerlessEventTx eventTx) : type(BetEventType::PEERLESS), nEventId(eventTx.nEventId) { }; + CBetEvent(const CFieldEventTx eventTx) : type(BetEventType::FIELD), nEventId(eventTx.nEventId) { }; + CBetEvent(const BetEventType type, const uint32_t nEventId) : type(type), nEventId(nEventId) { }; + + bool IsOpen(const CBettingsView& bettingsViewCache, uint32_t nTime); + +}; + +bool CreateBetEventFromDB(const CBettingsView& bettingsViewCache, const uint32_t nEventId, CBetEvent& event); + +#endif // WAGERR_BET_EVENT_H \ No newline at end of file diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 8eaef17467170..78114c9760517 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -20,7 +20,7 @@ #include #include -bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs) +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, const CBettingsView& bettingsViewCache, bool check_sigs) { AssertLockHeld(cs_main); @@ -48,16 +48,11 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali case TRANSACTION_GROUP_CREATION_REGULAR: return CheckGroupConfigurationTxRegular(tx, pindexPrev, state, view); case TRANSACTION_GROUP_CREATION_MGT: - if (!CheckGroupConfigurationTxMGT(tx, pindexPrev, state, view)) - { - CheckGroupConfigurationTxMGT(tx, pindexPrev, state, view); - return false; - } - return true; + return CheckGroupConfigurationTxMGT(tx, pindexPrev, state, view); case TRANSACTION_GROUP_CREATION_NFT: return CheckGroupConfigurationTxNFT(tx, pindexPrev, state, view); case TRANSACTION_GROUP_CREATION_BETTING: - return CheckGroupConfigurationTxBetting(tx, pindexPrev, state, view); + return CheckGroupConfigurationTxBetting(tx, pindexPrev, state, view, bettingsViewCache); case TRANSACTION_MNHF_SIGNAL: return pindexPrev->nHeight + 1 >= Params().GetConsensus().DIP0024Height && CheckMNHFTx(tx, pindexPrev, state); } @@ -88,6 +83,7 @@ bool ProcessSpecialTx(const CTransaction& tx, const CBlockIndex* pindex, CValida case TRANSACTION_GROUP_CREATION_REGULAR: case TRANSACTION_GROUP_CREATION_MGT: case TRANSACTION_GROUP_CREATION_NFT: + case TRANSACTION_GROUP_CREATION_BETTING: return true; // handled per block case TRANSACTION_MNHF_SIGNAL: return true; // handled per block @@ -115,6 +111,7 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) case TRANSACTION_GROUP_CREATION_REGULAR: case TRANSACTION_GROUP_CREATION_MGT: case TRANSACTION_GROUP_CREATION_NFT: + case TRANSACTION_GROUP_CREATION_BETTING: return true; // handled per block case TRANSACTION_MNHF_SIGNAL: return true; // handled per block @@ -124,7 +121,7 @@ bool UndoSpecialTx(const CTransaction& tx, const CBlockIndex* pindex) } bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq::CQuorumBlockProcessor& quorum_block_processor, - CValidationState& state, const CCoinsViewCache& view, bool fJustCheck, bool fCheckCbTxMerleRoots) + CValidationState& state, const CCoinsViewCache& view, const CBettingsView& bettingsViewCache, bool fJustCheck, bool fCheckCbTxMerleRoots) { AssertLockHeld(cs_main); @@ -137,7 +134,7 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, ll int64_t nTime1 = GetTimeMicros(); for (const auto& ptr_tx : block.vtx) { - if (!CheckSpecialTx(*ptr_tx, pindex->pprev, state, view, fCheckCbTxMerleRoots)) { + if (!CheckSpecialTx(*ptr_tx, pindex->pprev, state, view, bettingsViewCache, fCheckCbTxMerleRoots)) { // pass the state returned by the function above return false; } diff --git a/src/evo/specialtxman.h b/src/evo/specialtxman.h index 2980f6dad1f60..6bc6463c586bc 100644 --- a/src/evo/specialtxman.h +++ b/src/evo/specialtxman.h @@ -9,6 +9,7 @@ #include #include +class CBettingsView; class CBlock; class CBlockIndex; class CCoinsViewCache; @@ -16,9 +17,9 @@ class CValidationState; extern CCriticalSection cs_main; -bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, bool check_sigs) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, const CBettingsView& bettingsViewCache, bool check_sigs) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq::CQuorumBlockProcessor& quorum_block_processor, - CValidationState& state, const CCoinsViewCache& view, bool fJustCheck, bool fCheckCbTxMerleRoots) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CValidationState& state, const CCoinsViewCache& view, const CBettingsView& bettingsViewCache, bool fJustCheck, bool fCheckCbTxMerleRoots) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq::CQuorumBlockProcessor& quorum_block_processor) EXCLUSIVE_LOCKS_REQUIRED(cs_main); #endif // BITCOIN_EVO_SPECIALTXMAN_H diff --git a/src/evo/verifiable.h b/src/evo/verifiable.h index 58e8bb1567d37..8dfbb2777fd68 100644 --- a/src/evo/verifiable.h +++ b/src/evo/verifiable.h @@ -5,15 +5,16 @@ #ifndef BITCOIN_EVO_VERIFIABLE_H #define BITCOIN_EVO_VERIFIABLE_H -#include +#include #include #include +#include class CBLSPublicKey; class CBLSSignature; class CValidationState; -enum SignerType : uint8_t { +enum class SignerType : uint8_t { UNKNOWN = 0x00, MGT = 0x01, ORAT = 0x02, // unimplemented @@ -22,8 +23,8 @@ enum SignerType : uint8_t { }; template<> struct is_serializable_enum : std::true_type {}; -constexpr std::array makeSignerTypeDefs() { - std::array arr = { +constexpr std::array(SignerType::LAST)+1> makeSignerTypeDefs() { + std::array(SignerType::LAST)+1> arr = { "UNKNOWN", "MGT", "ORAT", @@ -31,7 +32,6 @@ constexpr std::array makeSignerTypeDefs() }; return arr; } - [[maybe_unused]] static constexpr auto signerTypeDefs = makeSignerTypeDefs(); class Verifiable { diff --git a/src/rpc/rpcevo.cpp b/src/rpc/rpcevo.cpp index 37efb623d30ed..964432cecf4a7 100644 --- a/src/rpc/rpcevo.cpp +++ b/src/rpc/rpcevo.cpp @@ -335,7 +335,8 @@ static std::string SignAndSendSpecialTx(const JSONRPCRequest& request, const CMu LOCK(cs_main); CValidationState state; - if (!CheckSpecialTx(CTransaction(tx), ::ChainActive().Tip(), state, ::ChainstateActive().CoinsTip(), true)) { + CBettingsView bettingsViewDummy = CBettingsView(); + if (!CheckSpecialTx(CTransaction(tx), ::ChainActive().Tip(), state, ::ChainstateActive().CoinsTip(), bettingsViewDummy, true)) { throw std::runtime_error(FormatStateMessage(state)); } } // cs_main diff --git a/src/tokens/tokengroupconfiguration.cpp b/src/tokens/tokengroupconfiguration.cpp index 56cc1d40ade4f..0a257053a4a7f 100644 --- a/src/tokens/tokengroupconfiguration.cpp +++ b/src/tokens/tokengroupconfiguration.cpp @@ -6,6 +6,7 @@ #include "tokens/tokengroupconfiguration.h" #include "tokens/tokengroupmanager.h" +#include #include #include #include @@ -328,7 +329,7 @@ bool CheckGroupConfigurationTxNFT(const CTransaction& tx, const CBlockIndex* pin return true; } -bool CheckGroupConfigurationTxBetting(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view) +bool CheckGroupConfigurationTxBetting(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, const CBettingsView& bettingsView) { if (!CheckGroupConfigurationTxBase(tx, pindexPrev, state, view)) { return false; @@ -342,11 +343,13 @@ bool CheckGroupConfigurationTxBetting(const CTransaction& tx, const CBlockIndex* if (!GetTxPayload(tx, tgDesc)) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "grp-bad-protx-payload"); } - /* - if (!tgDesc.vchData.size() > MAX_TX_NFT_DATA) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "grp-bad-data"); + CBetEvent event; + if (!CreateBetEventFromDB(bettingsView, tgDesc.nEventId, event)) { + return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "grp-bad-event"); + }; + if (!event.IsOpen(bettingsView, pindexPrev->nTime)) { + return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "grp-bad-event"); } - */ if (tgDesc.nVersion == 0 || tgDesc.nVersion > CTokenGroupDescriptionBetting::CURRENT_VERSION) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "grp-bad-version"); diff --git a/src/tokens/tokengroupconfiguration.h b/src/tokens/tokengroupconfiguration.h index 47ace9e4cdacf..ce57c288f61b9 100644 --- a/src/tokens/tokengroupconfiguration.h +++ b/src/tokens/tokengroupconfiguration.h @@ -13,6 +13,7 @@ #include class CBlockIndex; +class CBettingsView; class CTokenGroupStatus { @@ -96,6 +97,6 @@ bool CreateTokenGroup(const CTransactionRef tx, const uint256& blockHash, CToken bool CheckGroupConfigurationTxRegular(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view); bool CheckGroupConfigurationTxMGT(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view); bool CheckGroupConfigurationTxNFT(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view); -bool CheckGroupConfigurationTxBetting(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view); +bool CheckGroupConfigurationTxBetting(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state, const CCoinsViewCache& view, const CBettingsView& bettingsView); #endif diff --git a/src/validation.cpp b/src/validation.cpp index eaf2c27af081e..4a161e84037de 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -799,7 +799,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // DoS scoring a node for non-critical errors, e.g. duplicate keys because a TX is received that was already // mined // NOTE: we use UTXO here and do NOT allow mempool txes as masternode collaterals - if (!CheckSpecialTx(tx, ::ChainActive().Tip(), state, ::ChainstateActive().CoinsTip(), true)) + if (!CheckSpecialTx(tx, ::ChainActive().Tip(), state, ::ChainstateActive().CoinsTip(), bettingsViewCache, true)) return false; if (pool.existsProviderTxConflict(tx)) { @@ -2247,7 +2247,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl bool fV18Active_context = pindex->nHeight >= Params().GetConsensus().V18DeploymentHeight; // MUST process special txes before updating UTXO to ensure consistency between mempool and block processing - if (!ProcessSpecialTxsInBlock(block, pindex, *m_quorum_block_processor, state, view, fJustCheck, fScriptChecks)) { + if (!ProcessSpecialTxsInBlock(block, pindex, *m_quorum_block_processor, state, view, bettingsViewCache, fJustCheck, fScriptChecks)) { return error("ConnectBlock(WAGERR): ProcessSpecialTxsInBlock for block %s failed with %s", pindex->GetBlockHash().ToString(), FormatStateMessage(state)); } @@ -5025,7 +5025,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, } /** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */ -bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) +bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, CBettingsView& bettingsViewCache, const CChainParams& params) { assert(m_quorum_block_processor); @@ -5037,7 +5037,7 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i // MUST process special txes before updating UTXO to ensure consistency between mempool and block processing CValidationState state; - if (!ProcessSpecialTxsInBlock(block, pindex, *m_quorum_block_processor, state, inputs, false /*fJustCheck*/, false /*fScriptChecks*/)) { + if (!ProcessSpecialTxsInBlock(block, pindex, *m_quorum_block_processor, state, inputs, bettingsViewCache, false /*fJustCheck*/, false /*fScriptChecks*/)) { return error("RollforwardBlock(WAGERR): ProcessSpecialTxsInBlock for block %s failed with %s", pindex->GetBlockHash().ToString(), FormatStateMessage(state)); } @@ -5120,7 +5120,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params) const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight); LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight); uiInterface.ShowProgress(_("Replaying blocks...").translated, (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false); - if (!RollforwardBlock(pindex, cache, params)) return false; + if (!RollforwardBlock(pindex, cache, bettingsViewCache, params)) return false; } cache.SetBestBlock(pindexNew->GetBlockHash()); diff --git a/src/validation.h b/src/validation.h index 005388f959f85..a1c72502a8dde 100644 --- a/src/validation.h +++ b/src/validation.h @@ -736,7 +736,7 @@ class CChainState void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const FlatFilePos& pos) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& inputs, CBettingsView& bettingsViewCache, const CChainParams& params) EXCLUSIVE_LOCKS_REQUIRED(cs_main); //! Mark a block as conflicting bool MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);