@@ -4,17 +4,24 @@ import kotlinx.coroutines.CoroutineScope
44import kotlinx.coroutines.asCoroutineDispatcher
55import kotlinx.coroutines.delay
66import kotlinx.coroutines.launch
7+ import nodecore.api.grpc.RpcOutput
8+ import nodecore.api.grpc.RpcTransaction
79import nodecore.api.grpc.RpcTransactionUnion
810import nodecore.api.grpc.utilities.ByteStringUtility
11+ import nodecore.api.grpc.utilities.extensions.toHex
12+ import nodecore.api.grpc.utilities.extensions.toProperAddressType
913import org.veriblock.core.crypto.VbkTxId
14+ import org.veriblock.core.crypto.asVbkTxId
1015import org.veriblock.core.utilities.createLogger
1116import org.veriblock.sdk.models.Address
1217import org.veriblock.sdk.models.VeriBlockBlock
1318import org.veriblock.spv.SpvContext
19+ import org.veriblock.spv.model.FullBlock
1420import org.veriblock.spv.model.Transaction
1521import org.veriblock.spv.serialization.MessageSerializer
1622import org.veriblock.spv.util.SpvEventBus
1723import org.veriblock.spv.util.Threading
24+ import java.lang.IllegalArgumentException
1825import java.util.concurrent.ConcurrentHashMap
1926import java.util.concurrent.locks.ReentrantLock
2027import kotlin.concurrent.withLock
@@ -27,10 +34,13 @@ class PendingTransactionContainer(
2734) {
2835 // TODO(warchant): use Address as a key, instead of String
2936 private val pendingTransactionsByAddress: MutableMap <String , MutableList <Transaction >> = ConcurrentHashMap ()
30- private val confirmedTransactionReplies: MutableMap <VbkTxId , TransactionInfo > = ConcurrentHashMap ()
31- private val confirmedTransactions: MutableMap <VbkTxId , Transaction > = ConcurrentHashMap ()
3237 private val pendingTransactions: MutableMap <VbkTxId , Transaction > = ConcurrentHashMap ()
33- private val transactionsToMonitor: MutableSet <VbkTxId > = ConcurrentHashMap .newKeySet()
38+ // FIXME: add converting between TransactionData <-> Transaction and delete this
39+ private val pendingTransactionsInfo: MutableMap <VbkTxId , TransactionInfo > = ConcurrentHashMap ()
40+
41+ private val addedToBlockchainTransactions: MutableMap <VbkTxId , Transaction > = ConcurrentHashMap ()
42+ // FIXME: add converting between TransactionData <-> Transaction and delete this
43+ private val addedToBlockchainTransactionsInfo: MutableMap <VbkTxId , TransactionInfo > = ConcurrentHashMap ()
3444
3545 private val lock = ReentrantLock ()
3646
@@ -45,48 +55,50 @@ class PendingTransactionContainer(
4555 .sortedBy { it.value.getSignatureIndex() }
4656 .map { it.key }
4757 .toSet()
48- return pendingTransactions + transactionsToMonitor
58+ return pendingTransactions
4959 }
5060
5161 fun getTransactionInfo (txId : VbkTxId ): TransactionInfo ? {
52- confirmedTransactionReplies [txId]?.let {
62+ pendingTransactionsInfo [txId]?.let {
5363 return it
5464 }
55- if ( ! pendingTransactions.containsKey( txId)) {
56- transactionsToMonitor.add(txId)
65+ addedToBlockchainTransactionsInfo[ txId]?. let {
66+ return it
5767 }
5868 return null
5969 }
6070
6171 fun updateTransactionInfo (transactionInfo : TransactionInfo ) = lock.withLock {
6272 val transaction = transactionInfo.transaction
63- if (pendingTransactions.containsKey(transaction.txId) || transactionsToMonitor.contains(transaction.txId) ) {
64- confirmedTransactionReplies [transaction.txId] = transactionInfo
73+ if (pendingTransactions.containsKey(transaction.txId)) {
74+ pendingTransactionsInfo [transaction.txId] = transactionInfo
6575 val pendingTx = pendingTransactions[transaction.txId]
6676 if (pendingTx != null ) {
67- confirmedTransactions[transaction.txId] = pendingTx
6877 if (pendingTx.getSignatureIndex() > lastConfirmedSignatureIndex) {
6978 lastConfirmedSignatureIndex = pendingTx.getSignatureIndex()
7079 }
7180 }
7281 if (transactionInfo.confirmations > 0 ) {
82+ addedToBlockchainTransactions[transaction.txId] = pendingTx!!
83+ addedToBlockchainTransactionsInfo[transaction.txId] = pendingTransactionsInfo[transaction.txId]!!
84+
7385 pendingTransactions.remove(transaction.txId)
74- transactionsToMonitor .remove(transaction.txId)
86+ pendingTransactionsInfo .remove(transaction.txId)
7587 pendingTransactionsByAddress[transaction.sourceAddress]?.removeIf { it.txId == transaction.txId }
7688 }
7789 }
7890
7991 // Prune confirmed transactions
80- if (confirmedTransactionReplies .size > 10_000 ) {
81- val topConfirmedBlockHeight = confirmedTransactionReplies .values.maxOf { it.blockNumber }
82- val txToRemove = confirmedTransactionReplies .values.asSequence().filter {
92+ if (addedToBlockchainTransactionsInfo .size > 10_000 ) {
93+ val topConfirmedBlockHeight = addedToBlockchainTransactionsInfo .values.maxOf { it.blockNumber }
94+ val txToRemove = addedToBlockchainTransactionsInfo .values.asSequence().filter {
8395 it.blockNumber < topConfirmedBlockHeight - 1_000
8496 }.map {
8597 it.transaction.txId
8698 }
8799 for (txId in txToRemove) {
88- confirmedTransactionReplies .remove(txId)
89- confirmedTransactions .remove(txId)
100+ addedToBlockchainTransactions .remove(txId)
101+ addedToBlockchainTransactionsInfo .remove(txId)
90102 }
91103 }
92104 }
@@ -170,7 +182,6 @@ class PendingTransactionContainer(
170182 logger.info { " All the transactions for that address will be pruned in order to prevent further transactions from being rejected." }
171183 for (tx in newTransactions) {
172184 pendingTransactions.remove(tx.txId)
173- transactionsToMonitor.remove(tx.txId)
174185 }
175186 newTransactions.clear()
176187 }
@@ -182,21 +193,75 @@ class PendingTransactionContainer(
182193 }
183194
184195 private fun handleRemovedBestBlock (removedBlock : VeriBlockBlock ) = lock.withLock {
185- val reorganizedTransactions = confirmedTransactionReplies .values.filter {
196+ val reorganizedTransactions = addedToBlockchainTransactionsInfo .values.filter {
186197 it.blockNumber == removedBlock.height
187198 }.mapNotNull {
188- confirmedTransactions [it.transaction.txId]
199+ addedToBlockchainTransactions [it.transaction.txId]
189200 }
190201 for (transaction in reorganizedTransactions) {
191- confirmedTransactionReplies.remove(transaction.txId)
192- confirmedTransactions.remove(transaction.txId)
202+ addedToBlockchainTransactions.remove(transaction.txId)
193203 addTransaction(transaction)
194204 }
195205 }
196206
207+ fun updateTransactionsByBlock (block : FullBlock ) = lock.withLock {
208+ /*
209+ * We are removing only transactions that match the exact String from the block. If the block validation
210+ * fails, NO transactions are removed from the transaction pool.
211+ */
212+ val normalTransactions = block.normalTransactions
213+ ? : throw IllegalArgumentException (
214+ " removeTransactionsInBlock cannot be called with a block with a " +
215+ " null transaction set!"
216+ )
217+ var allSuccessful = true
218+ for (transaction in normalTransactions) {
219+ // FIXME: convert StandardTransaction to TransactionData
220+ val builder = transaction.getSignedMessageBuilder(context.config.networkParameters)
221+ val rpcTx = builder.build()
222+ val txData = rpcTx.transaction.toModel()
223+
224+ val txInfo = TransactionInfo (
225+ blockNumber = block.height,
226+ timestamp = block.timestamp,
227+ transaction = txData,
228+ confirmations = context.blockchain.getChainHeadIndex().height + 1 - block.height,
229+ blockHash = block.hash.toString(),
230+ merklePath = " " , // FIXME: is it possible to calculate merklePath in SPV?
231+ endorsedBlockHash = " " , // FIXME: for POP
232+ bitcoinBlockHash = " " , // FIXME: for POP
233+ bitcoinTxId = " " , // FIXME: for POP
234+ bitcoinConfirmations = 0 , // FIXME: for POP
235+ )
236+ updateTransactionInfo(txInfo)
237+ }
238+ }
239+
197240 enum class AddTransactionResult {
198241 SUCCESS ,
199242 INVALID ,
200243 DUPLICATE
201244 }
245+
246+ private fun RpcTransaction.toModel () = TransactionData (
247+ type = TransactionType .valueOf(type.name),
248+ sourceAddress = sourceAddress.toProperAddressType(),
249+ sourceAmount = sourceAmount,
250+ outputs = outputsList.map { it.toModel() },
251+ transactionFee = transactionFee,
252+ data = data.toHex(),
253+ bitcoinTransaction = bitcoinTransaction.toHex(),
254+ endorsedBlockHeader = endorsedBlockHeader.toHex(),
255+ bitcoinBlockHeaderOfProof = " " ,
256+ merklePath = merklePath,
257+ contextBitcoinBlockHeaders = listOf (),
258+ timestamp = timestamp,
259+ size = size,
260+ txId = txId.toByteArray().asVbkTxId()
261+ )
262+
263+ private fun RpcOutput.toModel () = OutputData (
264+ address = address.toHex(),
265+ amount = amount
266+ )
202267}
0 commit comments