use of org.aion.base.db.IContractDetails in project aion by aionnetwork.
the class AionContractDetailsTest method testExternalStorageTransition.
@Test
public void testExternalStorageTransition() {
Address address = Address.wrap(RandomUtils.nextBytes(Address.ADDRESS_LEN));
byte[] code = RandomUtils.nextBytes(512);
Map<DataWord, DataWord> elements = new HashMap<>();
AionRepositoryImpl repository = AionRepositoryImpl.createForTesting(repoConfig);
IByteArrayKeyValueDatabase externalStorage = repository.getDetailsDatabase();
AionContractDetailsImpl original = new AionContractDetailsImpl(0, 1000000);
original.setExternalStorageDataSource(externalStorage);
original.setAddress(address);
original.setCode(code);
for (int i = 0; i < IN_MEMORY_STORAGE_LIMIT / 64 + 10; i++) {
DataWord key = new DataWord(RandomUtils.nextBytes(16));
DataWord value = new DataWord(RandomUtils.nextBytes(16));
elements.put(key, value);
original.put(key, value);
}
original.syncStorage();
assertTrue(!externalStorage.isEmpty());
IContractDetails deserialized = deserialize(original.getEncoded(), externalStorage);
// adds keys for in-memory storage limit overflow
for (int i = 0; i < 10; i++) {
DataWord key = new DataWord(RandomUtils.nextBytes(16));
DataWord value = new DataWord(RandomUtils.nextBytes(16));
elements.put(key, value);
deserialized.put(key, value);
}
deserialized.syncStorage();
assertTrue(!externalStorage.isEmpty());
deserialized = deserialize(deserialized.getEncoded(), externalStorage);
Map<DataWord, DataWord> storage = deserialized.getStorage();
assertEquals(elements.size(), storage.size());
for (DataWord key : elements.keySet()) {
assertEquals(elements.get(key), storage.get(key));
}
}
use of org.aion.base.db.IContractDetails in project aion by aionnetwork.
the class AionRepositoryCache method flush.
/**
* @implNote To maintain intended functionality this method does not call
* the parent's {@code flush()} method. The changes are propagated
* to the parent through calling the parent's
* {@code updateBatch()} method.
*/
@Override
public synchronized void flush() {
// determine which accounts should get stored
HashMap<Address, AccountState> cleanedCacheAccounts = new HashMap<>();
for (Map.Entry<Address, AccountState> entry : cachedAccounts.entrySet()) {
AccountState account = entry.getValue();
if (account != null && account.isDirty() && account.isEmpty()) {
// ignore contract state for empty accounts at storage
cachedDetails.remove(entry.getKey());
} else {
cleanedCacheAccounts.put(entry.getKey(), entry.getValue());
}
}
// determine which contracts should get stored
for (Map.Entry<Address, IContractDetails<DataWord>> entry : cachedDetails.entrySet()) {
IContractDetails<DataWord> ctd = entry.getValue();
// different ContractDetails implementation
if (ctd != null && ctd instanceof ContractDetailsCacheImpl) {
ContractDetailsCacheImpl contractDetailsCache = (ContractDetailsCacheImpl) ctd;
contractDetailsCache.commit();
if (contractDetailsCache.origContract == null && repository.hasContractDetails(entry.getKey())) {
// in forked block the contract account might not exist thus
// it is created without
// origin, but on the main chain details can contain data
// which should be merged
// into a single storage trie so both branches with
// different stateRoots are valid
contractDetailsCache.origContract = repository.getContractDetails(entry.getKey());
contractDetailsCache.commit();
}
}
}
repository.updateBatch(cleanedCacheAccounts, cachedDetails);
cachedAccounts.clear();
cachedDetails.clear();
}
Aggregations