Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Post-fork transaction validation #879

Merged
merged 4 commits into from
Apr 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions modAionImpl/src/org/aion/zero/impl/AionBlockchainImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
import org.aion.interfaces.db.RepositoryCache;
import org.aion.log.AionLoggerFactory;
import org.aion.log.LogEnum;
import org.aion.mcf.core.FastImportResult;
import org.aion.mcf.core.AccountState;
import org.aion.mcf.core.FastImportResult;
import org.aion.mcf.core.ImportResult;
import org.aion.mcf.db.IBlockStorePow;
import org.aion.mcf.db.TransactionStore;
Expand Down Expand Up @@ -1170,16 +1170,15 @@ private boolean isValid(AionBlock block) {
.anyMatch(
tx ->
!TXValidator.isValid(tx)
|| !TransactionTypeValidator.isValid(
tx.getTargetVM()))) {
|| !TransactionTypeValidator.isValid(tx))) {
LOG.error("Some transactions in the block are invalid");
if (TX_LOG.isDebugEnabled()) {
for (AionTransaction tx : txs) {
TX_LOG.debug(
"Tx valid ["
+ TXValidator.isValid(tx)
+ "]. Type valid ["
+ TransactionTypeValidator.isValid(tx.getTargetVM())
+ TransactionTypeValidator.isValid(tx)
+ "]\n"
+ tx.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.aion.interfaces.block.Constant;
import org.aion.interfaces.db.Repository;
import org.aion.interfaces.db.RepositoryCache;
import org.aion.evtmgr.IEvent;
import org.aion.evtmgr.IEventMgr;
import org.aion.evtmgr.IHandler;
import org.aion.evtmgr.impl.callback.EventCallback;
import org.aion.evtmgr.impl.es.EventExecuteService;
import org.aion.evtmgr.impl.evt.EventBlock;
import org.aion.evtmgr.impl.evt.EventTx;
import org.aion.interfaces.block.Constant;
import org.aion.interfaces.db.Repository;
import org.aion.interfaces.db.RepositoryCache;
import org.aion.log.AionLoggerFactory;
import org.aion.log.LogEnum;
import org.aion.mcf.blockchain.IPendingStateInternal;
Expand Down Expand Up @@ -441,7 +441,7 @@ public synchronized TxResponse addPendingTransaction(AionTransaction tx) {
}

public boolean isValid(AionTransaction tx) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method can be a static method?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It cannot be changed to static because of the interface for the pending state.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay.

return TXValidator.isValid(tx) && TransactionTypeValidator.isValid(tx.getTargetVM());
return TXValidator.isValid(tx) && TransactionTypeValidator.isValid(tx);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,41 @@

import static org.aion.mcf.valid.TransactionTypeRule.isValidTransactionType;

import org.aion.interfaces.tx.Transaction;
import org.aion.zero.types.AionTransaction;

/**
* Validator for the type field of transactions allowed by the network. The transaction types
* currently correlate with which virtual machines are enabled. This field mainly impacts contract
* creation. Contracts created using the default transaction type should be deployed on the FastVM.
* AVM contracts creations/transactions are declared valid only if the AVM is enabled from the
* configuration and the transaction has the correct type associated with the AVM.
* currently correlate with which virtual machines are enabled.
*
* @author Alexandra Roatis
*/
public class TransactionTypeValidator {

private static boolean avmEnabled;

public static void enableAvmCheck(boolean enableAVM) {
avmEnabled = enableAVM;
}

public static boolean isValid(byte type) {
return true;
}

public static boolean isValidAfterFork(byte type) {
return isValidTransactionType(type);
/**
* Validates the transaction type as follows:
*
* <ol>
* <li>Any transaction type is allowed before the 0.4.0 fork which enables the use of the AVM.
* <li>Only the transaction types listed in {@link org.aion.mcf.tx.TransactionTypes#ALL} are
* valid after the fork.
* <li>Contract deployments must have the transaction types from the set {@link
* org.aion.mcf.tx.TransactionTypes#CREATE}.
* <li>Transactions that are not contract deployments must have the transaction type {@link
* org.aion.mcf.tx.TransactionTypes#DEFAULT}
* </ol>
*
* @implNote Delegates the check to {@link
* org.aion.mcf.valid.TransactionTypeRule#isValidTransactionType(Transaction)}.
* @param transaction the transaction to be validated
* @return {@code true} is the transaction satisfies the rule described above; {@code false}
* otherwise
*/
public static boolean isValid(AionTransaction transaction) {
/*
TODO: this class could be replaced by org.aion.mcf.valid.TransactionTypeRule
when the specification of modMcf and modAionImpl are properly defined and refactored
*/
return isValidTransactionType(transaction);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.aion.zero.impl.consensus;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
Expand All @@ -8,15 +9,18 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.aion.crypto.ECKey;
import org.aion.mcf.core.ImportResult;
import org.aion.mcf.valid.TransactionTypeRule;
import org.aion.types.Address;
import org.aion.util.conversions.Hex;
import org.aion.vm.VirtualMachineProvider;
import org.aion.zero.impl.StandaloneBlockchain;
import org.aion.zero.impl.StandaloneBlockchain.Builder;
import org.aion.zero.impl.StandaloneBlockchain.Bundle;
import org.aion.util.conversions.Hex;
import org.aion.zero.impl.types.AionBlock;
import org.aion.zero.impl.types.AionBlockSummary;
import org.aion.zero.impl.valid.TransactionTypeValidator;
import org.aion.zero.types.AionTransaction;
import org.aion.zero.types.AionTxReceipt;
import org.apache.commons.lang3.tuple.Pair;
Expand Down Expand Up @@ -65,6 +69,90 @@ public void tearDown() {
}
}

@Test
public void testTransactionTypeBeforeTheFork() {
// ensure that the fork was not triggered
TransactionTypeRule.disallowAVMContractTransaction();

BigInteger amount = BigInteger.TEN.pow(12).add(BigInteger.valueOf(293_865));
BigInteger initialBalance = getBalance(Address.wrap(SENDER_ADDR));
assertThat(initialBalance).isEqualTo(SENDER_BALANCE);
assertThat(this.blockchain.getMinerCoinbase().toBytes()).isEqualTo(MINER);

// get contract address from precompiled factory
Address to =
Address.wrap("a0123456a89a6ffbfdc45782771fba3f5e9da36baa69444f8f95e325430463e7");

// Make balance transfer transaction to precompiled contract.
ECKey key = org.aion.crypto.ECKeyFac.inst().fromPrivate(SENDER_KEY);
AionTransaction transaction =
new AionTransaction(
BigInteger.ZERO.toByteArray(),
to,
amount.toByteArray(),
new byte[0],
2_000_000,
ENERGY_PRICE,
(byte) 11); // legal type before the fork
transaction.sign(key);

// check that the transaction is valid
assertThat(TransactionTypeValidator.isValid(transaction)).isTrue();

// Process the transaction.
Pair<ImportResult, AionBlockSummary> results = processTransactions(transaction, 1);

// ensure transaction and block were valid
AionBlockSummary blockSummary = results.getRight();
AionTxReceipt receipt = blockSummary.getSummaries().get(0).getReceipt();
assertThat(receipt.isSuccessful()).isTrue();
assertThat(receipt.getEnergyUsed()).isEqualTo(21000);
}

@Test
public void testTransactionTypeAfterTheFork() {
// triggering fork changes
TransactionTypeRule.allowAVMContractTransaction();

BigInteger amount = BigInteger.TEN.pow(12).add(BigInteger.valueOf(293_865));
BigInteger initialBalance = getBalance(Address.wrap(SENDER_ADDR));
assertThat(initialBalance).isEqualTo(SENDER_BALANCE);
assertThat(this.blockchain.getMinerCoinbase().toBytes()).isEqualTo(MINER);

// get contract address from precompiled factory
Address to =
Address.wrap("a0123456a89a6ffbfdc45782771fba3f5e9da36baa69444f8f95e325430463e7");

// Make balance transfer transaction to precompiled contract.
ECKey key = org.aion.crypto.ECKeyFac.inst().fromPrivate(SENDER_KEY);
AionTransaction transaction =
new AionTransaction(
BigInteger.ZERO.toByteArray(),
to,
amount.toByteArray(),
new byte[0],
2_000_000,
ENERGY_PRICE,
(byte) 11); // illegal type after the fork
transaction.sign(key);

// check that the transaction is not valid
assertThat(TransactionTypeValidator.isValid(transaction)).isFalse();

// Process the transaction.
AionBlock parentBlock = this.blockchain.getRepository().blockStore.getBestBlock();
AionBlock block =
this.blockchain.createNewBlock(
parentBlock, Collections.singletonList(transaction), false);
Pair<ImportResult, AionBlockSummary> results =
this.blockchain.tryToConnectAndFetchSummary(block);

assertThat(results.getLeft()).isEqualTo(ImportResult.INVALID_BLOCK);

// cleaning up for future tests
TransactionTypeRule.disallowAVMContractTransaction();
}

private static final String RECIPIENT1 =
"a04272bb5f935fb170baf2998cb25dd15cc5794e7c5bac7241bec00c4971c7f8";
private static final String STATE_ROOT1 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ private AionTransaction makeValueTransferTransaction(
new byte[0],
2_000_000,
this.energyPrice,
(byte) 0x1);
TransactionTypes.DEFAULT);
transaction.sign(sender);
return transaction;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.aion.types.Address;
import org.aion.crypto.ECKey;
import org.aion.mcf.core.ImportResult;
import org.aion.mcf.tx.TransactionTypes;
import org.aion.mcf.vm.types.DataWordImpl;
import org.aion.types.Address;
import org.aion.util.conversions.Hex;

import org.aion.zero.impl.StandaloneBlockchain;
import org.aion.zero.impl.types.AionBlock;
import org.aion.zero.impl.types.AionBlockSummary;
Expand Down Expand Up @@ -158,7 +158,7 @@ private AionTransaction makeFvmContractCreateTransaction(ECKey sender, BigIntege
contractBytes,
5_000_000,
this.energyPrice,
(byte) 0x01);
TransactionTypes.FVM_CREATE_CODE);
transaction.sign(this.deployerKey);
return transaction;
}
Expand All @@ -178,7 +178,7 @@ private AionTransaction makeFvmContractCallTransaction(
callBytes,
2_000_000,
this.energyPrice,
(byte) 0x01);
TransactionTypes.DEFAULT);
transaction.sign(this.deployerKey);
return transaction;
}
Expand All @@ -197,7 +197,7 @@ private int getDeployedTickerCountValue(ECKey sender, BigInteger nonce, Address
callBytes,
2_000_000,
this.energyPrice,
(byte) 0x01);
TransactionTypes.DEFAULT);
transaction.sign(this.deployerKey);

AionBlockSummary summary =
Expand Down
39 changes: 23 additions & 16 deletions modAionImpl/test/org/aion/zero/impl/vm/InvalidBlockTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.mongodb.client.model.Collation;
import org.aion.avm.core.dappreading.JarBuilder;
import org.aion.avm.core.util.CodeAndArguments;
import org.aion.crypto.ECKey;
import org.aion.mcf.core.ImportResult;
import org.aion.mcf.tx.TransactionTypes;
import org.aion.types.Address;
import org.aion.vm.VirtualMachineProvider;
import org.aion.zero.impl.StandaloneBlockchain;
Expand Down Expand Up @@ -64,35 +63,44 @@ public void tearDown() {

@Test
public void test() {
BigInteger nonce = this.blockchain.getRepository().getNonce(Address.wrap(this.deployerKey.getAddress()));
BigInteger nonce =
this.blockchain
.getRepository()
.getNonce(Address.wrap(this.deployerKey.getAddress()));
List<AionTransaction> transactions = makeTransactions(10, nonce);
Collections.shuffle(transactions);

AionBlock parent = this.blockchain.getBestBlock();
AionBlock block = this.blockchain.createNewBlock(parent, transactions, false);

Pair<ImportResult, AionBlockSummary> res = this.blockchain.tryToConnectAndFetchSummary(block);
Pair<ImportResult, AionBlockSummary> res =
this.blockchain.tryToConnectAndFetchSummary(block);
System.out.println(res.getLeft());
System.out.println(res.getRight().getReceipts());
}

private List<AionTransaction> makeTransactions(int num, BigInteger initialNonce) {
List<AionTransaction> transactions = new ArrayList<>();

byte[] jar = new CodeAndArguments(JarBuilder.buildJarForMainAndClassesAndUserlib(Contract.class), new byte[0]).encodeToBytes();
byte[] jar =
new CodeAndArguments(
JarBuilder.buildJarForMainAndClassesAndUserlib(Contract.class),
new byte[0])
.encodeToBytes();
BigInteger nonce = initialNonce;

for (int i = 0; i < num; i++) {

AionTransaction transaction = new AionTransaction(
nonce.toByteArray(),
Address.wrap(this.deployerKey.getAddress()),
null,
BigInteger.ZERO.toByteArray(),
jar,
5_000_000L,
10_000_000_000L,
(byte) 0x0f);
AionTransaction transaction =
new AionTransaction(
nonce.toByteArray(),
Address.wrap(this.deployerKey.getAddress()),
null,
BigInteger.ZERO.toByteArray(),
jar,
5_000_000L,
10_000_000_000L,
TransactionTypes.AVM_CREATE_CODE);
transaction.sign(this.deployerKey);

transactions.add(transaction);
Expand All @@ -101,5 +109,4 @@ private List<AionTransaction> makeTransactions(int num, BigInteger initialNonce)

return transactions;
}

}
}
Loading