27
27
import org .bouncycastle .util .encoders .Hex ;
28
28
import org .ethereum .core .AccountState ;
29
29
import org .ethereum .core .Block ;
30
+ import org .ethereum .core .ImportResult ;
30
31
import org .ethereum .core .Repository ;
31
32
import org .ethereum .crypto .Keccak256Helper ;
32
33
import org .ethereum .datasource .HashMapDB ;
@@ -117,7 +118,8 @@ public static void migrateStateToUnitrieIfNeeded(RskContext ctx) throws IOExcept
117
118
118
119
// this block number has to be validated before the release to ensure the migration works fine for every user
119
120
long minimumBlockNumberToMigrate = ctx .getRskSystemProperties ().getDatabaseMigrationMinimumHeight ();
120
- Block blockToMigrate = ctx .getBlockStore ().getBestBlock ();
121
+ Block bestBlock = ctx .getBlockStore ().getBestBlock ();
122
+ Block blockToMigrate = ctx .getBlockStore ().getBlockByHash (bestBlock .getParentHash ().getBytes ());
121
123
if (blockToMigrate == null || blockToMigrate .getNumber () < minimumBlockNumberToMigrate ) {
122
124
logger .error (
123
125
"The database can't be migrated because the node wasn't up to date before upgrading. " +
@@ -127,7 +129,7 @@ public static void migrateStateToUnitrieIfNeeded(RskContext ctx) throws IOExcept
127
129
logger .error ("Reset database or continue syncing with previous version" );
128
130
// just opening the db against the unitrie directory creates certain file structure
129
131
// we clean that here in case of an error
130
- Files . deleteIfExists (unitrieDatabase );
132
+ FileUtil . recursiveDelete (unitrieDatabase . toString () );
131
133
System .exit (1 );
132
134
}
133
135
@@ -143,11 +145,31 @@ public static void migrateStateToUnitrieIfNeeded(RskContext ctx) throws IOExcept
143
145
)
144
146
);
145
147
146
- unitrieMigrationTool .migrate ();
148
+ try {
149
+ unitrieMigrationTool .migrate ();
150
+ } catch (RuntimeException e ) {
151
+ logger .error ("Couldn't migrate the database" , e );
152
+
153
+ FileUtil .recursiveDelete (unitrieDatabase .toString ());
154
+ Files .deleteIfExists (Paths .get (databaseDir , MissingOrchidStorageKeysProvider .MAPDB_FILENAME ));
155
+ System .exit (1 );
156
+ }
157
+
158
+ ctx .getBlockStore ().removeBlock (bestBlock );
159
+ if (ctx .getBlockchain ().tryToConnect (bestBlock ) != ImportResult .IMPORTED_BEST ) {
160
+ logger .error (
161
+ "The database can't be migrated because the block {} couldn't be connected after the migration." ,
162
+ bestBlock .getNumber ()
163
+ );
164
+ // just opening the db against the unitrie directory creates certain file structure
165
+ // we clean that here in case of an error
166
+ FileUtil .recursiveDelete (unitrieDatabase .toString ());
167
+ System .exit (1 );
168
+ }
147
169
}
148
170
149
171
public void migrate () {
150
- logger .info ("Migration started" );
172
+ logger .info ("Migration started. It can take up to 15 minutes " );
151
173
logger .info ("Block {}" , blockToMigrate .getNumber ());
152
174
Trie migratedTrie = migrateState (blockToMigrate );
153
175
unitrieRepository .flush ();
@@ -165,19 +187,7 @@ private Trie migrateState(Block blockToMigrate) {
165
187
throw new IllegalStateException (String .format ("Stored account state is not consistent with the expected root (%s) for block %d" , Hex .toHexString (orchidStateRoot ), blockToMigrate .getNumber ()));
166
188
}
167
189
168
- try {
169
- buildPartialUnitrie (orchidAccountsTrie , unitrieRepository );
170
- } catch (MissingContractStorageKeysException e ) {
171
- StringBuilder missingStorageKeysMessage = new StringBuilder (
172
- "We have detected an inconsistency in your database and are unable to migrate it automatically.\n " +
173
- "Please visit https://github.com/rsksmart/rskj/issues/452 for information on how to continue.\n " +
174
- "Here is the data you'll need:\n "
175
- );
176
- for (Keccak256 entry : e .getMissingStorageKeys ()) {
177
- missingStorageKeysMessage .append (entry .toHexString ()).append ("\n " );
178
- }
179
- logger .error (missingStorageKeysMessage .toString ());
180
- }
190
+ buildPartialUnitrie (orchidAccountsTrie , unitrieRepository );
181
191
182
192
byte [] lastStateRoot = unitrieRepository .getRoot ();
183
193
byte [] orchidMigratedStateRoot = trieConverter .getOrchidAccountTrieRoot (unitrieRepository .getMutableTrie ().getTrie ());
@@ -195,12 +205,11 @@ private Trie migrateState(Block blockToMigrate) {
195
205
return unitrieRepository .getMutableTrie ().getTrie ();
196
206
}
197
207
198
- private void buildPartialUnitrie (Trie orchidAccountsTrie , Repository repository ) throws MissingContractStorageKeysException {
208
+ private void buildPartialUnitrie (Trie orchidAccountsTrie , Repository repository ) {
199
209
int accountsToLog = 500 ;
200
210
int accountsCounter = 0 ;
201
211
logger .trace ("(x = {} accounts): " , accountsToLog );
202
212
Iterator <Trie .IterationElement > orchidAccountsTrieIterator = orchidAccountsTrie .getPreOrderIterator ();
203
- Collection <Keccak256 > missingStorageKeys = new HashSet <>();
204
213
while (orchidAccountsTrieIterator .hasNext ()) {
205
214
Trie .IterationElement orchidAccountsTrieElement = orchidAccountsTrieIterator .next ();
206
215
TrieKeySlice currentElementExpandedPath = orchidAccountsTrieElement .getNodeKey ();
@@ -216,23 +225,16 @@ private void buildPartialUnitrie(Trie orchidAccountsTrie, Repository repository)
216
225
byte [] codeHash = oldAccountState .getCodeHash ();
217
226
byte [] accountStateRoot = oldAccountState .getStateRoot ();
218
227
if (contractData != null ) {
219
- try {
220
- migrateContract (accountAddress , repository , contractData , codeHash , accountStateRoot );
221
- } catch (MissingContractStorageKeysException e ) {
222
- missingStorageKeys .addAll (e .getMissingStorageKeys ());
223
- }
228
+ migrateContract (accountAddress , repository , contractData , codeHash , accountStateRoot );
224
229
}
225
230
if (accountsCounter % accountsToLog == 0 ) {
226
231
logger .trace ("x" );
227
232
}
228
233
}
229
234
}
230
- if (!missingStorageKeys .isEmpty ()) {
231
- throw new MissingContractStorageKeysException (missingStorageKeys );
232
- }
233
235
}
234
236
235
- private void migrateContract (RskAddress accountAddress , Repository currentRepository , byte [] contractDataRaw , byte [] accountCodeHash , byte [] stateRoot ) throws MissingContractStorageKeysException {
237
+ private void migrateContract (RskAddress accountAddress , Repository currentRepository , byte [] contractDataRaw , byte [] accountCodeHash , byte [] stateRoot ) {
236
238
ContractData contractData = new ContractData (contractDataRaw );
237
239
238
240
boolean initialized = false ;
@@ -260,17 +262,12 @@ private void migrateContract(RskAddress accountAddress, Repository currentReposi
260
262
Keccak256 storageKeyHash = new Keccak256 (Keccak256Helper .keccak256 (rawKey ));
261
263
keccak256Cache .put (storageKeyHash , storageKey );
262
264
}
263
- Collection <Keccak256 > missingStorageKeys = new HashSet <>();
264
265
Iterator <Trie .IterationElement > inOrderIterator = contractStorageTrie .getInOrderIterator ();
265
266
while (inOrderIterator .hasNext ()) {
266
267
Trie .IterationElement iterationElement = inOrderIterator .next ();
267
268
if (iterationElement .getNode ().getValue () != null ) {
268
269
Keccak256 storageKeyHash = new Keccak256 (iterationElement .getNodeKey ().encode ());
269
270
DataWord storageKey = keccak256Cache .computeIfAbsent (storageKeyHash , missingOrchidStorageKeysProvider ::getKeccak256PreImage );
270
- if (storageKey == null ) {
271
- missingStorageKeys .add (storageKeyHash );
272
- continue ;
273
- }
274
271
275
272
byte [] value = iterationElement .getNode ().getValue ();
276
273
migratedKeysCounter ++;
@@ -284,10 +281,6 @@ private void migrateContract(RskAddress accountAddress, Repository currentReposi
284
281
currentRepository .addStorageBytes (contractAddress , storageKey , value );
285
282
}
286
283
}
287
- if (!missingStorageKeys .isEmpty ()) {
288
- logger .error ("{} keys lost" , missingStorageKeys .size ());
289
- throw new MissingContractStorageKeysException (missingStorageKeys );
290
- }
291
284
}
292
285
293
286
byte [] code = contractData .getCode ();
@@ -484,17 +477,4 @@ public byte[] retrieveValue(byte[] hash) {
484
477
public void flush () {
485
478
}
486
479
}
487
-
488
- private static class MissingContractStorageKeysException extends Exception {
489
-
490
- private final Collection <Keccak256 > missingStorageKeys ;
491
-
492
- private MissingContractStorageKeysException (Collection <Keccak256 > missingStorageKeys ) {
493
- this .missingStorageKeys = Collections .unmodifiableCollection (missingStorageKeys );
494
- }
495
-
496
- private Collection <Keccak256 > getMissingStorageKeys () {
497
- return missingStorageKeys ;
498
- }
499
- }
500
480
}
0 commit comments