@@ -4,17 +4,24 @@ import kotlinx.coroutines.CoroutineScope
4
4
import kotlinx.coroutines.asCoroutineDispatcher
5
5
import kotlinx.coroutines.delay
6
6
import kotlinx.coroutines.launch
7
+ import nodecore.api.grpc.RpcOutput
8
+ import nodecore.api.grpc.RpcTransaction
7
9
import nodecore.api.grpc.RpcTransactionUnion
8
10
import nodecore.api.grpc.utilities.ByteStringUtility
11
+ import nodecore.api.grpc.utilities.extensions.toHex
12
+ import nodecore.api.grpc.utilities.extensions.toProperAddressType
9
13
import org.veriblock.core.crypto.VbkTxId
14
+ import org.veriblock.core.crypto.asVbkTxId
10
15
import org.veriblock.core.utilities.createLogger
11
16
import org.veriblock.sdk.models.Address
12
17
import org.veriblock.sdk.models.VeriBlockBlock
13
18
import org.veriblock.spv.SpvContext
19
+ import org.veriblock.spv.model.FullBlock
14
20
import org.veriblock.spv.model.Transaction
15
21
import org.veriblock.spv.serialization.MessageSerializer
16
22
import org.veriblock.spv.util.SpvEventBus
17
23
import org.veriblock.spv.util.Threading
24
+ import java.lang.IllegalArgumentException
18
25
import java.util.concurrent.ConcurrentHashMap
19
26
import java.util.concurrent.locks.ReentrantLock
20
27
import kotlin.concurrent.withLock
@@ -27,10 +34,13 @@ class PendingTransactionContainer(
27
34
) {
28
35
// TODO(warchant): use Address as a key, instead of String
29
36
private val pendingTransactionsByAddress: MutableMap <String , MutableList <Transaction >> = ConcurrentHashMap ()
30
- private val confirmedTransactionReplies: MutableMap <VbkTxId , TransactionInfo > = ConcurrentHashMap ()
31
- private val confirmedTransactions: MutableMap <VbkTxId , Transaction > = ConcurrentHashMap ()
32
37
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 ()
34
44
35
45
private val lock = ReentrantLock ()
36
46
@@ -45,48 +55,50 @@ class PendingTransactionContainer(
45
55
.sortedBy { it.value.getSignatureIndex() }
46
56
.map { it.key }
47
57
.toSet()
48
- return pendingTransactions + transactionsToMonitor
58
+ return pendingTransactions
49
59
}
50
60
51
61
fun getTransactionInfo (txId : VbkTxId ): TransactionInfo ? {
52
- confirmedTransactionReplies [txId]?.let {
62
+ pendingTransactionsInfo [txId]?.let {
53
63
return it
54
64
}
55
- if ( ! pendingTransactions.containsKey( txId)) {
56
- transactionsToMonitor.add(txId)
65
+ addedToBlockchainTransactionsInfo[ txId]?. let {
66
+ return it
57
67
}
58
68
return null
59
69
}
60
70
61
71
fun updateTransactionInfo (transactionInfo : TransactionInfo ) = lock.withLock {
62
72
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
65
75
val pendingTx = pendingTransactions[transaction.txId]
66
76
if (pendingTx != null ) {
67
- confirmedTransactions[transaction.txId] = pendingTx
68
77
if (pendingTx.getSignatureIndex() > lastConfirmedSignatureIndex) {
69
78
lastConfirmedSignatureIndex = pendingTx.getSignatureIndex()
70
79
}
71
80
}
72
81
if (transactionInfo.confirmations > 0 ) {
82
+ addedToBlockchainTransactions[transaction.txId] = pendingTx!!
83
+ addedToBlockchainTransactionsInfo[transaction.txId] = pendingTransactionsInfo[transaction.txId]!!
84
+
73
85
pendingTransactions.remove(transaction.txId)
74
- transactionsToMonitor .remove(transaction.txId)
86
+ pendingTransactionsInfo .remove(transaction.txId)
75
87
pendingTransactionsByAddress[transaction.sourceAddress]?.removeIf { it.txId == transaction.txId }
76
88
}
77
89
}
78
90
79
91
// 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 {
83
95
it.blockNumber < topConfirmedBlockHeight - 1_000
84
96
}.map {
85
97
it.transaction.txId
86
98
}
87
99
for (txId in txToRemove) {
88
- confirmedTransactionReplies .remove(txId)
89
- confirmedTransactions .remove(txId)
100
+ addedToBlockchainTransactions .remove(txId)
101
+ addedToBlockchainTransactionsInfo .remove(txId)
90
102
}
91
103
}
92
104
}
@@ -170,7 +182,6 @@ class PendingTransactionContainer(
170
182
logger.info { " All the transactions for that address will be pruned in order to prevent further transactions from being rejected." }
171
183
for (tx in newTransactions) {
172
184
pendingTransactions.remove(tx.txId)
173
- transactionsToMonitor.remove(tx.txId)
174
185
}
175
186
newTransactions.clear()
176
187
}
@@ -182,21 +193,75 @@ class PendingTransactionContainer(
182
193
}
183
194
184
195
private fun handleRemovedBestBlock (removedBlock : VeriBlockBlock ) = lock.withLock {
185
- val reorganizedTransactions = confirmedTransactionReplies .values.filter {
196
+ val reorganizedTransactions = addedToBlockchainTransactionsInfo .values.filter {
186
197
it.blockNumber == removedBlock.height
187
198
}.mapNotNull {
188
- confirmedTransactions [it.transaction.txId]
199
+ addedToBlockchainTransactions [it.transaction.txId]
189
200
}
190
201
for (transaction in reorganizedTransactions) {
191
- confirmedTransactionReplies.remove(transaction.txId)
192
- confirmedTransactions.remove(transaction.txId)
202
+ addedToBlockchainTransactions.remove(transaction.txId)
193
203
addTransaction(transaction)
194
204
}
195
205
}
196
206
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
+
197
240
enum class AddTransactionResult {
198
241
SUCCESS ,
199
242
INVALID ,
200
243
DUPLICATE
201
244
}
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
+ )
202
267
}
0 commit comments