diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 00a95a1185a..bdaab4e682b 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -121,6 +121,7 @@ class CMainParams : public CChainParams { consensus.ClarkeQuayHeight = 595738; consensus.DakotaHeight = 678000; // 1st March 2021 consensus.DakotaCrescentHeight = 733000; // 25th March 2021 + consensus.EunosHeight = std::numeric_limits::max(); consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -305,6 +306,7 @@ class CTestNetParams : public CChainParams { consensus.ClarkeQuayHeight = 155000; consensus.DakotaHeight = 220680; consensus.DakotaCrescentHeight = 287700; + consensus.EunosHeight = std::numeric_limits::max(); consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -454,6 +456,7 @@ class CDevNetParams : public CChainParams { consensus.ClarkeQuayHeight = 0; consensus.DakotaHeight = 0; consensus.DakotaCrescentHeight = 0; + consensus.EunosHeight = 0; consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.pos.nTargetTimespan = 5 * 60; // 5 min == 10 blocks @@ -595,6 +598,7 @@ class CRegTestParams : public CChainParams { consensus.ClarkeQuayHeight = 10000000; consensus.DakotaHeight = 10000000; consensus.DakotaCrescentHeight = 10000000; + consensus.EunosHeight = 10000000; consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks diff --git a/src/consensus/params.h b/src/consensus/params.h index 279a3548fa8..6b12005096e 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -82,6 +82,8 @@ struct Params { int DakotaHeight; /** Fifth major fork **/ int DakotaCrescentHeight; + /** Sixth major fork **/ + int EunosHeight; /** Foundation share after AMK, normalized to COIN = 100% */ CAmount foundationShareDFIP1; diff --git a/src/masternodes/anchors.cpp b/src/masternodes/anchors.cpp index 79f09444527..22f82970e45 100644 --- a/src/masternodes/anchors.cpp +++ b/src/masternodes/anchors.cpp @@ -26,6 +26,7 @@ std::unique_ptr panchorAwaitingConfirms; static const char DB_ANCHORS = 'A'; static const char DB_PENDING = 'p'; +static const char DB_BITCOININDEX = 'Z'; // Bitcoin height to blockhash table template bool CheckSigs(uint256 const & sigHash, TContainer const & sigs, std::set const & keys) @@ -526,7 +527,7 @@ void CAnchorIndex::CheckPendingAnchors() { AssertLockHeld(cs_main); - std::set anchorsPending(OrderPendingAnchors); + spv::PendingSet anchorsPending(spv::PendingOrder); ForEachPending([&anchorsPending](uint256 const &, AnchorRec & rec) { anchorsPending.insert(rec); }); @@ -546,11 +547,12 @@ void CAnchorIndex::CheckPendingAnchors() continue; } - uint32_t timestamp = spv::pspv->ReadTxTimestamp(rec.txHash); - auto blockHeight = spv::pspv->ReadTxBlockHeight(rec.txHash); + const auto timestamp = spv::pspv->ReadTxTimestamp(rec.txHash); + const auto blockHeight = spv::pspv->ReadTxBlockHeight(rec.txHash); + const auto blockHash = panchors->ReadBlockHash(rec.btcHeight); // Do not delete, TX time still pending. If block height is set to max we cannot trust the timestamp. - if (timestamp == 0 || blockHeight == std::numeric_limits::max()) { + if (timestamp == 0 || blockHeight == std::numeric_limits::max() || blockHash == uint256()) { continue; } @@ -652,14 +654,7 @@ CAnchorIndex::AnchorRec const * BestOfTwo(CAnchorIndex::AnchorRec const * a1, CA if (a2 == nullptr) return a1; - if (a1->anchor.height > a2->anchor.height) - return a1; - else if (a1->anchor.height < a2->anchor.height) - return a2; - // if heights are equal, return anchor with less btc tx hash - else if (a1->txHash < a2->txHash) - return a1; - return a2; + return spv::PendingOrder(*a1, *a2) ? a1 : a2; } /// @returns true if top active anchor has been changed @@ -754,6 +749,19 @@ bool CAnchorIndex::DeletePendingByBtcTx(uint256 const & btcTxHash) return false; } +bool CAnchorIndex::WriteBlock(const uint32_t height, const uint256& blockHash) +{ + // Store Bitcoin block index + return db->Write(std::make_pair(DB_BITCOININDEX, height), blockHash); +} + +uint256 CAnchorIndex::ReadBlockHash(const uint32_t& height) +{ + uint256 blockHash; + db->Read(std::make_pair(DB_BITCOININDEX, height), blockHash); + return blockHash; +} + void CAnchorIndex::ForEachPending(std::function callback) { AssertLockHeld(cs_main); @@ -1152,3 +1160,30 @@ bool GetAnchorEmbeddedData(const CKeyID& data, uint64_t& anchorCreationHeight, s return true; } + +namespace spv +{ +const PendingOrderType PendingOrder = PendingOrderType([](const CAnchorIndex::AnchorRec& a, const CAnchorIndex::AnchorRec& b) +{ + if (a.btcHeight == b.btcHeight) + { + if (a.anchor.height == b.anchor.height) + { + if (a.anchor.height >= static_cast(Params().GetConsensus().EunosHeight)) + { + const auto blockHash = panchors->ReadBlockHash(a.btcHeight); + auto aHash = Hash(a.txHash.begin(), a.txHash.end(), blockHash.begin(), blockHash.end()); + auto bHash = Hash(b.txHash.begin(), b.txHash.end(), blockHash.begin(), blockHash.end()); + return aHash < bHash; + } + + return a.txHash < b.txHash; + } + + // Higher DeFi comes first + return a.anchor.height > b.anchor.height; + } + + return a.btcHeight < b.btcHeight; +}); +} diff --git a/src/masternodes/anchors.h b/src/masternodes/anchors.h index 38f44adf33d..f2313341d49 100644 --- a/src/masternodes/anchors.h +++ b/src/masternodes/anchors.h @@ -256,6 +256,10 @@ class CAnchorIndex // Used to apply chain context to post-fork anchors which get added to pending. void CheckPendingAnchors(); + // Store and read Bitcoin block hash by height, used in BestOfTwo calculation. + bool WriteBlock(const uint32_t height, const uint256& blockHash); + uint256 ReadBlockHash(const uint32_t& height); + private: AnchorIndexImpl anchors; AnchorRec const * top = nullptr; @@ -501,20 +505,6 @@ bool ContextualValidateAnchor(const CAnchorData& anchor, CBlockIndex &anchorBloc // Get info from data embedded into CAnchorData::nextTeam bool GetAnchorEmbeddedData(const CKeyID& data, uint64_t& anchorCreationHeight, std::shared_ptr>& prefix); -// Comparator to organise by Bitcoin height, anchor height or TX hash -const auto OrderPendingAnchors = [](const CAnchorIndex::AnchorRec& a, const CAnchorIndex::AnchorRec& b) { - if (a.btcHeight == b.btcHeight) { - if (a.anchor.height == b.anchor.height) { - return a.txHash < b.txHash; - } - - // Higher DeFi height wins - return a.anchor.height > b.anchor.height; - } - - return a.btcHeight < b.btcHeight; -}; - // Selects "best" of two anchors at the equal btc height (prevs must be checked before) CAnchorIndex::AnchorRec const* BestOfTwo(CAnchorIndex::AnchorRec const* a1, CAnchorIndex::AnchorRec const* a2); @@ -523,4 +513,12 @@ extern std::unique_ptr panchorauths; extern std::unique_ptr panchors; extern std::unique_ptr panchorAwaitingConfirms; +namespace spv +{ +// Define comparator and set to hold pending anchors +using PendingOrderType = std::function; +using PendingSet = std::set; +extern const PendingOrderType PendingOrder; +} + #endif // DEFI_MASTERNODES_ANCHORS_H diff --git a/src/spv/bitcoin/BRPeerManager.cpp b/src/spv/bitcoin/BRPeerManager.cpp index ca15bf533c2..5ef77e1622e 100644 --- a/src/spv/bitcoin/BRPeerManager.cpp +++ b/src/spv/bitcoin/BRPeerManager.cpp @@ -515,7 +515,7 @@ static void _requestUnrelayedTxGetdataDone(void *info, int success) } else if (! isPublishing && _BRTxPeerListCount(manager->txRelays, hash) < manager->maxConnectCount) { // set timestamp 0 to mark as unverified - BRWalletUpdateTransactions(manager->wallet, &hash, 1, TX_UNCONFIRMED, 0); + BRWalletUpdateTransactions(manager->wallet, &hash, 1, TX_UNCONFIRMED, 0, UINT256_ZERO); } } } @@ -1053,7 +1053,7 @@ static void _peerRelayedTx(void *info, BRTransaction *tx) // set timestamp when tx is verified if (tx && relayCount >= manager->maxConnectCount && tx->blockHeight == TX_UNCONFIRMED && tx->timestamp == 0) { - BRWalletUpdateTransactions(manager->wallet, &tx->txHash, 1, TX_UNCONFIRMED, (uint32_t)time(NULL)); + BRWalletUpdateTransactions(manager->wallet, &tx->txHash, 1, TX_UNCONFIRMED, (uint32_t)time(NULL), UINT256_ZERO); } manager->lock.unlock(); @@ -1107,7 +1107,7 @@ static void _peerHasTx(void *info, UInt256 txHash) // set timestamp when tx is verified if (relayCount >= manager->maxConnectCount && tx && tx->blockHeight == TX_UNCONFIRMED && tx->timestamp == 0) { - BRWalletUpdateTransactions(manager->wallet, &txHash, 1, TX_UNCONFIRMED, (uint32_t)time(NULL)); + BRWalletUpdateTransactions(manager->wallet, &txHash, 1, TX_UNCONFIRMED, (uint32_t)time(NULL), UINT256_ZERO); } _BRTxPeerListRemovePeer(manager->txRequests, txHash, peer); @@ -1134,7 +1134,7 @@ static void _peerRejectedTx(void *info, UInt256 txHash, uint8_t code) if (tx) { if (_BRTxPeerListRemovePeer(manager->txRelays, txHash, peer) && tx->blockHeight == TX_UNCONFIRMED) { // set timestamp 0 to mark tx as unverified - BRWalletUpdateTransactions(manager->wallet, &txHash, 1, TX_UNCONFIRMED, 0); + BRWalletUpdateTransactions(manager->wallet, &txHash, 1, TX_UNCONFIRMED, 0, UINT256_ZERO); } // if we get rejected for any reason other than double-spend, the peer is likely misconfigured @@ -1305,7 +1305,7 @@ static void _peerRelayedBlock(void *info, BRMerkleBlock *block) BRSetAdd(manager->blocks, block); manager->lastBlock = block; - if (txCount > 0) BRWalletUpdateTransactions(manager->wallet, txHashes, txCount, block->height, txTime); + if (txCount > 0) BRWalletUpdateTransactions(manager->wallet, txHashes, txCount, block->height, txTime, block->blockHash); if (manager->downloadPeer) BRPeerSetCurrentBlockHeight(manager->downloadPeer, block->height); if (block->height < manager->estimatedHeight && peer == manager->downloadPeer) { @@ -1332,7 +1332,7 @@ static void _peerRelayedBlock(void *info, BRMerkleBlock *block) assert (NULL != b); if (BRMerkleBlockEq(b, block)) { // if it's not on a fork, set block heights for its transactions - if (txCount > 0) BRWalletUpdateTransactions(manager->wallet, txHashes, txCount, block->height, txTime); + if (txCount > 0) BRWalletUpdateTransactions(manager->wallet, txHashes, txCount, block->height, txTime, block->blockHash); if (block->height == manager->lastBlock->height) manager->lastBlock = block; } @@ -1392,7 +1392,7 @@ static void _peerRelayedBlock(void *info, BRMerkleBlock *block) count = BRMerkleBlockTxHashes(b, txHashes, count); b = (BRMerkleBlock *)BRSetGet(manager->blocks, &b->prevBlock); if (b) timestamp = timestamp/2 + b->timestamp/2; - if (count > 0) BRWalletUpdateTransactions(manager->wallet, txHashes, count, height, timestamp); + if (count > 0) BRWalletUpdateTransactions(manager->wallet, txHashes, count, height, timestamp, b->blockHash); } manager->lastBlock = block; diff --git a/src/spv/bitcoin/BRWallet.cpp b/src/spv/bitcoin/BRWallet.cpp index ace12972c1a..58f45aa5394 100644 --- a/src/spv/bitcoin/BRWallet.cpp +++ b/src/spv/bitcoin/BRWallet.cpp @@ -95,7 +95,7 @@ struct BRWalletStruct { void *callbackInfo; void (*balanceChanged)(void *info, uint64_t balance); void (*txAdded)(void *info, BRTransaction *tx); - void (*txUpdated)(void *info, const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, uint32_t timestamp); + void (*txUpdated)(void *info, const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, uint32_t timestamp, const UInt256& blockHash); void (*txDeleted)(void *info, UInt256 txHash, int notifyUser, int recommendRescan); boost::mutex lock; }; @@ -521,7 +521,7 @@ BRWallet *BRWalletNew(BRTransaction *transactions[], size_t txCount, BRMasterPub // info is a void pointer that will be passed along with each callback call // void balanceChanged(void *, uint64_t) - called when the wallet balance changes // void txAdded(void *, BRTransaction *) - called when transaction is added to the wallet -// void txUpdated(void *, const UInt256[], size_t, uint32_t, uint32_t) +// void txUpdated(void *, const UInt256[], size_t, uint32_t, uint32_t, const UInt256&) // - called when the blockHeight or timestamp of previously added transactions are updated // void txDeleted(void *, UInt256) - called when a previously added transaction is removed from the wallet // NOTE: if a transaction is deleted, and BRWalletAmountSentByTx() is greater than 0, recommend the user do a rescan @@ -529,7 +529,7 @@ void BRWalletSetCallbacks(BRWallet *wallet, void *info, void (*balanceChanged)(void *info, uint64_t balance), void (*txAdded)(void *info, BRTransaction *tx), void (*txUpdated)(void *info, const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, - uint32_t timestamp), + uint32_t timestamp, const UInt256& blockHash), void (*txDeleted)(void *info, UInt256 txHash, int notifyUser, int recommendRescan)) { assert(wallet != NULL); @@ -1319,7 +1319,7 @@ int BRWalletTransactionIsVerified(BRWallet *wallet, const BRTransaction *tx) // set the block heights and timestamps for the given transactions // use height TX_UNCONFIRMED and timestamp 0 to indicate a tx should remain marked as unverified (not 0-conf safe) void BRWalletUpdateTransactions(BRWallet *wallet, const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, - uint32_t timestamp) + uint32_t timestamp, const UInt256& blockHash) { BRTransaction *tx; UInt256 hashes[txCount]; @@ -1356,7 +1356,7 @@ void BRWalletUpdateTransactions(BRWallet *wallet, const UInt256 txHashes[], size if (needsUpdate) _BRWalletUpdateBalance(wallet); wallet->lock.unlock(); - if (j > 0 && wallet->txUpdated) wallet->txUpdated(wallet->callbackInfo, hashes, j, blockHeight, timestamp); + if (j > 0 && wallet->txUpdated) wallet->txUpdated(wallet->callbackInfo, hashes, j, blockHeight, timestamp, blockHash); } // marks all transactions confirmed after blockHeight as unconfirmed (useful for chain re-orgs) @@ -1380,7 +1380,7 @@ void BRWalletSetTxUnconfirmedAfter(BRWallet *wallet, uint32_t blockHeight) if (count > 0) _BRWalletUpdateBalance(wallet); wallet->lock.unlock(); - if (count > 0 && wallet->txUpdated) wallet->txUpdated(wallet->callbackInfo, hashes, count, TX_UNCONFIRMED, 0); + if (count > 0 && wallet->txUpdated) wallet->txUpdated(wallet->callbackInfo, hashes, count, TX_UNCONFIRMED, 0, UINT256_ZERO); } // returns the amount received by the wallet from the transaction (total outputs to change and/or receive addresses) diff --git a/src/spv/bitcoin/BRWallet.h b/src/spv/bitcoin/BRWallet.h index 31028efd959..497f10fd10e 100644 --- a/src/spv/bitcoin/BRWallet.h +++ b/src/spv/bitcoin/BRWallet.h @@ -77,14 +77,14 @@ BRWallet *BRWalletNew(BRTransaction *transactions[], size_t txCount, BRMasterPub // info is a void pointer that will be passed along with each callback call // void balanceChanged(void *, uint64_t) - called when the wallet balance changes // void txAdded(void *, BRTransaction *) - called when transaction is added to the wallet -// void txUpdated(void *, const UInt256[], size_t, uint32_t, uint32_t) +// void txUpdated(void *, const UInt256[], size_t, uint32_t, uint32_t, const UInt256&) // - called when the blockHeight or timestamp of previously added transactions are updated // void txDeleted(void *, UInt256, int, int) - called when a previously added transaction is removed from the wallet void BRWalletSetCallbacks(BRWallet *wallet, void *info, void (*balanceChanged)(void *info, uint64_t balance), void (*txAdded)(void *info, BRTransaction *tx), void (*txUpdated)(void *info, const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, - uint32_t timestamp), + uint32_t timestamp, const UInt256& blockHash), void (*txDeleted)(void *info, UInt256 txHash, int notifyUser, int recommendRescan)); // wallets are composed of chains of addresses @@ -182,7 +182,7 @@ int BRWalletTransactionIsVerified(BRWallet *wallet, const BRTransaction *tx); // set the block heights and timestamps for the given transactions // use height TX_UNCONFIRMED and timestamp 0 to indicate a tx should remain marked as unverified (not 0-conf safe) void BRWalletUpdateTransactions(BRWallet *wallet, const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, - uint32_t timestamp); + uint32_t timestamp, const UInt256 &blockHash); // marks all transactions confirmed after blockHeight as unconfirmed (useful for chain re-orgs) void BRWalletSetTxUnconfirmedAfter(BRWallet *wallet, uint32_t blockHeight); diff --git a/src/spv/spv_rpc.cpp b/src/spv/spv_rpc.cpp index 4efa3f4d6d0..22bd2905edc 100644 --- a/src/spv/spv_rpc.cpp +++ b/src/spv/spv_rpc.cpp @@ -215,6 +215,9 @@ UniValue spv_createanchortemplate(const JSONRPCRequest& request) }, }.Check(request); + if (!spv::pspv) { + throw JSONRPCError(RPC_INVALID_REQUEST, "spv module disabled"); + } if (pwallet->chain().isInitialBlockDownload()) { throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot create anchor while still in Initial Block Download"); @@ -391,6 +394,29 @@ UniValue spv_gettxconfirmations(const JSONRPCRequest& request) return UniValue(panchors->GetAnchorConfirmations(txHash)); } +// Populate anchors in listanchors, listanchorspending and listanchorsunrewarded +void AnchorToUniv(const CAnchorIndex::AnchorRec& rec, UniValue& anchor) +{ + CTxDestination rewardDest = rec.anchor.rewardKeyType == 1 ? CTxDestination(PKHash(rec.anchor.rewardKeyID)) : CTxDestination(WitnessV0KeyHash(rec.anchor.rewardKeyID)); + anchor.pushKV("btcBlockHeight", static_cast(rec.btcHeight)); + anchor.pushKV("btcBlockHash", panchors->ReadBlockHash(rec.btcHeight).ToString()); + anchor.pushKV("btcTxHash", rec.txHash.ToString()); + anchor.pushKV("previousAnchor", rec.anchor.previousAnchor.ToString()); + anchor.pushKV("defiBlockHeight", static_cast(rec.anchor.height)); + anchor.pushKV("defiBlockHash", rec.anchor.blockHash.ToString()); + anchor.pushKV("rewardAddress", EncodeDestination(rewardDest)); + anchor.pushKV("confirmations", panchors->GetAnchorConfirmations(&rec)); + anchor.pushKV("signatures", static_cast(rec.anchor.sigs.size())); + + // If post-fork show creation height + uint64_t anchorCreationHeight{0}; + std::shared_ptr> prefix; + if (rec.anchor.nextTeam.size() == 1 && GetAnchorEmbeddedData(*rec.anchor.nextTeam.begin(), anchorCreationHeight, prefix)) + { + anchor.pushKV("anchorCreationHeight", static_cast(anchorCreationHeight)); + } +} + UniValue spv_listanchors(const JSONRPCRequest& request) { CWallet* const pwallet = GetWallet(request); @@ -439,25 +465,9 @@ UniValue spv_listanchors(const JSONRPCRequest& request) if ( (minBtcHeight >= 0 && (int)rec.btcHeight < minBtcHeight) || (maxConfs >= 0 && confs > maxConfs) ) return false; // break - - CTxDestination rewardDest = rec.anchor.rewardKeyType == 1 ? CTxDestination(PKHash(rec.anchor.rewardKeyID)) : CTxDestination(WitnessV0KeyHash(rec.anchor.rewardKeyID)); UniValue anchor(UniValue::VOBJ); - anchor.pushKV("btcBlockHeight", static_cast(rec.btcHeight)); - anchor.pushKV("btcTxHash", rec.txHash.ToString()); - anchor.pushKV("previousAnchor", rec.anchor.previousAnchor.ToString()); - anchor.pushKV("defiBlockHeight", static_cast(rec.anchor.height)); - anchor.pushKV("defiBlockHash", rec.anchor.blockHash.ToString()); - anchor.pushKV("rewardAddress", EncodeDestination(rewardDest)); - anchor.pushKV("confirmations", panchors->GetAnchorConfirmations(&rec)); - - // If post-fork show creation height - uint64_t anchorCreationHeight{0}; - std::shared_ptr> prefix; - if (rec.anchor.nextTeam.size() == 1 && GetAnchorEmbeddedData(*rec.anchor.nextTeam.begin(), anchorCreationHeight, prefix)) { - anchor.pushKV("anchorCreationHeight", static_cast(anchorCreationHeight)); - } + AnchorToUniv(rec, anchor); - anchor.pushKV("signatures", static_cast(rec.anchor.sigs.size())); bool const isActive = cur && cur->txHash == rec.txHash; anchor.pushKV("active", isActive); if (isActive) { @@ -495,25 +505,10 @@ UniValue spv_listanchorspending(const JSONRPCRequest& request) auto locked_chain = pwallet->chain().lock(); UniValue result(UniValue::VARR); - panchors->ForEachPending([&result](uint256 const &, CAnchorIndex::AnchorRec & rec) { - - CTxDestination rewardDest = rec.anchor.rewardKeyType == 1 ? CTxDestination(PKHash(rec.anchor.rewardKeyID)) : CTxDestination(WitnessV0KeyHash(rec.anchor.rewardKeyID)); + panchors->ForEachPending([&result](uint256 const &, CAnchorIndex::AnchorRec & rec) + { UniValue anchor(UniValue::VOBJ); - anchor.pushKV("btcBlockHeight", static_cast(rec.btcHeight)); - anchor.pushKV("btcTxHash", rec.txHash.ToString()); - anchor.pushKV("previousAnchor", rec.anchor.previousAnchor.ToString()); - anchor.pushKV("defiBlockHeight", static_cast(rec.anchor.height)); - anchor.pushKV("defiBlockHash", rec.anchor.blockHash.ToString()); - anchor.pushKV("rewardAddress", EncodeDestination(rewardDest)); - anchor.pushKV("confirmations", panchors->GetAnchorConfirmations(&rec)); - anchor.pushKV("signatures", static_cast(rec.anchor.sigs.size())); - - // If post-fork show creation height - uint64_t anchorCreationHeight{0}; - std::shared_ptr> prefix; - if (rec.anchor.nextTeam.size() == 1 && GetAnchorEmbeddedData(*rec.anchor.nextTeam.begin(), anchorCreationHeight, prefix)) { - anchor.pushKV("anchorCreationHeight", static_cast(anchorCreationHeight)); - } + AnchorToUniv(rec, anchor); result.push_back(anchor); return true; @@ -753,11 +748,7 @@ UniValue spv_listanchorsunrewarded(const JSONRPCRequest& request) for (auto const & btcTxHash : unrewarded) { auto rec = panchors->GetAnchorByTx(btcTxHash); UniValue item(UniValue::VOBJ); - item.pushKV("previousAnchor", rec->anchor.previousAnchor.ToString()); - item.pushKV("dfiHeight", static_cast(rec->anchor.height)); - item.pushKV("dfiHash", rec->anchor.blockHash.ToString()); - item.pushKV("btcHeight", static_cast(rec->btcHeight)); - item.pushKV("btcHash", btcTxHash.ToString()); + AnchorToUniv(*rec, item); result.push_back(item); } diff --git a/src/spv/spv_wrapper.cpp b/src/spv/spv_wrapper.cpp index 12495b87969..31a7a3eacf3 100644 --- a/src/spv/spv_wrapper.cpp +++ b/src/spv/spv_wrapper.cpp @@ -77,11 +77,11 @@ void txAdded(void *info, BRTransaction *tx) static_cast(info)->OnTxAdded(tx); } -void txUpdated(void *info, const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, uint32_t timestamp) +void txUpdated(void *info, const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, uint32_t timestamp, const UInt256& blockHash) { /// @attention called under spv manager lock!!! if (ShutdownRequested()) return; - static_cast(info)->OnTxUpdated(txHashes, txCount, blockHeight, timestamp); + static_cast(info)->OnTxUpdated(txHashes, txCount, blockHeight, timestamp, blockHash); } void txDeleted(void *info, UInt256 txHash, int notifyUser, int recommendRescan) @@ -408,12 +408,13 @@ void CSpvWrapper::OnTxAdded(BRTransaction * tx) } } -void CSpvWrapper::OnTxUpdated(const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, uint32_t timestamp) +void CSpvWrapper::OnTxUpdated(const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, uint32_t timestamp, const UInt256& blockHash) { /// @attention called under spv manager lock!!! for (size_t i = 0; i < txCount; ++i) { uint256 const txHash{to_uint256(txHashes[i])}; - UpdateTx(txHash, blockHeight, timestamp); + const uint256 btcHash{to_uint256(blockHash)}; + UpdateTx(txHash, blockHeight, timestamp, btcHash); LogPrint(BCLog::SPV, "tx updated, hash: %s, blockHeight: %d, timestamp: %d\n", txHash.ToString(), blockHeight, timestamp); LOCK(cs_main); @@ -510,7 +511,7 @@ void CSpvWrapper::WriteTx(const BRTransaction *tx) db->Write(std::make_pair(DB_SPVTXS, to_uint256(tx->txHash)), std::make_pair(buf, std::make_pair(tx->blockHeight, tx->timestamp)) ); } -void CSpvWrapper::UpdateTx(uint256 const & hash, uint32_t blockHeight, uint32_t timestamp) +void CSpvWrapper::UpdateTx(uint256 const & hash, uint32_t blockHeight, uint32_t timestamp, const uint256& blockHash) { std::pair const key{std::make_pair(DB_SPVTXS, hash)}; db_tx_rec txrec; @@ -519,6 +520,9 @@ void CSpvWrapper::UpdateTx(uint256 const & hash, uint32_t blockHeight, uint32_t txrec.second.second = timestamp; db->Write(key, txrec); } + + // Store block index in anchors + panchors->WriteBlock(blockHeight, blockHash); } uint32_t CSpvWrapper::ReadTxTimestamp(uint256 const & hash) @@ -1323,7 +1327,9 @@ void CFakeSpvWrapper::OnSendRawTx(BRTransaction *tx, std::promise * promise // Use realistic time tx->timestamp = GetTime(); - OnTxUpdated(&tx->txHash, 1, lastBlockHeight, GetTime() + 1000); + + // UInt256 cannot be null or anchor will remain in pending assumed unconfirmed + OnTxUpdated(&tx->txHash, 1, lastBlockHeight, GetTime() + 1000, UInt256{ .u64 = { 1, 1, 1, 1 } }); if (promise) { promise->set_value(0); diff --git a/src/spv/spv_wrapper.h b/src/spv/spv_wrapper.h index 01c6cdd0333..015729260ae 100644 --- a/src/spv/spv_wrapper.h +++ b/src/spv/spv_wrapper.h @@ -118,7 +118,7 @@ class CSpvWrapper /// Wallet callbacks void OnBalanceChanged(uint64_t balance); void OnTxAdded(BRTransaction *tx); - void OnTxUpdated(const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, uint32_t timestamp); + void OnTxUpdated(const UInt256 txHashes[], size_t txCount, uint32_t blockHeight, uint32_t timestamp, const UInt256 &blockHash); void OnTxDeleted(UInt256 txHash, int notifyUser, int recommendRescan); /// Peermanager callbacks void OnSyncStarted(); @@ -226,7 +226,7 @@ class CSpvWrapper void WriteBlock(BRMerkleBlock const * block); void WriteTx(BRTransaction const * tx); - void UpdateTx(uint256 const & hash, uint32_t blockHeight, uint32_t timestamp); + void UpdateTx(uint256 const & hash, uint32_t blockHeight, uint32_t timestamp, const uint256 &blockHash); void EraseTx(uint256 const & hash); }; diff --git a/src/test/anchor_tests.cpp b/src/test/anchor_tests.cpp index 3cb85bc49c3..8d1152a82bc 100644 --- a/src/test/anchor_tests.cpp +++ b/src/test/anchor_tests.cpp @@ -33,16 +33,16 @@ BOOST_AUTO_TEST_CASE(anchor_order_logic) anchorTwo.btcHeight = 200; // Lowest Bitcoin height wins - BOOST_CHECK(OrderPendingAnchors(anchorOne, anchorTwo) == true); - BOOST_CHECK(OrderPendingAnchors(anchorTwo, anchorOne) == false); + BOOST_CHECK(spv::PendingOrder(anchorOne, anchorTwo) == true); + BOOST_CHECK(spv::PendingOrder(anchorTwo, anchorOne) == false); anchorOne.btcHeight = anchorTwo.btcHeight; anchorOne.anchor.height = 100; anchorTwo.anchor.height = 200; // Heighest DeFi height wins - BOOST_CHECK(OrderPendingAnchors(anchorOne, anchorTwo) == false); - BOOST_CHECK(OrderPendingAnchors(anchorTwo, anchorOne) == true); + BOOST_CHECK(spv::PendingOrder(anchorOne, anchorTwo) == false); + BOOST_CHECK(spv::PendingOrder(anchorTwo, anchorOne) == true); BOOST_CHECK(BestOfTwo(&anchorOne, &anchorTwo)->anchor.height == 200); BOOST_CHECK(BestOfTwo(&anchorTwo, &anchorOne)->anchor.height == 200); @@ -51,10 +51,54 @@ BOOST_AUTO_TEST_CASE(anchor_order_logic) anchorTwo.txHash = uint256S("852bb89808af5a5487d4afed23b4ec3c4186ec8101ff9e7c73a038c9a2c436d9"); // Lowest hash wins - BOOST_CHECK(OrderPendingAnchors(anchorOne, anchorTwo) == true); - BOOST_CHECK(OrderPendingAnchors(anchorTwo, anchorOne) == false); + BOOST_CHECK(spv::PendingOrder(anchorOne, anchorTwo) == true); + BOOST_CHECK(spv::PendingOrder(anchorTwo, anchorOne) == false); BOOST_CHECK(BestOfTwo(&anchorOne, &anchorTwo)->txHash == uint256S("12ca5ac2b666478bbbdfc0e0b328552a8cd83aa1b3fbb822560ab8cbf72be893")); BOOST_CHECK(BestOfTwo(&anchorTwo, &anchorOne)->txHash == uint256S("12ca5ac2b666478bbbdfc0e0b328552a8cd83aa1b3fbb822560ab8cbf72be893")); + + // Test new anchor ordering logic with randomised hashes + anchorOne.anchor.height = 10000000; + anchorTwo.anchor.height = 10000000; + + anchorOne.txHash = uint256S("12ca5ac2b666478bbbdfc0e0b328552a8cd83aa1b3fbb822560ab8cbf72be893"); // 5cfe6594dad4efe238e5c7903ba5afa4c3f92ee81282a43e7ba5919f4cebd210 + anchorTwo.txHash = uint256S("852bb89808af5a5487d4afed23b4ec3c4186ec8101ff9e7c73a038c9a2c436d9"); // 1af2609c24bcbe59af8ffb921129454e12f7aef07da3c3c0fead97711469045a + + BOOST_CHECK(spv::PendingOrder(anchorOne, anchorTwo) == true); + BOOST_CHECK(spv::PendingOrder(anchorTwo, anchorOne) == false); + BOOST_CHECK(BestOfTwo(&anchorOne, &anchorTwo)->txHash == uint256S("12ca5ac2b666478bbbdfc0e0b328552a8cd83aa1b3fbb822560ab8cbf72be893")); + BOOST_CHECK(BestOfTwo(&anchorTwo, &anchorOne)->txHash == uint256S("12ca5ac2b666478bbbdfc0e0b328552a8cd83aa1b3fbb822560ab8cbf72be893")); + + anchorOne.txHash = uint256S("e48106cf7254b73be5d550f2054495b32c4e98f2c2c251697c267ab0a6cb87cf"); // ff8c5aa31428aa787513d1e3451914ed7f8a1b6174e3a572dc5f2a449201240d + anchorTwo.txHash = uint256S("a5c974e6eca14593bdfd53eaf49c777e4615342370e79705d96b5afd2a016278"); // a06399a5ed47c65452f71174174e9c2696dd3fae83a9d0e89796e195c01b670d + + BOOST_CHECK(spv::PendingOrder(anchorOne, anchorTwo) == true); + BOOST_CHECK(spv::PendingOrder(anchorTwo, anchorOne) == false); + BOOST_CHECK(BestOfTwo(&anchorOne, &anchorTwo)->txHash == uint256S("e48106cf7254b73be5d550f2054495b32c4e98f2c2c251697c267ab0a6cb87cf")); + BOOST_CHECK(BestOfTwo(&anchorTwo, &anchorOne)->txHash == uint256S("e48106cf7254b73be5d550f2054495b32c4e98f2c2c251697c267ab0a6cb87cf")); + + anchorOne.txHash = uint256S("7398ddf9bdabb2c1271b918d3f516fd4573bbead448b4e8a611b7ffd5451777b"); // 9a15e4a213dcd75e035141012e3636c4a46f15ef2ba13ff52357f6121d46901b + anchorTwo.txHash = uint256S("b2f2ed1fc0b6192b9398b0aef2e79e57d4a473c3e9b2be45e556f7c85e269cbc"); // 699b691491d27aad44fe58d897af97a0e631e6ad27f83408a0d64d933639fd03 + + BOOST_CHECK(spv::PendingOrder(anchorOne, anchorTwo) == false); + BOOST_CHECK(spv::PendingOrder(anchorTwo, anchorOne) == true); + BOOST_CHECK(BestOfTwo(&anchorOne, &anchorTwo)->txHash == uint256S("b2f2ed1fc0b6192b9398b0aef2e79e57d4a473c3e9b2be45e556f7c85e269cbc")); + BOOST_CHECK(BestOfTwo(&anchorTwo, &anchorOne)->txHash == uint256S("b2f2ed1fc0b6192b9398b0aef2e79e57d4a473c3e9b2be45e556f7c85e269cbc")); + + anchorOne.txHash = uint256S("3264bb76dc2cdff731733fa33dd530b0058da45606af9824b49b61e1f5ac9d9d"); // f4b83366e8d5650ec7714962b3c6619d737ae43f8b6641b35b4e39ab9605b88f + anchorTwo.txHash = uint256S("851d8697118d6688b6552cb142a95f461b45e61b9accafa1ef3386b1be0cc2bb"); // c9f467a9e6233f9614d88111b333336a29be9f6d54ac7171fce0f5f58eed04e9 + + BOOST_CHECK(spv::PendingOrder(anchorOne, anchorTwo) == true); + BOOST_CHECK(spv::PendingOrder(anchorTwo, anchorOne) == false); + BOOST_CHECK(BestOfTwo(&anchorOne, &anchorTwo)->txHash == uint256S("3264bb76dc2cdff731733fa33dd530b0058da45606af9824b49b61e1f5ac9d9d")); + BOOST_CHECK(BestOfTwo(&anchorTwo, &anchorOne)->txHash == uint256S("3264bb76dc2cdff731733fa33dd530b0058da45606af9824b49b61e1f5ac9d9d")); + + anchorOne.txHash = uint256S("87c638cfe4efa94d8e259978c55a85de101cafaac68c9f6c03b3dc0335016b55"); // af431c6193434919a996fb87aba8790c65f49116f489a7a72e08af02276804ca + anchorTwo.txHash = uint256S("390a8b3b581e75e13e8eec4fc7fe0b35a382e9fba29d9b42c547e1b6c6785a51"); // 1ecf49be7a49c081245b6df1ba9f7a0463f7da3a33c505145c963be8f741b086 + + BOOST_CHECK(spv::PendingOrder(anchorOne, anchorTwo) == false); + BOOST_CHECK(spv::PendingOrder(anchorTwo, anchorOne) == true); + BOOST_CHECK(BestOfTwo(&anchorOne, &anchorTwo)->txHash == uint256S("390a8b3b581e75e13e8eec4fc7fe0b35a382e9fba29d9b42c547e1b6c6785a51")); + BOOST_CHECK(BestOfTwo(&anchorTwo, &anchorOne)->txHash == uint256S("390a8b3b581e75e13e8eec4fc7fe0b35a382e9fba29d9b42c547e1b6c6785a51")); } BOOST_AUTO_TEST_CASE(best_anchor_activation_logic) diff --git a/test/functional/feature_anchor_rewards.py b/test/functional/feature_anchor_rewards.py index fc8eeecb2e8..be1947e5fb9 100755 --- a/test/functional/feature_anchor_rewards.py +++ b/test/functional/feature_anchor_rewards.py @@ -210,11 +210,11 @@ def run_test(self): unrewarded = self.nodes[0].spv_listanchorsunrewarded() assert_equal(len(unrewarded), 1) - assert_equal(unrewarded[0]['btcHeight'], 1) - if unrewarded[0]['btcHash'] != btcHash0: - assert_equal(unrewarded[0]['btcHash'], btcHash1) - assert_equal(unrewarded[0]['dfiHeight'], 15) - assert_equal(unrewarded[0]['dfiHash'], dfiHash) + assert_equal(unrewarded[0]['btcBlockHeight'], 1) + if unrewarded[0]['btcTxHash'] != btcHash0: + assert_equal(unrewarded[0]['btcTxHash'], btcHash1) + assert_equal(unrewarded[0]['defiBlockHeight'], 15) + assert_equal(unrewarded[0]['defiBlockHash'], dfiHash) # important to wait here! self.sync_blocks(self.nodes[0:2])