use of org.aion.base.AccountState in project aion by aionnetwork.
the class TransactionCreateSpecificationTests method deployInternalAvmContractOnTopOfAddressWithStorageUsingAvmVersion2_DeployAndRequireSuccess.
@Test
public void deployInternalAvmContractOnTopOfAddressWithStorageUsingAvmVersion2_DeployAndRequireSuccess() throws VmFatalException {
// Deploy AVM contract.
AionTransaction deployTxAvm = BlockchainTestUtils.deployAvmContractTransaction(AvmContract.DEPLOY_INTERNAL, resourceProvider.factoryForVersion2, SENDER_KEY, BigInteger.ZERO);
AionAddress contract = TxUtil.calculateContractAddress(deployTxAvm);
Pair<Block, ImportResult> resultImport = BlockchainTestUtils.addMiningBlock(blockchain, blockchain.getBestBlock(), List.of(deployTxAvm));
assertThat(resultImport.getRight()).isEqualTo(ImportResult.IMPORTED_BEST);
// Call AVM contract to deploy new internal AVM contract (version with required success).
long internalLimit = 1_000_000;
AionTransaction deployInternal = BlockchainTestUtils.callSimpleAvmContractTransaction(resourceProvider.factoryForVersion2, SENDER_KEY, BigInteger.ONE, contract, "deployAndRequireSuccess", deployTxAvm.getData(), internalLimit);
AionAddress internalContract = new AionAddress(Hex.decode("a0268090998a99666b72cc452b9307438a34341047d9e0d7b92c9207bf413655"));
assertThat(blockchain.getRepository().hasAccountState(internalContract)).isFalse();
// Manipulate the repository to have a non-default storage value.
RepositoryCache cache = blockchain.getRepository().startTracking();
cache.addStorageRow(internalContract, ByteArrayWrapper.wrap(RandomUtils.nextBytes(16)), ByteArrayWrapper.wrap(RandomUtils.nextBytes(16)));
cache.saveVmType(internalContract, InternalVmType.AVM);
cache.flushTo(cache.getParent(), true);
// Check assumptions about contract state.
AccountState contractState = (AccountState) blockchain.getRepository().startTracking().getAccountState(internalContract);
assertThat(contractState.getBalance()).isEqualTo(BigInteger.ZERO);
assertThat(contractState.getNonce()).isEqualTo(BigInteger.ZERO);
assertThat(contractState.getStateRoot()).isNotEqualTo(EMPTY_TRIE_HASH);
assertThat(contractState.getCodeHash()).isEqualTo(EMPTY_DATA_HASH);
byte[] oldRoot = contractState.getStateRoot();
// Next, process the deploy transaction with fork040 enabled.
AionTxExecSummary result = executeTransaction(deployInternal, true);
assertThat(result.isFailed()).isTrue();
assertThat(result.getReceipt().getError()).isEqualTo("reverted");
assertThat(result.getNrgUsed()).isLessThan(BigInteger.valueOf(deployInternal.getEnergyLimit()));
assertThat(result.getLogs()).isEmpty();
InternalTransaction itx = result.getInternalTransactions().get(0);
assertThat(itx.isCreate).isTrue();
assertThat(TxUtil.calculateContractAddress(itx)).isEqualTo(internalContract);
assertThat(itx.isRejected).isTrue();
assertThat(itx.energyLimit).isEqualTo(internalLimit);
assertThat(result.getNrgUsed()).isGreaterThan(BigInteger.valueOf(itx.energyLimit));
contractState = (AccountState) blockchain.getRepository().startTracking().getAccountState(internalContract);
assertThat(contractState.getBalance()).isEqualTo(BigInteger.ZERO);
assertThat(contractState.getNonce()).isEqualTo(BigInteger.ZERO);
assertThat(contractState.getStateRoot()).isEqualTo(oldRoot);
assertThat(contractState.getCodeHash()).isEqualTo(EMPTY_DATA_HASH);
}
use of org.aion.base.AccountState in project aion by aionnetwork.
the class AionRepositoryCacheTest method testLoadAccountState_withExistingAccountFromRepository.
@Test
public void testLoadAccountState_withExistingAccountFromRepository() {
AionAddress address = new AionAddress(RandomUtils.nextBytes(AionAddress.LENGTH));
byte[] code = RandomUtils.nextBytes(100);
byte[] codeHash = h256(code);
// initialize contract in the repository
RepositoryCache tempCache = repository.startTracking();
tempCache.createAccount(address);
tempCache.saveCode(address, code);
tempCache.saveVmType(address, InternalVmType.FVM);
tempCache.addBalance(address, BigInteger.TEN);
tempCache.flushTo(repository, true);
AionRepositoryCache tracker = (AionRepositoryCache) cache.startTracking();
assertThat(tracker.hasAccountState(address)).isTrue();
assertThat(tracker.hasContractDetails(address)).isTrue();
assertThat(tracker.cachedAccounts.containsKey(address)).isFalse();
assertThat(tracker.cachedDetails.containsKey(address)).isFalse();
// load account state for new address
AccountState account = tracker.getAccountState(address);
InnerContractDetails details = (InnerContractDetails) tracker.getContractDetails(address);
// 1. Ensure new account and details were created
assertThat(tracker.cachedAccounts.containsKey(address)).isTrue();
assertThat(tracker.cachedDetails.containsKey(address)).isTrue();
assertThat(account.getBalance()).isEqualTo(BigInteger.TEN);
assertThat(account.getNonce()).isEqualTo(BigInteger.ZERO);
assertThat(account.getCodeHash()).isEqualTo(codeHash);
assertThat(account.getStateRoot()).isEqualTo(EMPTY_TRIE_HASH);
assertThat(details.origContract).isNotNull();
assertThat(details.getCode(codeHash)).isEqualTo(code);
assertThat(details.getVmType()).isEqualTo(InternalVmType.FVM);
assertThat(details.isDeleted()).isFalse();
assertThat(details.isDirty()).isFalse();
// 2. Ensure that the cache does not contain the account and details
assertThat(cache.cachedAccounts.containsKey(address)).isFalse();
assertThat(cache.cachedDetails.containsKey(address)).isFalse();
// 3. Ensure that the repository does contain the account and details
assertThat(repository.hasAccountState(address)).isTrue();
assertThat(repository.hasContractDetails(address)).isTrue();
}
use of org.aion.base.AccountState in project aion by aionnetwork.
the class AionRepositoryCache method flushTo.
@Override
public void flushTo(Repository other, boolean clearStateAfterFlush) {
lock.lock();
try {
// determine which accounts should get stored
HashMap<AionAddress, AccountState> cleanedCacheAccounts = new HashMap<>();
for (Map.Entry<AionAddress, 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<AionAddress, ContractDetail> entry : cachedDetails.entrySet()) {
InnerContractDetails contractDetailsCache = (InnerContractDetails) entry.getValue();
contractDetailsCache.commit();
if (contractDetailsCache.origContract == null && other.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 = (ContractDetails) other.getContractDetails(entry.getKey());
contractDetailsCache.commit();
}
}
other.updateBatch(cleanedCacheAccounts, cachedDetails, cachedTransformedCode);
if (clearStateAfterFlush) {
cachedAccounts.clear();
cachedDetails.clear();
cachedTransformedCode.clear();
}
} finally {
lock.unlock();
}
}
use of org.aion.base.AccountState in project aion by aionnetwork.
the class AionRepositoryCache method getLocalAccountState.
private AccountState getLocalAccountState(AionAddress address) {
// check if the account is cached locally
AccountState accountState = this.cachedAccounts.get(address);
// when the account is not cached load it from the repository
if (accountState == null) {
Pair<AccountState, InnerContractDetails> pair = getAccountStateFromParent(address);
this.cachedAccounts.put(address, pair.getLeft());
this.cachedDetails.put(address, pair.getRight());
accountState = pair.getLeft();
}
return accountState;
}
use of org.aion.base.AccountState in project aion by aionnetwork.
the class AionRepositoryImpl method updateBatch.
@Override
public void updateBatch(Map<AionAddress, AccountState> stateCache, Map<AionAddress, ContractDetail> detailsCache, Map<AionAddress, TransformedCodeInfoInterface> transformedCodeCache) {
rwLock.writeLock().lock();
try {
for (Map.Entry<AionAddress, AccountState> entry : stateCache.entrySet()) {
AionAddress address = entry.getKey();
AccountState accountState = entry.getValue();
ContractDetails contractDetails = (ContractDetails) detailsCache.get(address);
if (accountState.isDeleted()) {
// TODO-A: batch operations here
try {
worldState.delete(address.toByteArray());
} catch (Exception e) {
LOG.error("key deleted exception [{}]", e.toString());
}
if (LOG.isDebugEnabled()) {
LOG.debug("key deleted <key={}>", Hex.toHexString(address.toByteArray()));
}
} else {
if (!contractDetails.isDirty() || (contractDetails.getVmType() == InternalVmType.EITHER && !ContractInfo.isPrecompiledContract(address))) {
// ContractState class
if (accountState.isDirty()) {
updateAccountState(address, accountState);
if (LOG.isTraceEnabled()) {
LOG.trace("update: [{}],nonce: [{}] balance: [{}] [{}]", Hex.toHexString(address.toByteArray()), accountState.getNonce(), accountState.getBalance(), Hex.toHexString(contractDetails.getStorageHash()));
}
}
continue;
}
InnerContractDetails contractDetailsCache = (InnerContractDetails) contractDetails;
if (contractDetailsCache.origContract == null) {
contractDetailsCache.origContract = detailsDS.newContractDetails(address, contractDetailsCache.getVmType());
contractDetailsCache.commit();
}
StoredContractDetails parentDetails = (StoredContractDetails) contractDetailsCache.origContract;
// this method requires the encoding functionality therefore can be applied only to StoredContractDetails
detailsDS.update(address, parentDetails);
accountState.setStateRoot(parentDetails.getStorageHash());
updateAccountState(address, accountState);
cachedContractIndex.put(address, Pair.of(ByteArrayWrapper.wrap(accountState.getCodeHash()), parentDetails.getVmType()));
if (LOG.isTraceEnabled()) {
LOG.trace("update: [{}],nonce: [{}] balance: [{}] [{}]", Hex.toHexString(address.toByteArray()), accountState.getNonce(), accountState.getBalance(), Hex.toHexString(parentDetails.getStorageHash()));
}
}
}
for (Map.Entry<AionAddress, TransformedCodeInfoInterface> entry : transformedCodeCache.entrySet()) {
for (Map.Entry<ByteArrayWrapper, Map<Integer, byte[]>> infoMap : ((TransformedCodeInfo) entry.getValue()).transformedCodeMap.entrySet()) {
for (Map.Entry<Integer, byte[]> innerEntry : infoMap.getValue().entrySet()) {
setTransformedCode(entry.getKey(), infoMap.getKey().toBytes(), innerEntry.getKey(), innerEntry.getValue());
}
}
}
LOG.trace("updated: detailsCache.size: {}", detailsCache.size());
stateCache.clear();
detailsCache.clear();
transformedCodeCache.clear();
} finally {
rwLock.writeLock().unlock();
}
}
Aggregations