@@ -12,6 +12,7 @@ import org.ergoplatform.nodeView.state.UtxoState
12
12
import org .ergoplatform .nodeView .wallet .ErgoWallet
13
13
import org .ergoplatform .nodeView .wallet .ErgoWalletActorMessages .ScanBoxesFromUtxoSnapshot
14
14
import org .ergoplatform .serialization .ManifestSerializer
15
+ import org .ergoplatform .serialization .ManifestSerializer .MainnetManifestDepth
15
16
import org .ergoplatform .wallet .boxes .ErgoBoxSerializer
16
17
import scorex .crypto .authds .avltree .batch .VersionedLDBAVLStorage
17
18
import scorex .crypto .hash .Blake2b256
@@ -25,6 +26,12 @@ import scala.concurrent.Await
25
26
import scala .concurrent .ExecutionContext .Implicits .global
26
27
import scala .concurrent .duration .Duration
27
28
29
+ /**
30
+ * This class is used to provide the current UTXO set for wallet scans when bootstrapping
31
+ * by UTXO set snapshot. This is done by creating a snapshot of the UTXO set, deserializing
32
+ * the raw bytes to ErgoBoxes and sending them to the wallet actor in chunks.
33
+ * @param nodeView - NodeView actor to get wallet and UTXOs from
34
+ */
28
35
class UtxoSetScanner (nodeView : ActorRef ) extends Actor with ScorexLogging {
29
36
30
37
private var history : ErgoHistory = _
@@ -33,22 +40,39 @@ class UtxoSetScanner(nodeView: ActorRef) extends Actor with ScorexLogging {
33
40
private implicit val timeout : Timeout = Timeout (10 , TimeUnit .SECONDS )
34
41
private implicit val duration : Duration = Duration .create(10 , TimeUnit .SECONDS )
35
42
43
+ /**
44
+ * Internal buffer that holds deserialized AVL subtrees until they are sent to wallet
45
+ */
36
46
private val chunkBuffer : ArrayBuffer [(ModifierId ,Array [ErgoBox ])] = ArrayBuffer .empty[(ModifierId ,Array [ErgoBox ])]
37
47
48
+ /**
49
+ * Reads the current progress of the scanner.
50
+ * @return (current segment, total segments)
51
+ */
38
52
private def readProgress (): (Int , Int ) =
39
53
historyStorage.getIndex(utxoSetScanProgressKey).map(ByteBuffer .wrap).map { buffer =>
40
54
val current = buffer.getInt
41
55
val total = buffer.getInt
42
56
(current, total)
43
57
}.getOrElse((0 , 0 ))
44
58
59
+ /**
60
+ * Writes progress to db.
61
+ * @param current - current retrieved segment
62
+ * @param total - total segment count
63
+ */
45
64
private def writeProgress (current : Int , total : Int ): Unit = {
46
65
val buffer : ByteBuffer = ByteBuffer .allocate(8 )
47
66
buffer.putInt(current)
48
67
buffer.putInt(total)
49
68
historyStorage.insert(Array ((utxoSetScanProgressKey, buffer.array)), Array .empty[BlockSection ])
50
69
}
51
70
71
+ /**
72
+ * Send deserialized AVL subtrees to wallet for scanning.
73
+ * @param wallet - wallet to send to
74
+ * @param current - current retrieved segment
75
+ */
52
76
private def sendBufferToWallet (wallet : ErgoWallet , current : Int ): Unit = {
53
77
wallet.scanUtxoSnapshot(ScanBoxesFromUtxoSnapshot (chunkBuffer, current, MainnetTotal ))
54
78
writeProgress(current, MainnetTotal )
@@ -69,11 +93,12 @@ class UtxoSetScanner(nodeView: ActorRef) extends Actor with ScorexLogging {
69
93
)
70
94
71
95
val initialized : Boolean = Await .result(wallet.getWalletStatus.map(_.initialized), duration)
72
- if (! initialized) return
96
+ if (! initialized) // wallet is not initialized
97
+ return
73
98
74
99
log.info(s " Starting UTXO set snapshot scan for $total chunks " )
75
100
76
- state.persistentProver.storage.asInstanceOf [VersionedLDBAVLStorage ].iterateAVLTree(current) { subtree =>
101
+ state.persistentProver.storage.asInstanceOf [VersionedLDBAVLStorage ].iterateAVLTree(current, MainnetManifestDepth ) { subtree =>
77
102
current += 1
78
103
79
104
chunkBuffer += ((
@@ -93,6 +118,7 @@ class UtxoSetScanner(nodeView: ActorRef) extends Actor with ScorexLogging {
93
118
94
119
if (current == total) {
95
120
log.info(s " Successfully scanned $total Utxo set subtrees " )
121
+ // send newest block to wallet, if blocks were applied since scan began it will go back to scan them
96
122
wallet.scanPersistent(history.bestFullBlockOpt.get)
97
123
}else {
98
124
log.error(s " Inconsistent Utxo set scan state: $current scanned subtrees out of $total" )
@@ -101,27 +127,40 @@ class UtxoSetScanner(nodeView: ActorRef) extends Actor with ScorexLogging {
101
127
}
102
128
103
129
override def receive : Receive = {
104
- case StartUtxoSetScanWithHistory (history : ErgoHistory ) =>
130
+ case InitializeUtxoSetScannerWithHistory (history : ErgoHistory ) =>
105
131
this .history = history
106
132
run()
107
133
case StartUtxoSetScan (rescan : Boolean ) =>
108
- if (readProgress()._1 == 0 || rescan) writeProgress(0 , MainnetTotal )
134
+ if (readProgress()._1 == 0 || //
135
+ rescan) // start over UTXO set scan
136
+ writeProgress(0 , MainnetTotal )
109
137
run()
110
138
}
111
139
112
140
override def preStart (): Unit = {
113
- context.system.eventStream.subscribe(self, classOf [StartUtxoSetScanWithHistory ])
141
+ context.system.eventStream.subscribe(self, classOf [InitializeUtxoSetScannerWithHistory ])
114
142
context.system.eventStream.subscribe(self, classOf [StartUtxoSetScan ])
115
143
}
116
144
117
145
}
118
146
119
147
object UtxoSetScanner {
120
148
121
- case class StartUtxoSetScanWithHistory (history : ErgoHistory )
149
+ /**
150
+ * Initialize UTXO set scanner with database and try continuing scan if possible
151
+ * @param history - database handle
152
+ */
153
+ case class InitializeUtxoSetScannerWithHistory (history : ErgoHistory )
122
154
155
+ /**
156
+ * Start scanning UTXO set, or continue if the scan was interrupted, or start over if rescan = true
157
+ * @param rescan - whether to start over or just continue scan
158
+ */
123
159
case class StartUtxoSetScan (rescan : Boolean )
124
160
161
+ /**
162
+ * Number of subtrees to divide AVL tree to
163
+ */
125
164
private val MainnetTotal : Int = math.pow(2 , ManifestSerializer .MainnetManifestDepth ).toInt
126
165
127
166
private val utxoSetScanProgressKey : ByteArrayWrapper =
0 commit comments