Skip to content

Commit

Permalink
Made AionTransaction immutable
Browse files Browse the repository at this point in the history
  • Loading branch information
geoff-aion committed Aug 9, 2019
1 parent 4ecb6f4 commit acb56a3
Show file tree
Hide file tree
Showing 24 changed files with 431 additions and 561 deletions.
50 changes: 0 additions & 50 deletions modAion/src/org/aion/zero/types/AionPendingTx.java
@@ -1,50 +0,0 @@
package org.aion.zero.types;

import java.math.BigInteger;
import java.util.Arrays;
import org.aion.base.AionTransaction;
import org.aion.base.TxUtil;
import org.aion.mcf.blockchain.AbstractPendingTx;

/** aion pending transaction class. */
public class AionPendingTx extends AbstractPendingTx {

public AionPendingTx(byte[] bs) {
super(bs);
}

public AionPendingTx(AionTransaction transaction) {
super(transaction, 0);
}

public AionPendingTx(AionTransaction tx, long bn) {
super(tx, bn);
}

protected void parse(byte[] bytes) {
byte[] numberBytes = new byte[bytes[0]];
byte[] txBytes = new byte[bytes.length - 1 - numberBytes.length];

System.arraycopy(bytes, 1, numberBytes, 0, numberBytes.length);
System.arraycopy(bytes, 1 + numberBytes.length, txBytes, 0, txBytes.length);

this.blockNumber = new BigInteger(numberBytes).longValue();
this.transaction = TxUtil.decode(txBytes);
}

/** Two pending transaction are equal if equal their sender + nonce */
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof AionPendingTx)) {
return false;
}

AionPendingTx that = (AionPendingTx) o;

return getSender().equals(that.getSender())
&& Arrays.equals(transaction.getNonce(), that.getTransaction().getNonce());
}
}
5 changes: 2 additions & 3 deletions modAion/test/org/aion/zero/types/AionTransactionTest.java
Expand Up @@ -25,7 +25,7 @@ private void assertTransactionEquals(AionTransaction tx, AionTransaction tx2) {
assertArrayEquals(tx.getData(), tx2.getData());
assertEquals(tx.getEnergyLimit(), tx2.getEnergyLimit());
assertEquals(tx.getEnergyPrice(), tx2.getEnergyPrice());
assertEquals(tx.getTargetVM(), tx2.getTargetVM());
assertEquals(tx.getType(), tx2.getType());

assertArrayEquals(tx.getTimestamp(), tx2.getTimestamp());
assertArrayEquals(tx.getSignature().toBytes(), tx2.getSignature().toBytes());
Expand Down Expand Up @@ -90,7 +90,6 @@ public void testTransactionCost() {
@Test
public void testTransactionCost2() {
byte[] nonce = BigInteger.ONE.toByteArray();
AionAddress to = null;
byte[] value = BigInteger.ONE.toByteArray();
byte[] data = RandomUtils.nextBytes(128);
long nrg = new DataWordImpl(1000L).longValue();
Expand All @@ -99,7 +98,7 @@ public void testTransactionCost2() {
AionTransaction tx = AionTransaction.create(
key,
nonce,
to,
null,
value,
data,
nrg,
Expand Down
Expand Up @@ -22,6 +22,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.aion.base.AionTransaction;
import org.aion.base.PooledTransaction;
import org.aion.base.TxUtil;
import org.aion.evtmgr.IEvent;
import org.aion.evtmgr.IEventMgr;
Expand Down Expand Up @@ -151,13 +152,17 @@ public void run() {

private synchronized void processTxBuffer() {
if (!txBuffer.isEmpty()) {
List<AionTransaction> txs = new ArrayList<>();

List<PooledTransaction> txs = new ArrayList<>();
try {
for (AionTxExecSummary s : txBuffer) {
txs.add(s.getTransaction());
for (AionTxExecSummary summary : txBuffer) {
txs.add(
new PooledTransaction(
summary.getTransaction(),
summary.getReceipt().getEnergyUsed()));
}

List<AionTransaction> newPending = txPool.add(txs);
List<PooledTransaction> newPending = txPool.add(txs);

if (LOGGER_TX.isTraceEnabled()) {
LOGGER_TX.trace(
Expand All @@ -169,9 +174,12 @@ private synchronized void processTxBuffer() {
int cnt = 0;
for (AionTxExecSummary summary : txBuffer) {
if (newPending.get(cnt) != null
&& !newPending.get(cnt).equals(summary.getTransaction())) {
&& !newPending
.get(cnt)
.tx
.equals(summary.getTransaction())) {
AionTxReceipt rp = new AionTxReceipt();
rp.setTransaction(newPending.get(cnt));
rp.setTransaction(newPending.get(cnt).tx);
fireTxUpdate(rp, PendingTransactionState.DROPPED, best.get());
}
cnt++;
Expand All @@ -184,7 +192,11 @@ private synchronized void processTxBuffer() {
if (LOGGER_TX.isDebugEnabled()) {
LOGGER_TX.debug("processTxBuffer tx#{}", txs.size());
}
AionImpl.inst().broadcastTransactions(txs);
List<AionTransaction> aionTransactions = new ArrayList<>();
for (PooledTransaction pooledTransaction : txs) {
aionTransactions.add(pooledTransaction.tx);
}
AionImpl.inst().broadcastTransactions(aionTransactions);
}
} catch (Exception e) {
LOGGER_TX.error("processTxBuffer throw ", e);
Expand Down Expand Up @@ -548,7 +560,7 @@ else if (cmp == 0) {
boolean added = false;

do {
TxResponse implResponse = addPendingTransactionImpl(tx, txNonce);
TxResponse implResponse = addPendingTransactionImpl(tx);
if (!added) {
txResponses.add(implResponse);
added = true;
Expand Down Expand Up @@ -579,7 +591,7 @@ else if (cmp == 0) {
// typically because of low energy
else if (bestRepoNonce(txFrom).compareTo(txNonce) < 1) {
// repay Tx
TxResponse implResponse = addPendingTransactionImpl(tx, txNonce);
TxResponse implResponse = addPendingTransactionImpl(tx);
if (implResponse.equals(TxResponse.SUCCESS)) {
newPending.add(tx);
txResponses.add(TxResponse.REPAID);
Expand Down Expand Up @@ -687,36 +699,35 @@ private void fireTxUpdate(
* Executes pending tx on the latest best block Fires pending state update
*
* @param tx transaction come from API or P2P
* @param txNonce nonce of the transaction.
* @return SUCCESS if transaction gets NEW_PENDING state, else appropriate message such as
* DROPPED, INVALID_TX, etc.
*/
private TxResponse addPendingTransactionImpl(final AionTransaction tx, BigInteger txNonce) {
private TxResponse addPendingTransactionImpl(final AionTransaction tx) {

if (!isValid(tx)) {
LOGGER_TX.error("invalid Tx [{}]", tx.toString());
fireDroppedTx(tx, "INVALID_TX");
return TxResponse.INVALID_TX;
}

if (inValidTxNrgPrice(tx)) {
if (invalidTxNrgPrice(tx)) {
LOGGER_TX.error("invalid Tx Nrg price [{}]", tx.toString());
fireDroppedTx(tx, "INVALID_TX_NRG_PRICE");
return TxResponse.INVALID_TX_NRG_PRICE;
}

AionTxExecSummary txSum;
boolean ip = inPool(txNonce, tx.getSenderAddress());
boolean ip = inPool(tx.getNonceBI(), tx.getSenderAddress());
if (ip) {
// check energy usage
AionTransaction poolTx = txPool.getPoolTx(tx.getSenderAddress(), txNonce);
PooledTransaction poolTx = txPool.getPoolTx(tx.getSenderAddress(), tx.getNonceBI());
if (poolTx == null) {
LOGGER_TX.error(
"addPendingTransactionImpl no same tx nonce in the pool {}", tx.toString());
fireDroppedTx(tx, "REPAYTX_POOL_EXCEPTION");
return TxResponse.REPAYTX_POOL_EXCEPTION;
} else {
long price = (poolTx.getEnergyPrice() << 1);
long price = (poolTx.tx.getEnergyPrice() << 1);
if (price > 0 && price <= tx.getEnergyPrice()) {
txSum = executeTx(tx, true);
} else {
Expand All @@ -739,7 +750,7 @@ private TxResponse addPendingTransactionImpl(final AionTransaction tx, BigIntege
fireTxUpdate(txSum.getReceipt(), PendingTransactionState.DROPPED, best.get());
return TxResponse.DROPPED;
} else {
tx.setNrgConsume(txSum.getReceipt().getEnergyUsed());
PooledTransaction pendingTx = new PooledTransaction(tx, txSum.getReceipt().getEnergyUsed());

if (LOGGER_TX.isTraceEnabled()) {
LOGGER_TX.trace("addPendingTransactionImpl validTx {}", tx.toString());
Expand All @@ -748,10 +759,10 @@ private TxResponse addPendingTransactionImpl(final AionTransaction tx, BigIntege
if (bufferEnable) {
txBuffer.add(txSum);
} else {
AionTransaction rtn = this.txPool.add(tx);
if (rtn != null && !rtn.equals(tx)) {
PooledTransaction rtn = this.txPool.add(pendingTx);
if (rtn != null && !rtn.equals(pendingTx)) {
AionTxReceipt rp = new AionTxReceipt();
rp.setTransaction(rtn);
rp.setTransaction(rtn.tx);

if (poolBackUp) {
backupPendingPoolRemove.add(tx.getTransactionHash().clone());
Expand All @@ -766,7 +777,7 @@ private TxResponse addPendingTransactionImpl(final AionTransaction tx, BigIntege
}
}

private boolean inValidTxNrgPrice(AionTransaction tx) {
private boolean invalidTxNrgPrice(AionTransaction tx) {
return tx.getEnergyPrice() < NRGPRICE_MIN || tx.getEnergyPrice() > NRGPRICE_MAX;
}

Expand All @@ -782,9 +793,9 @@ private void fireDroppedTx(AionTransaction tx, String error) {
fireTxUpdate(rp, PendingTransactionState.DROPPED, best.get());
}

private AionTxReceipt createDroppedReceipt(AionTransaction tx, String error) {
private AionTxReceipt createDroppedReceipt(PooledTransaction pooledTx, String error) {
AionTxReceipt txReceipt = new AionTxReceipt();
txReceipt.setTransaction(tx);
txReceipt.setTransaction(pooledTx.tx);
txReceipt.setError(error);
return txReceipt;
}
Expand Down Expand Up @@ -838,8 +849,10 @@ public synchronized void processBest(AionBlock newBlock, List receipts) {
LOGGER_TX.debug("Rollback: {}", rollback.getShortDescr());
}
List<AionTransaction> atl = rollback.getTransactionsList();
if (!atl.isEmpty()) {
this.txPool.add(atl);
for (AionTransaction atx : atl) {
/* We can add the Tx directly to the pool with a junk energyConsumed value
because all txs in the pool are going to be re-run in rerunTxsInPool(best.get()) */
txPool.add(new PooledTransaction(atx, 1));
}
rollback = blockchain.getBlockByHash(rollback.getParentHash());
}
Expand Down Expand Up @@ -880,7 +893,7 @@ public synchronized void processBest(AionBlock newBlock, List receipts) {
"PendingStateImpl.processBest: closeToNetworkBest[{}]", closeToNetworkBest);
}

updateState(best.get());
rerunTxsInPool(best.get());

txPool.updateBlkNrgLimit(best.get().getNrgLimit());

Expand Down Expand Up @@ -940,20 +953,20 @@ private void processBestInternal(Block block, List<AionTxReceipt> receipts) {

private void clearOutdated(final long blockNumber) {

List<AionTransaction> outdated = new ArrayList<>();
List<PooledTransaction> outdated = new ArrayList<>();

final long timeout = this.txPool.getOutDateTime();
for (AionTransaction tx : this.txPool.getOutdatedList()) {
outdated.add(tx);
for (PooledTransaction pooledTx : this.txPool.getOutdatedList()) {
outdated.add(pooledTx);

if (poolBackUp) {
backupPendingPoolRemove.add(tx.getTransactionHash().clone());
backupPendingPoolRemove.add(pooledTx.tx.getTransactionHash().clone());
}
// @Jay
// TODO : considering add new state - TIMEOUT
fireTxUpdate(
createDroppedReceipt(
tx, "Tx was not included into last " + timeout + " seconds"),
pooledTx, "Tx was not included into last " + timeout + " seconds"),
PendingTransactionState.DROPPED,
best.get());
}
Expand Down Expand Up @@ -1027,19 +1040,19 @@ private AionTxInfo getTransactionInfo(byte[] txHash, byte[] blockHash) {
}

@SuppressWarnings("UnusedReturnValue")
private List<AionTransaction> updateState(Block block) {
private List<AionTransaction> rerunTxsInPool(Block block) {

pendingState = repository.startTracking();

processTxBuffer();
List<AionTransaction> pendingTxl = this.txPool.snapshotAll();
List<AionTransaction> rtn = new ArrayList<>();
if (LOGGER_TX.isInfoEnabled()) {
LOGGER_TX.info("updateState - snapshotAll tx[{}]", pendingTxl.size());
LOGGER_TX.info("rerunTxsInPool - snapshotAll tx[{}]", pendingTxl.size());
}
for (AionTransaction tx : pendingTxl) {
if (LOGGER_TX.isTraceEnabled()) {
LOGGER_TX.trace("updateState - loop: " + tx.toString());
LOGGER_TX.trace("rerunTxsInPool - loop: " + tx.toString());
}

AionTxExecSummary txSum = executeTx(tx, false);
Expand All @@ -1050,7 +1063,7 @@ private List<AionTransaction> updateState(Block block) {
if (LOGGER_TX.isDebugEnabled()) {
LOGGER_TX.debug("Invalid transaction in txpool: {}", tx);
}
txPool.remove(Collections.singletonList(tx));
txPool.remove(new PooledTransaction(tx, receipt.getEnergyUsed()));

if (poolBackUp) {
backupPendingPoolRemove.add(tx.getTransactionHash().clone());
Expand Down
Expand Up @@ -171,7 +171,6 @@ private static AionBlock createBundleAndCheck(
List<AionTransaction> transactions = new ArrayList<>();

// create 400 transactions per bundle
// byte[] nonce, Address to, byte[] value, byte[] data, long nrg, long nrgPrice
for (int i = 0; i < 400; i++) {
AionAddress destAddr = new AionAddress(HashUtil.h256(accountNonce.toByteArray()));
AionTransaction sendTransaction =
Expand Down Expand Up @@ -280,7 +279,6 @@ private static AionBlock createContractBundle(

byte[] callData = Hex.decode("26121ff0");

// byte[] nonce, Address to, byte[] value, byte[] data, long nrg, long nrgPrice
for (int i = 0; i < repeat; i++) {
AionTransaction sendTransaction =
AionTransaction.create(
Expand Down
Expand Up @@ -48,7 +48,6 @@ private static AionBlock createBundleAndCheck(
List<AionTransaction> transactions = new ArrayList<>();

// create 400 transactions per bundle
// byte[] nonce, Address to, byte[] value, byte[] data, long nrg, long nrgPrice
for (int i = 0; i < 400; i++) {
AionAddress destAddr = new AionAddress(HashUtil.h256(accountNonce.toByteArray()));
AionTransaction sendTransaction =
Expand Down Expand Up @@ -113,7 +112,6 @@ private static AionBlock createContractBundle(
List<AionTransaction> transactions = new ArrayList<>();

// create 400 transactions per bundle
// byte[] nonce, Address to, byte[] value, byte[] data, long nrg, long nrgPrice
for (int i = 0; i < 400; i++) {
AionAddress destAddr = new AionAddress(HashUtil.h256(accountNonce.toByteArray()));
AionTransaction sendTransaction =
Expand Down
2 changes: 0 additions & 2 deletions modAionImpl/test/org/aion/zero/impl/BlockchainEnergyTest.java
Expand Up @@ -63,8 +63,6 @@ public void testEnergyUsageRecorded() {
// TODO: where is the 21000 defined? bad to define magic variables
int amount = (int) (bc.getGenesis().getNrgLimit() / DEFAULT_TX_AMOUNT);

// (byte[] nonce, byte[] from, byte[] to, byte[] value, byte[] data, byte[] nrg, byte[]
// nrgPrice)
List<AionTransaction> txs = new ArrayList<>();
ECKey key = bundle.privateKeys.get(0);
for (int i = 0; i < amount; i++) {
Expand Down

0 comments on commit acb56a3

Please sign in to comment.