Skip to content

Commit

Permalink
Forward-port of fix for AKI-302
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-aion committed Sep 12, 2019
2 parents 8d25135 + 733c05b commit d09947a
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 10 deletions.
12 changes: 9 additions & 3 deletions org.aion.avm.core/src/org/aion/avm/core/AvmImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,14 @@ private AvmWrappedTransactionResult runExternalInvoke(IExternalState parentKerne
AionAddress sender = tx.senderAddress;
long energyPrice = tx.energyPrice;
BigInteger value = tx.value;
BigInteger transactionCost = BigInteger.valueOf(tx.energyLimit).multiply(BigInteger.valueOf(energyPrice)).add(value);
if (!parentKernel.accountBalanceIsAtLeast(sender, transactionCost)) {

long basicTransactionCost = BillingRules.getBasicTransactionCost(tx.copyOfTransactionData());
BigInteger balanceRequired = BigInteger.valueOf(tx.energyLimit).multiply(BigInteger.valueOf(energyPrice)).add(value);

if (basicTransactionCost > tx.energyLimit) {
error = AvmInternalError.REJECTED_INVALID_ENERGY_LIMIT;
}
else if (!parentKernel.accountBalanceIsAtLeast(sender, balanceRequired)) {
error = AvmInternalError.REJECTED_INSUFFICIENT_BALANCE;
}

Expand All @@ -365,7 +371,7 @@ private AvmWrappedTransactionResult runExternalInvoke(IExternalState parentKerne
parentKernel.adjustBalance(sender, BigInteger.valueOf(tx.energyLimit).multiply(BigInteger.valueOf(energyPrice).negate()));

// Run the common logic with the parent kernel as the top-level one.
AvmWrappedTransactionResult result = commonInvoke(parentKernel, task, tx, BillingRules.getBasicTransactionCost(tx.copyOfTransactionData()));
AvmWrappedTransactionResult result = commonInvoke(parentKernel, task, tx, basicTransactionCost);

// Refund energy for transaction
BigInteger refund = BigInteger.valueOf(tx.energyLimit - result.energyUsed()).multiply(BigInteger.valueOf(energyPrice));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.aion.avm.core;

import static org.aion.avm.core.BillingRules.getBasicTransactionCost;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.math.BigInteger;
import java.util.Random;
import org.aion.kernel.AvmWrappedTransactionResult.AvmInternalError;
import org.aion.kernel.TestingState;
import org.aion.types.AionAddress;
import org.aion.types.Transaction;
Expand All @@ -15,6 +18,7 @@
import org.aion.kernel.TestingBlock;
import org.aion.types.TransactionResult;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

Expand All @@ -23,19 +27,25 @@
* after a transaction has been sent.
*/
public class TransactionAccountBalanceTest {
private static AionAddress from = TestingState.PREMINED_ADDRESS;
private static long energyLimit = 10_000_000L;
private static AionAddress from;
private static final long energyLimit = 10_000_000L;

private static long energyLimitForValueTransfer = 21_000L;
private static long energyPrice = 5;
private static TestingBlock block = new TestingBlock(new byte[32], 1, Helpers.randomAddress(), System.currentTimeMillis(), new byte[0]);
private static final long energyLimitForValueTransfer = 21_000L;
private static final long energyPrice = 5;
private static TestingBlock block;

private static TestingState kernel;
private static AvmImpl avm;

@Before
public void resetTestingState() {
block = new TestingBlock(new byte[32], 1, Helpers.randomAddress(), System.currentTimeMillis(), new byte[0]);
kernel = new TestingState(block);
from = TestingState.PREMINED_ADDRESS;
}

@BeforeClass
public static void setup() {
kernel = new TestingState(block);
avm = CommonAvmFactory.buildAvmInstanceForConfiguration(new EmptyCapabilities(), new AvmConfiguration());
}

Expand Down Expand Up @@ -113,6 +123,149 @@ public void testSenderBalanceAfterValueTransfer() {
assertEquals(senderBalance.subtract(transactionCost).subtract(value), kernel.getBalance(from));
}

@Test
public void testInsufficientBalance() {
BigInteger senderBalanceBefore = kernel.getBalance(from);
BigInteger balanceTransferCost = BigInteger.valueOf(BillingRules.BASIC_TRANSACTION_COST * energyPrice);
BigInteger value = senderBalanceBefore.subtract(balanceTransferCost).add(BigInteger.ONE);
AionAddress recipient = createNewAccountWithBalance(BigInteger.ZERO);

TransactionResult result = transferValueWithData(recipient, value, new byte[0]);

assertEquals(AvmInternalError.REJECTED_INSUFFICIENT_BALANCE.error, result.transactionStatus.causeOfError);

long energyUsed = result.energyUsed;
assertEquals(energyLimitForValueTransfer, energyUsed);

assertEquals(senderBalanceBefore, kernel.getBalance(from));
assertEquals(BigInteger.ZERO, kernel.getBalance(recipient));
}

@Test
public void testTransferEntireBalance() {
BigInteger senderBalanceBefore = kernel.getBalance(from);
BigInteger balanceTransferCost = BigInteger.valueOf(BillingRules.BASIC_TRANSACTION_COST * energyPrice);
BigInteger value = senderBalanceBefore.subtract(balanceTransferCost);
AionAddress recipient = createNewAccountWithBalance(BigInteger.ZERO);

TransactionResult result = transferValueWithData(recipient, value, new byte[0]);

assertTrue(result.transactionStatus.isSuccess());

long energyUsed = result.energyUsed;
assertEquals(energyLimitForValueTransfer, energyUsed);

assertEquals(BigInteger.ZERO, kernel.getBalance(from));
assertEquals(value, kernel.getBalance(recipient));
}

@Test
public void testTransferEntireBalanceWithData() {
BigInteger senderBalanceBefore = kernel.getBalance(from);
byte[] data = new byte[1];
long energyRequired = getBasicTransactionCost(data);
BigInteger balanceTransferCost = BigInteger.valueOf(energyRequired * energyPrice);
BigInteger value = senderBalanceBefore.subtract(balanceTransferCost);
AionAddress recipient = createNewAccountWithBalance(BigInteger.ZERO);

TransactionResult result = transferValueWithDataAndEnergyLimit(recipient, value, data, energyRequired);

assertTrue(result.transactionStatus.isSuccess());

long energyUsed = result.energyUsed;
assertEquals(energyRequired, energyUsed);

assertEquals(BigInteger.ZERO, kernel.getBalance(from));
assertEquals(value, kernel.getBalance(recipient));
}



@Test
public void testTransferEntireBalanceWithRandomData() {
BigInteger senderBalanceBefore = kernel.getBalance(from);
Random r = new Random();
byte[] data = new byte[1000];
r.nextBytes(data);
long energyRequired = getBasicTransactionCost(data);
BigInteger balanceTransferCost = BigInteger.valueOf(energyRequired * energyPrice);
BigInteger value = senderBalanceBefore.subtract(balanceTransferCost);
AionAddress recipient = createNewAccountWithBalance(BigInteger.ZERO);

TransactionResult result = transferValueWithDataAndEnergyLimit(recipient, value, data, energyRequired);

assertTrue(result.transactionStatus.isSuccess());

long energyUsed = result.energyUsed;
assertEquals(energyRequired, energyUsed);

assertEquals(BigInteger.ZERO, kernel.getBalance(from));
assertEquals(value, kernel.getBalance(recipient));
}

@Test
public void testInvalidEnergyAndInsufficientFunds() {
BigInteger senderBalanceBefore = kernel.getBalance(from);
BigInteger balanceTransferCost = BigInteger.valueOf(BillingRules.BASIC_TRANSACTION_COST * energyPrice);
BigInteger value = senderBalanceBefore.subtract(balanceTransferCost);
AionAddress recipient = createNewAccountWithBalance(BigInteger.ZERO);

TransactionResult result = transferValueWithData(recipient, value, new byte[1]);

assertEquals(AvmInternalError.REJECTED_INVALID_ENERGY_LIMIT.error, result.transactionStatus.causeOfError);
}

@Test
public void testBalanceTransferWithDataLowEnergyLimit() {
BigInteger senderBalanceBefore = kernel.getBalance(from);
BigInteger value = BigInteger.ONE;
AionAddress recipient = createNewAccountWithBalance(BigInteger.ZERO);

TransactionResult result = transferValueWithData(recipient, value, new byte[1]);

assertEquals(AvmInternalError.REJECTED_INVALID_ENERGY_LIMIT.error, result.transactionStatus.causeOfError);

long energyUsed = result.energyUsed;
assertEquals(energyLimitForValueTransfer, energyUsed);

assertEquals(senderBalanceBefore, kernel.getBalance(from));
assertEquals(BigInteger.ZERO, kernel.getBalance(recipient));
}

@Test
public void testBalanceTransferWithLotsOfDataLowEnergyLimit() {
BigInteger senderBalanceBefore = kernel.getBalance(from);
BigInteger value = BigInteger.ONE;
AionAddress recipient = createNewAccountWithBalance(BigInteger.ZERO);

TransactionResult result = transferValueWithData(recipient, value, new byte[1000000]);

assertEquals(AvmInternalError.REJECTED_INVALID_ENERGY_LIMIT.error, result.transactionStatus.causeOfError);

long energyUsed = result.energyUsed;
assertEquals(energyLimitForValueTransfer, energyUsed);

assertEquals(senderBalanceBefore, kernel.getBalance(from));
assertEquals(BigInteger.ZERO, kernel.getBalance(recipient));
}

@Test
public void testValueZeroWithDataLowEnergyLimit() {
BigInteger senderBalanceBefore = kernel.getBalance(from);
BigInteger value = BigInteger.ZERO;
AionAddress recipient = createNewAccountWithBalance(BigInteger.ZERO);

TransactionResult result = transferValueWithData(recipient, value, new byte[1]);

assertEquals(AvmInternalError.REJECTED_INVALID_ENERGY_LIMIT.error, result.transactionStatus.causeOfError);

long energyUsed = result.energyUsed;
assertEquals(energyLimitForValueTransfer, energyUsed);

assertEquals(senderBalanceBefore, kernel.getBalance(from));
assertEquals(BigInteger.ZERO, kernel.getBalance(recipient));
}

@Test
public void testMinerBalanceAfterCreate() {
BigInteger minerBalance = kernel.getBalance(block.getCoinbase());
Expand Down Expand Up @@ -238,8 +391,18 @@ private TransactionResult callContract(AionAddress contract, BigInteger value) {
}

private TransactionResult transferValue(AionAddress recipient, BigInteger value) {
return transferValueWithData(recipient, value, new byte[0]);
}

private TransactionResult transferValueWithData(AionAddress recipient, BigInteger value, byte[] data) {
kernel.generateBlock();
Transaction transaction = AvmTransactionUtil.call(from, recipient, kernel.getNonce(from), value, data, BillingRules.BASIC_TRANSACTION_COST, energyPrice);
return avm.run(TransactionAccountBalanceTest.kernel, new Transaction[] {transaction}, ExecutionType.ASSUME_MAINCHAIN, kernel.getBlockNumber()-1)[0].getResult();
}

private TransactionResult transferValueWithDataAndEnergyLimit(AionAddress recipient, BigInteger value, byte[] data, long energyLimit) {
kernel.generateBlock();
Transaction transaction = AvmTransactionUtil.call(from, recipient, kernel.getNonce(from), value, new byte[0], BillingRules.BASIC_TRANSACTION_COST, energyPrice);
Transaction transaction = AvmTransactionUtil.call(from, recipient, kernel.getNonce(from), value, data, energyLimit , energyPrice);
return avm.run(TransactionAccountBalanceTest.kernel, new Transaction[] {transaction}, ExecutionType.ASSUME_MAINCHAIN, kernel.getBlockNumber()-1)[0].getResult();
}

Expand Down

0 comments on commit d09947a

Please sign in to comment.