@@ -2,26 +2,27 @@ package io.iohk.ethereum.transactions
22
33import akka .actor .{Actor , ActorRef , Cancellable , Props }
44import akka .util .{ByteString , Timeout }
5+ import com .google .common .cache .{Cache , CacheBuilder , RemovalCause , RemovalNotification }
56import io .iohk .ethereum .domain .SignedTransaction
67import io .iohk .ethereum .network .PeerEventBusActor .PeerEvent .MessageFromPeer
78import io .iohk .ethereum .network .PeerEventBusActor .SubscriptionClassifier .MessageClassifier
89import io .iohk .ethereum .network .PeerEventBusActor .{PeerEvent , PeerSelector , Subscribe , SubscriptionClassifier }
910import io .iohk .ethereum .network .PeerManagerActor .Peers
10- import io .iohk .ethereum .network .{EtcPeerManagerActor , Peer , PeerId , PeerManagerActor }
1111import io .iohk .ethereum .network .p2p .messages .CommonMessages .SignedTransactions
12+ import io .iohk .ethereum .network .{EtcPeerManagerActor , Peer , PeerId , PeerManagerActor }
1213import io .iohk .ethereum .utils .TxPoolConfig
13-
14- import scala .concurrent .duration ._
14+ import scala .collection .JavaConverters ._
1515import scala .concurrent .ExecutionContext .Implicits .global
16+ import scala .concurrent .duration ._
1617
1718object PendingTransactionsManager {
1819 def props (txPoolConfig : TxPoolConfig , peerManager : ActorRef , etcPeerManager : ActorRef , peerMessageBus : ActorRef ): Props =
1920 Props (new PendingTransactionsManager (txPoolConfig, peerManager, etcPeerManager, peerMessageBus))
2021
21- case class AddTransactions (signedTransactions : List [SignedTransaction ])
22+ case class AddTransactions (signedTransactions : Set [SignedTransaction ])
2223
2324 object AddTransactions {
24- def apply (txs : SignedTransaction * ): AddTransactions = AddTransactions (txs.toList )
25+ def apply (txs : SignedTransaction * ): AddTransactions = AddTransactions (txs.toSet )
2526 }
2627
2728 case class AddOrOverrideTransaction (signedTransaction : SignedTransaction )
@@ -39,21 +40,28 @@ object PendingTransactionsManager {
3940}
4041
4142class PendingTransactionsManager (txPoolConfig : TxPoolConfig , peerManager : ActorRef ,
42- etcPeerManager : ActorRef , peerEventBus : ActorRef ) extends Actor {
43+ etcPeerManager : ActorRef , peerEventBus : ActorRef ) extends Actor {
4344
4445 import PendingTransactionsManager ._
4546 import akka .pattern .ask
4647
47- /**
48- * stores all pending transactions
49- */
50- var pendingTransactions : List [PendingTransaction ] = Nil
51-
5248 /**
5349 * stores information which tx hashes are "known" by which peers
5450 */
5551 var knownTransactions : Map [ByteString , Set [PeerId ]] = Map .empty
5652
53+ /**
54+ * stores all pending transactions
55+ */
56+ val pendingTransactions : Cache [ByteString , PendingTransaction ] = CacheBuilder .newBuilder()
57+ .expireAfterWrite(txPoolConfig.transactionTimeout._1, txPoolConfig.transactionTimeout._2)
58+ .maximumSize(txPoolConfig.txPoolSize)
59+ .removalListener(
60+ (notification : RemovalNotification [ByteString , PendingTransaction ]) => if (notification.getCause == RemovalCause .EXPIRED ) {
61+ knownTransactions = knownTransactions.filterNot(_._1 == notification.getKey)
62+ }
63+ ).build()
64+
5765 /**
5866 * stores transactions timeouts by tx hash
5967 */
@@ -67,68 +75,61 @@ class PendingTransactionsManager(txPoolConfig: TxPoolConfig, peerManager: ActorR
6775 // scalastyle:off method.length
6876 override def receive : Receive = {
6977 case PeerEvent .PeerHandshakeSuccessful (peer, _) =>
70- self ! NotifyPeer (pendingTransactions.map(_.stx), peer)
78+ pendingTransactions.cleanUp()
79+ val stxs = pendingTransactions.asMap().values().asScala.toSeq.map(_.stx)
80+ self ! NotifyPeer (stxs, peer)
7181
7282 case AddTransactions (signedTransactions) =>
73- val transactionsToAdd = signedTransactions.filterNot(t => pendingTransactions.map(_.stx).contains(t))
83+ pendingTransactions.cleanUp()
84+ val stxs = pendingTransactions.asMap().values().asScala.map(_.stx).toSet
85+ val transactionsToAdd = signedTransactions.diff(stxs)
7486 if (transactionsToAdd.nonEmpty) {
75- transactionsToAdd.foreach(setTimeout)
7687 val timestamp = System .currentTimeMillis()
77- pendingTransactions = (transactionsToAdd.map( PendingTransaction (_ , timestamp)) ++ pendingTransactions).take(txPoolConfig.txPoolSize )
88+ transactionsToAdd.foreach(t => pendingTransactions.put(t.hash, PendingTransaction (t , timestamp)))
7889 (peerManager ? PeerManagerActor .GetPeers ).mapTo[Peers ].foreach { peers =>
79- peers.handshaked.foreach { peer => self ! NotifyPeer (transactionsToAdd, peer) }
90+ peers.handshaked.foreach { peer => self ! NotifyPeer (transactionsToAdd.toSeq , peer) }
8091 }
8192 }
8293
8394 case AddOrOverrideTransaction (newStx) =>
84- val (obsoleteTxs, txsWithoutObsoletes) = pendingTransactions.partition(ptx =>
85- ptx.stx.senderAddress == newStx.senderAddress &&
86- ptx.stx.tx.nonce == newStx.tx.nonce)
87- obsoleteTxs.map(_.stx).foreach(clearTimeout)
95+ pendingTransactions.cleanUp()
96+ val obsoleteTxs = pendingTransactions.asMap().asScala.filter(
97+ ptx => ptx._2.stx.senderAddress == newStx.senderAddress && ptx._2.stx.tx.nonce == newStx.tx.nonce
98+ )
99+ pendingTransactions.invalidateAll(obsoleteTxs.keys.asJava)
88100
89101 val timestamp = System .currentTimeMillis()
90- pendingTransactions = (PendingTransaction (newStx, timestamp) +: txsWithoutObsoletes).take(txPoolConfig.txPoolSize)
91- setTimeout(newStx)
102+ pendingTransactions.put(newStx.hash, PendingTransaction (newStx, timestamp))
92103
93- (peerManager ? PeerManagerActor .GetPeers ).mapTo[Peers ].foreach { peers =>
94- peers.handshaked.foreach { peer => self ! NotifyPeer (List (newStx), peer) }
104+ (peerManager ? PeerManagerActor .GetPeers ).mapTo[Peers ].foreach {
105+ peers => peers .handshaked.foreach { peer => self ! NotifyPeer (List (newStx), peer) }
95106 }
96107
97108 case NotifyPeer (signedTransactions, peer) =>
109+ pendingTransactions.cleanUp()
98110 val txsToNotify = signedTransactions
99- .filter(stx => pendingTransactions.exists(_.stx.hash == stx.hash)) // signed transactions that are still pending
111+ .filter(stx => pendingTransactions.asMap().containsKey( stx.hash)) // signed transactions that are still pending
100112 .filterNot(isTxKnown(_, peer.id)) // and not known by peer
101113
102- if (txsToNotify.nonEmpty) {
103- etcPeerManager ! EtcPeerManagerActor .SendMessage (SignedTransactions (txsToNotify), peer.id)
104- txsToNotify.foreach(setTxKnown(_, peer.id))
105- }
114+ if (txsToNotify.nonEmpty) {
115+ etcPeerManager ! EtcPeerManagerActor .SendMessage (SignedTransactions (txsToNotify), peer.id)
116+ txsToNotify.foreach(setTxKnown(_, peer.id))
117+ }
106118
107119 case GetPendingTransactions =>
108- sender() ! PendingTransactionsResponse (pendingTransactions)
120+ pendingTransactions.cleanUp()
121+ sender() ! PendingTransactionsResponse (pendingTransactions.asMap().asScala.values.toSeq)
109122
110123 case RemoveTransactions (signedTransactions) =>
111- pendingTransactions = pendingTransactions.filterNot(pt => signedTransactions.contains(pt.stx))
112- knownTransactions = knownTransactions.filterNot(signedTransactions.map(_.hash).contains)
113- signedTransactions.foreach(clearTimeout)
124+ pendingTransactions.invalidateAll(signedTransactions.map(_.hash).asJava)
125+ knownTransactions = knownTransactions -- signedTransactions.map(_.hash)
114126
115127 case MessageFromPeer (SignedTransactions (signedTransactions), peerId) =>
116- self ! AddTransactions (signedTransactions.toList )
128+ self ! AddTransactions (signedTransactions.toSet )
117129 signedTransactions.foreach(setTxKnown(_, peerId))
118130
119131 case ClearPendingTransactions =>
120- pendingTransactions = Nil
121- }
122-
123- private def setTimeout (stx : SignedTransaction ): Unit = {
124- timeouts.get(stx.hash).map(_.cancel())
125- val cancellable = context.system.scheduler.scheduleOnce(txPoolConfig.transactionTimeout, self, RemoveTransactions (Seq (stx)))
126- timeouts += (stx.hash -> cancellable)
127- }
128-
129- private def clearTimeout (stx : SignedTransaction ): Unit = {
130- timeouts.get(stx.hash).map(_.cancel())
131- timeouts -= stx.hash
132+ pendingTransactions.invalidateAll()
132133 }
133134
134135 private def isTxKnown (signedTransaction : SignedTransaction , peerId : PeerId ): Boolean =
0 commit comments