@@ -768,7 +768,8 @@ void CInstantSendManager::ProcessInstantSendLock(NodeId from, const uint256& has
768768 db.WriteInstantSendLockMined (hash, pindexMined->nHeight );
769769 }
770770
771- pendingRetryTxs.emplace (islock.txid );
771+ // This will also add children TXs to pendingRetryTxs
772+ RemoveNonLockedTx (islock.txid );
772773 }
773774
774775 CInv inv (MSG_ISLOCK, hash);
@@ -837,12 +838,63 @@ void CInstantSendManager::SyncTransaction(const CTransaction& tx, const CBlockIn
837838 }
838839
839840 bool chainlocked = pindex && chainLocksHandler->HasChainLock (pindex->nHeight , pindex->GetBlockHash ());
840- if (!islockHash.IsNull () || chainlocked) {
841- LOCK (cs);
842- pendingRetryTxs.emplace (tx.GetHash ());
843- } else {
841+ if (islockHash.IsNull () && !chainlocked) {
844842 ProcessTx (tx, Params ().GetConsensus ());
845843 }
844+
845+ LOCK (cs);
846+ if (!chainlocked && islockHash.IsNull ()) {
847+ // TX is not locked, so make sure it is tracked
848+ AddNonLockedTx (MakeTransactionRef (tx));
849+ nonLockedTxs.at (tx.GetHash ()).pindexMined = posInBlock == CMainSignals::SYNC_TRANSACTION_NOT_IN_BLOCK ? pindex : nullptr ;
850+ } else {
851+ // TX is locked, so make sure we don't track it anymore
852+ RemoveNonLockedTx (tx.GetHash ());
853+ }
854+ }
855+
856+ void CInstantSendManager::AddNonLockedTx (const CTransactionRef& tx)
857+ {
858+ AssertLockHeld (cs);
859+ auto it = nonLockedTxs.emplace (tx->GetHash (), NonLockedTxInfo ()).first ;
860+ auto & info = it->second ;
861+
862+ if (!info.tx ) {
863+ info.tx = tx;
864+ for (const auto & in : tx->vin ) {
865+ nonLockedTxs[in.prevout .hash ].children .emplace (tx->GetHash ());
866+ }
867+ }
868+ }
869+
870+ void CInstantSendManager::RemoveNonLockedTx (const uint256& txid)
871+ {
872+ AssertLockHeld (cs);
873+
874+ auto it = nonLockedTxs.find (txid);
875+ if (it == nonLockedTxs.end ()) {
876+ return ;
877+ }
878+ auto & info = it->second ;
879+
880+ // TX got locked, so we can retry locking children
881+ for (auto & childTxid : info.children ) {
882+ pendingRetryTxs.emplace (childTxid);
883+ }
884+
885+ if (info.tx ) {
886+ for (const auto & in : info.tx ->vin ) {
887+ auto jt = nonLockedTxs.find (in.prevout .hash );
888+ if (jt != nonLockedTxs.end ()) {
889+ jt->second .children .erase (txid);
890+ if (!jt->second .tx && jt->second .children .empty ()) {
891+ nonLockedTxs.erase (jt);
892+ }
893+ }
894+ }
895+ }
896+
897+ nonLockedTxs.erase (it);
846898}
847899
848900void CInstantSendManager::NotifyChainLock (const CBlockIndex* pindexChainLock)
@@ -887,8 +939,20 @@ void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex)
887939 }
888940 }
889941
890- // Retry all not yet locked mempool TXs and TX which where mined after the fully confirmed block
891- pendingRetryAllTxs = true ;
942+ // Find all previously unlocked TXs that got locked by this fully confirmed (ChainLock) block and remove them
943+ // from the nonLockedTxs map. Also collect all children of these TXs and mark them for retrying of IS locking.
944+ std::vector<uint256> toRemove;
945+ for (auto & p : nonLockedTxs) {
946+ auto pindexMined = p.second .pindexMined ;
947+
948+ if (pindexMined && pindex->GetAncestor (pindexMined->nHeight ) == pindexMined) {
949+ toRemove.emplace_back (p.first );
950+ }
951+ }
952+ for (auto & txid : toRemove) {
953+ // This will also add children to pendingRetryTxs
954+ RemoveNonLockedTx (txid);
955+ }
892956 }
893957
894958 for (auto & p : removeISLocks) {
@@ -922,96 +986,35 @@ void CInstantSendManager::RemoveMempoolConflictsForLock(const uint256& hash, con
922986
923987bool CInstantSendManager::ProcessPendingRetryLockTxs ()
924988{
925- bool retryAllTxs;
926- decltype (pendingRetryTxs) parentTxs;
989+ decltype (pendingRetryTxs) retryTxs;
927990 {
928991 LOCK (cs);
929- retryAllTxs = pendingRetryAllTxs;
930- parentTxs = std::move (pendingRetryTxs);
931- pendingRetryAllTxs = false ;
992+ retryTxs = std::move (pendingRetryTxs);
932993 }
933994
934- if (!retryAllTxs && parentTxs .empty ()) {
995+ if (retryTxs .empty ()) {
935996 return false ;
936997 }
937998
938999 if (!IsNewInstantSendEnabled ()) {
9391000 return false ;
9401001 }
9411002
942- // Let's retry all unlocked TXs from mempool and and recently connected blocks
943-
944- std::unordered_map<uint256, CTransactionRef> txs;
945-
946- {
947- LOCK (mempool.cs );
948-
949- if (retryAllTxs) {
950- txs.reserve (mempool.mapTx .size ());
951- for (auto it = mempool.mapTx .begin (); it != mempool.mapTx .end (); ++it) {
952- txs.emplace (it->GetTx ().GetHash (), it->GetSharedTx ());
953- }
954- } else {
955- for (const auto & parentTx : parentTxs) {
956- auto it = mempool.mapNextTx .lower_bound (COutPoint (parentTx, 0 ));
957- while (it != mempool.mapNextTx .end () && it->first ->hash == parentTx) {
958- txs.emplace (it->second ->GetHash (), mempool.get (it->second ->GetHash ()));
959- ++it;
960- }
961- }
962- }
963- }
964-
965- const CBlockIndex* pindexWalk = nullptr ;
966- {
967- LOCK (cs_main);
968- pindexWalk = chainActive.Tip ();
969- }
970-
971- // scan blocks until we hit the last chainlocked block we know of. Also stop scanning after a depth of 6 to avoid
972- // signing thousands of TXs at once. Also, after a depth of 6, blocks get eligible for ChainLocking even if unsafe
973- // TXs are included, so there is no need to retroactively sign these.
974- int depth = 0 ;
975- while (pindexWalk && depth < 6 ) {
976- if (chainLocksHandler->HasChainLock (pindexWalk->nHeight , pindexWalk->GetBlockHash ())) {
977- break ;
978- }
979-
980- CBlock block;
1003+ int retryCount = 0 ;
1004+ for (const auto & txid : retryTxs) {
1005+ CTransactionRef tx;
9811006 {
982- LOCK (cs_main );
983- if (! ReadBlockFromDisk (block, pindexWalk, Params (). GetConsensus ())) {
984- pindexWalk = pindexWalk-> pprev ;
1007+ LOCK (cs );
1008+ auto it = nonLockedTxs. find (txid);
1009+ if (it == nonLockedTxs. end ()) {
9851010 continue ;
9861011 }
987- }
1012+ tx = it-> second . tx ;
9881013
989- for (const auto & tx : block.vtx ) {
990- if (retryAllTxs) {
991- txs.emplace (tx->GetHash (), tx);
992- } else {
993- bool isChild = false ;
994- for (auto & in : tx->vin ) {
995- if (parentTxs.count (in.prevout .hash )) {
996- isChild = true ;
997- break ;
998- }
999- }
1000- if (isChild) {
1001- txs.emplace (tx->GetHash (), tx);
1002- }
1014+ if (!tx) {
1015+ continue ;
10031016 }
1004- }
10051017
1006- pindexWalk = pindexWalk->pprev ;
1007- depth++;
1008- }
1009-
1010- bool didWork = false ;
1011- for (auto & p : txs) {
1012- auto & tx = p.second ;
1013- {
1014- LOCK (cs);
10151018 if (txToCreatingInstantSendLocks.count (tx->GetHash ())) {
10161019 // we're already in the middle of locking this one
10171020 continue ;
@@ -1036,10 +1039,16 @@ bool CInstantSendManager::ProcessPendingRetryLockTxs()
10361039 }
10371040
10381041 ProcessTx (*tx, Params ().GetConsensus ());
1039- didWork = true ;
1042+ retryCount++;
1043+ }
1044+
1045+ if (retryCount != 0 ) {
1046+ LOCK (cs);
1047+ LogPrint (" instantsend" , " CInstantSendManager::%s -- retried %d TXs. nonLockedTxs.size=%d\n " , __func__,
1048+ retryCount, nonLockedTxs.size ());
10401049 }
10411050
1042- return didWork ;
1051+ return retryCount != 0 ;
10431052}
10441053
10451054bool CInstantSendManager::AlreadyHave (const CInv& inv)
0 commit comments