@@ -768,7 +768,8 @@ void CInstantSendManager::ProcessInstantSendLock(NodeId from, const uint256& has
768
768
db.WriteInstantSendLockMined (hash, pindexMined->nHeight );
769
769
}
770
770
771
- pendingRetryTxs.emplace (islock.txid );
771
+ // This will also add children TXs to pendingRetryTxs
772
+ RemoveNonLockedTx (islock.txid );
772
773
}
773
774
774
775
CInv inv (MSG_ISLOCK, hash);
@@ -837,12 +838,63 @@ void CInstantSendManager::SyncTransaction(const CTransaction& tx, const CBlockIn
837
838
}
838
839
839
840
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) {
844
842
ProcessTx (tx, Params ().GetConsensus ());
845
843
}
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);
846
898
}
847
899
848
900
void CInstantSendManager::NotifyChainLock (const CBlockIndex* pindexChainLock)
@@ -887,8 +939,20 @@ void CInstantSendManager::HandleFullyConfirmedBlock(const CBlockIndex* pindex)
887
939
}
888
940
}
889
941
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
+ }
892
956
}
893
957
894
958
for (auto & p : removeISLocks) {
@@ -922,96 +986,35 @@ void CInstantSendManager::RemoveMempoolConflictsForLock(const uint256& hash, con
922
986
923
987
bool CInstantSendManager::ProcessPendingRetryLockTxs ()
924
988
{
925
- bool retryAllTxs;
926
- decltype (pendingRetryTxs) parentTxs;
989
+ decltype (pendingRetryTxs) retryTxs;
927
990
{
928
991
LOCK (cs);
929
- retryAllTxs = pendingRetryAllTxs;
930
- parentTxs = std::move (pendingRetryTxs);
931
- pendingRetryAllTxs = false ;
992
+ retryTxs = std::move (pendingRetryTxs);
932
993
}
933
994
934
- if (!retryAllTxs && parentTxs .empty ()) {
995
+ if (retryTxs .empty ()) {
935
996
return false ;
936
997
}
937
998
938
999
if (!IsNewInstantSendEnabled ()) {
939
1000
return false ;
940
1001
}
941
1002
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;
981
1006
{
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 ()) {
985
1010
continue ;
986
1011
}
987
- }
1012
+ tx = it-> second . tx ;
988
1013
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 ;
1003
1016
}
1004
- }
1005
1017
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);
1015
1018
if (txToCreatingInstantSendLocks.count (tx->GetHash ())) {
1016
1019
// we're already in the middle of locking this one
1017
1020
continue ;
@@ -1036,10 +1039,16 @@ bool CInstantSendManager::ProcessPendingRetryLockTxs()
1036
1039
}
1037
1040
1038
1041
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 ());
1040
1049
}
1041
1050
1042
- return didWork ;
1051
+ return retryCount != 0 ;
1043
1052
}
1044
1053
1045
1054
bool CInstantSendManager::AlreadyHave (const CInv& inv)
0 commit comments