diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/AionBlockchainImpl.java b/modAionImpl/src/org/aion/zero/impl/blockchain/AionBlockchainImpl.java index 9bf5f6472a..6556355039 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/AionBlockchainImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/AionBlockchainImpl.java @@ -53,6 +53,7 @@ import org.aion.zero.impl.core.FastImportResult; import org.aion.zero.impl.core.ImportResult; import org.aion.zero.impl.db.TransactionStore; +import org.aion.zero.impl.forks.ForkUtility; import org.aion.zero.impl.trie.Trie; import org.aion.zero.impl.trie.TrieImpl; import org.aion.zero.impl.trie.TrieNodeResult; @@ -121,13 +122,13 @@ public class AionBlockchainImpl implements IAionBlockchain { private static final int DIFFICULTY_BYTES = 16; private static final Logger LOGGER_VM = AionLoggerFactory.getLogger(LogEnum.VM.toString()); static long fork040BlockNumber = -1L; - private static long FORK_5_BLOCK_NUMBER = Long.MAX_VALUE; private static boolean fork040Enable; private final GrandParentBlockHeaderValidator preUnityGrandParentBlockHeaderValidator; private final GreatGrandParentBlockHeaderValidator unityGreatGrandParentBlockHeaderValidator; private final ParentBlockHeaderValidator preUnityParentBlockHeaderValidator; private final ParentBlockHeaderValidator unityParentBlockHeaderValidator; private final StakingContractHelper stakingContractHelper; + public final ForkUtility forkUtility; public final BeaconHashValidator beaconHashValidator; /** @@ -230,16 +231,16 @@ protected AionBlockchainImpl( } this.energyLimitStrategy = config.getEnergyLimitStrategy(); + // initialize fork utility + this.forkUtility = new ForkUtility(); // forks are disabled by default Optional maybeFork050 = load050ForkNumberFromConfig(CfgAion.inst()); - if(! maybeFork050.isPresent()) { - this.beaconHashValidator = new BeaconHashValidator(this, - BeaconHashValidator.FORK_050_DISABLED); - } else { - this.beaconHashValidator = new BeaconHashValidator(this, - maybeFork050.get()); - FORK_5_BLOCK_NUMBER = maybeFork050.get(); + if (maybeFork050.isPresent()) { + this.forkUtility.enableUnityFork(maybeFork050.get()); } - + + // initialize beacon hash validator + this.beaconHashValidator = new BeaconHashValidator(this, this.forkUtility); + stakingContractHelper = new StakingContractHelper( forTest @@ -621,7 +622,7 @@ private State pushState(byte[] bestBlockHash) { if (bestBlock.getHeader().getSealType() == BlockSealType.SEAL_POW_BLOCK) { bestMiningBlock = (AionBlock) bestBlock; - if (bestBlock.getNumber() > getUnityForkNumber()) { + if (forkUtility.isUnityForkActive(bestBlock.getNumber())) { bestStakingBlock = (StakingBlock) getBlockStore().getBlockByHash(bestBlock.getParentHash()); } else { bestStakingBlock = null; @@ -766,7 +767,7 @@ public void loadBestStakingBlock() { long bestBlockNumber = bestBlock.getNumber(); if (bestStakingBlock == null) { - if (bestBlockNumber <= getUnityForkNumber()) { + if (!forkUtility.isUnityForkActive(bestBlockNumber)) { bestStakingBlock = CfgAion.inst().getGenesisStakingBlock(); } else { if (bestBlock.getHeader().getSealType() == BlockSealType.SEAL_POS_BLOCK) { @@ -1165,7 +1166,7 @@ BlockContext createNewMiningBlockInternal( // We want the fork block itself to be a PoW block subject to the old pre-Unity rules, // so we use a strict greater than here - if (block.getNumber() > FORK_5_BLOCK_NUMBER) { + if (forkUtility.isUnityForkActive(block.getNumber())) { if (parentHdr.getSealType() == BlockSealType.SEAL_POW_BLOCK) { LOG.error("Tried to create 2 PoW block in a row"); return null; @@ -1198,7 +1199,7 @@ private StakingBlock createNewStakingBlock( Block parent, List txs, byte[] newSeed, byte[] signingPublicKey, byte[] coinbase) { BlockHeader parentHdr = parent.getHeader(); - if (parentHdr.getNumber() < FORK_5_BLOCK_NUMBER) { + if (!forkUtility.isUnityForkActive(parentHdr.getNumber() + 1)) { LOG.debug("Unity fork has not been enabled! Can't create the staking blocks"); return null; } @@ -1211,7 +1212,7 @@ private StakingBlock createNewStakingBlock( return null; } else if (parentHdr.getSealType() == BlockSealType.SEAL_POW_BLOCK) { - if (parentHdr.getNumber() == FORK_5_BLOCK_NUMBER) { + if (forkUtility.isUnityForkBlock(parentHdr.getNumber())) { // this is the first PoS block parentStakingBlock = CfgAion.inst().getGenesisStakingBlock().getHeader(); } else { @@ -1521,9 +1522,7 @@ public boolean isValid(BlockHeader header) { Block grandParent = getParent(parent.getHeader()); if (header.getSealType() == BlockSealType.SEAL_POW_BLOCK) { - // We want the fork block itself to be a PoW block subject to the old pre-Unity rules, - // so we use a strict greater than here - if (header.getNumber() > FORK_5_BLOCK_NUMBER) { + if (forkUtility.isUnityForkActive(header.getNumber())) { if (grandParent == null) { return false; } @@ -1540,7 +1539,7 @@ public boolean isValid(BlockHeader header) { preUnityGrandParentBlockHeaderValidator.validate(parent.getHeader(), grandParent == null ? null : grandParent.getHeader(), header, LOG); } } else if (header.getSealType() == BlockSealType.SEAL_POS_BLOCK) { - if (header.getNumber() <= FORK_5_BLOCK_NUMBER) { + if (!forkUtility.isUnityForkActive(header.getNumber())) { return false; } @@ -1614,7 +1613,7 @@ private boolean isValid(Block block) { Map nonceCache = new HashMap<>(); - boolean unityForkEnabled = block.getHeader().getNumber() >= FORK_5_BLOCK_NUMBER; + boolean unityForkEnabled = forkUtility.isUnityForkActive(block.getNumber()); if (txs.parallelStream() .anyMatch( tx -> @@ -1719,7 +1718,7 @@ private RetValidPreBlock generatePreBlock(Block block) { getPostExecutionWorkForGeneratePreBlock(repository), BlockCachingContext.PENDING, bestBlock.getNumber(), - isUnityForkEnabled()); + forkUtility.isUnityForkActive(block.getNumber())); for (AionTxExecSummary summary : executionSummaries) { if (!summary.isRejected()) { @@ -1778,7 +1777,7 @@ private AionBlockSummary applyBlock(Block block) { getPostExecutionWorkForApplyBlock(repository), executionTypeForAVM, cachedBlockNumberForAVM, - isUnityForkEnabled()); + forkUtility.isUnityForkActive(block.getNumber())); for (AionTxExecSummary summary : executionSummaries) { receipts.add(summary.getReceipt()); @@ -2499,22 +2498,9 @@ public boolean isMainChain(byte[] hash) { return getBlockStore().isMainChain(hash); } - - @VisibleForTesting - @Override - public void setUnityForkNumber(long unityForkNumber) { - LOG.info("Unity enabled at fork number " + unityForkNumber); - FORK_5_BLOCK_NUMBER = unityForkNumber; - } - - @Override - public long getUnityForkNumber() { - return FORK_5_BLOCK_NUMBER; - } - @Override - public boolean isUnityForkEnabled() { - return bestBlockNumber.get() >= FORK_5_BLOCK_NUMBER; + public boolean isUnityForkEnabledAtNextBlock() { + return forkUtility.isUnityForkActive(bestBlockNumber.get() + 1); } /** diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/AionHub.java b/modAionImpl/src/org/aion/zero/impl/blockchain/AionHub.java index 0f3c3a9637..55bd78e4c7 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/AionHub.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/AionHub.java @@ -617,7 +617,7 @@ public BlockContext getNewMiningBlockTemplate(BlockContext oldBlockTemplate, lon context = blockchain.createNewMiningBlockContext( bestBlock, new ArrayList<>(ret), false); - } else if (systemTime > oldBlockTemplate.block.getTimestamp() && blockchain.isUnityForkEnabled()) { + } else if (systemTime > oldBlockTemplate.block.getTimestamp() && blockchain.isUnityForkEnabledAtNextBlock()) { A0BlockHeader newHeader = oldBlockTemplate.block.getHeader().updateTimestamp(System.currentTimeMillis() / 1000); AionBlock newBlock = new AionBlock(newHeader, oldBlockTemplate.block.getTransactionsList()); context = new BlockContext(newBlock, oldBlockTemplate.baseBlockReward, oldBlockTemplate.transactionFee); @@ -641,4 +641,13 @@ public StakingBlock getStakingBlockTemplate(byte[] newSeed, byte[] signingPublic return blockTemplate; } + + public void enableUnityFork(long unityForkNumber) { + this.blockchain.forkUtility.enableUnityFork(unityForkNumber); + } + + @VisibleForTesting + public void disableUnityFork() { + this.blockchain.forkUtility.disableUnityFork(); + } } diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/IAionBlockchain.java b/modAionImpl/src/org/aion/zero/impl/blockchain/IAionBlockchain.java index f02d85a6f7..75c8c41e63 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/IAionBlockchain.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/IAionBlockchain.java @@ -1,6 +1,5 @@ package org.aion.zero.impl.blockchain; -import com.google.common.annotations.VisibleForTesting; import java.math.BigInteger; import java.util.List; import java.util.Map; @@ -192,10 +191,5 @@ void dropImported( void loadBestStakingBlock(); - boolean isUnityForkEnabled(); - - @VisibleForTesting - void setUnityForkNumber(long blockNumber); - - long getUnityForkNumber(); + boolean isUnityForkEnabledAtNextBlock(); } diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/StakingContractHelper.java b/modAionImpl/src/org/aion/zero/impl/blockchain/StakingContractHelper.java index 90bb129a85..8f0da40c7e 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/StakingContractHelper.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/StakingContractHelper.java @@ -165,7 +165,7 @@ private AionTxReceipt callConstant(AionTransaction tx) , block.getNrgLimit() , BlockCachingContext.CALL.avmType , 0 - , chain.isUnityForkEnabled()); + , chain.forkUtility.isUnityForkActive(block.getNumber())); return summaries.get(0).getReceipt(); } diff --git a/modAionImpl/src/org/aion/zero/impl/blockchain/StandaloneBlockchain.java b/modAionImpl/src/org/aion/zero/impl/blockchain/StandaloneBlockchain.java index 95d56b07f6..f2f1e35308 100644 --- a/modAionImpl/src/org/aion/zero/impl/blockchain/StandaloneBlockchain.java +++ b/modAionImpl/src/org/aion/zero/impl/blockchain/StandaloneBlockchain.java @@ -197,13 +197,6 @@ public Builder withAccount(ByteArrayWrapper publicKey, AccountState accState) { return this; } - private long unityForkNumber = Long.MAX_VALUE; - - public Builder withUnityForkNumber(long unityForkNumber) { - this.unityForkNumber = unityForkNumber; - return this; - } - private AionBlock best = null, parentBest = null; private byte[] trieData = null; private BigInteger totalDiff = null, totalDiffParent = null; @@ -529,7 +522,7 @@ public AionBlock createBlock( boolean waitUntilBlockTime, long currTimeSeconds) { - boolean unityForkEnabled = (parent.getHeader().getNumber() + 1) >= getUnityForkNumber(); + boolean unityForkEnabled = forkUtility.isUnityForkActive(parent.getNumber() + 1); for (AionTransaction tx : txs) { if (unityForkEnabled ? !TXValidator.isValidAfterUnity(tx): !TXValidator.isValid(tx)) { throw new InvalidParameterException("invalid transaction input! " + tx.toString()); diff --git a/modAionImpl/src/org/aion/zero/impl/forks/ForkUtility.java b/modAionImpl/src/org/aion/zero/impl/forks/ForkUtility.java new file mode 100644 index 0000000000..d0dca1267a --- /dev/null +++ b/modAionImpl/src/org/aion/zero/impl/forks/ForkUtility.java @@ -0,0 +1,46 @@ +package org.aion.zero.impl.forks; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; + +/** Utility for managing the fork checks. Currently implements only Unity fork functionality. */ +public class ForkUtility { + + // variables used by the Unity fork + private boolean unityForkEnabled = false; + private long unityForkBlockHeight = Long.MAX_VALUE; + + /** + * Enables the Unity fork after the given block number. + * + * @param unityForkBlockHeight the height of the block after which Unity behaviour is applied + */ + public void enableUnityFork(long unityForkBlockHeight) { + Preconditions.checkArgument(unityForkBlockHeight >= 2, "Invalid fork0.5.0 (Unity) block number: must be >= 2"); + this.unityForkBlockHeight = unityForkBlockHeight; + this.unityForkEnabled = true; + } + + /** Disables the Unity fork. */ + @VisibleForTesting + public void disableUnityFork() { + this.unityForkBlockHeight = Long.MAX_VALUE; + this.unityForkEnabled = false; + } + + /** + * Returns a boolean value indicating if the Unity fork is active for the given context (block + * number). We want the fork block itself to be a PoW block subject to the old pre-Unity rules, + * so we use a strict greater than comparison. + * + * @return {@code true} if the unity fork is active for the given context (block number), {@code + * false} otherwise + */ + public boolean isUnityForkActive(long contextBlockNumber) { + return unityForkEnabled && (contextBlockNumber > unityForkBlockHeight); + } + + public boolean isUnityForkBlock(long contextBlockNumber) { + return unityForkEnabled && (contextBlockNumber == unityForkBlockHeight); + } +} diff --git a/modAionImpl/src/org/aion/zero/impl/pendingState/AionPendingStateImpl.java b/modAionImpl/src/org/aion/zero/impl/pendingState/AionPendingStateImpl.java index eedd624344..872108c90c 100644 --- a/modAionImpl/src/org/aion/zero/impl/pendingState/AionPendingStateImpl.java +++ b/modAionImpl/src/org/aion/zero/impl/pendingState/AionPendingStateImpl.java @@ -98,7 +98,7 @@ public TransactionSortedSet() { private final int MAX_REPLAY_TX_BUFFER_SIZE = MAX_VALIDATED_PENDING_TXS >> 2; - private IAionBlockchain blockchain; + private AionBlockchainImpl blockchain; private TransactionStore transactionStore; @@ -434,7 +434,7 @@ public synchronized TxResponse addPendingTransaction(AionTransaction tx) { } public boolean isValid(AionTransaction tx) { - return (blockchain.isUnityForkEnabled() ? TXValidator.isValidAfterUnity(tx) : TXValidator.isValid(tx)) + return (blockchain.isUnityForkEnabledAtNextBlock() ? TXValidator.isValidAfterUnity(tx) : TXValidator.isValid(tx)) && TransactionTypeValidator.isValid(tx) && beaconHashValidator.validateTxForPendingState(tx); } @@ -1113,7 +1113,7 @@ private AionTxExecSummary executeTx(AionTransaction tx, boolean inPool) { LOGGER_VM, BlockCachingContext.PENDING, bestBlk.getNumber(), - blockchain.isUnityForkEnabled()); + blockchain.forkUtility.isUnityForkActive(currentBlockNumber)); } catch (VmFatalException e) { LOGGER_VM.error("Shutdown due to a VM fatal error.", e); System.exit(SystemExitCodes.FATAL_VM_ERROR); diff --git a/modAionImpl/src/org/aion/zero/impl/valid/BeaconHashValidator.java b/modAionImpl/src/org/aion/zero/impl/valid/BeaconHashValidator.java index f5c99ba039..c8bea2a0db 100644 --- a/modAionImpl/src/org/aion/zero/impl/valid/BeaconHashValidator.java +++ b/modAionImpl/src/org/aion/zero/impl/valid/BeaconHashValidator.java @@ -1,12 +1,12 @@ package org.aion.zero.impl.valid; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import org.aion.base.AionTransaction; import org.aion.log.LogEnum; import org.aion.mcf.blockchain.Block; import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.blockchain.IAionBlockchain; +import org.aion.zero.impl.forks.ForkUtility; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,32 +15,20 @@ /** Validates beacon hash of transaction */ public class BeaconHashValidator { private final IAionBlockchain blockchain; - private final long fork050Number; + private final ForkUtility forkUtility; private static final Logger TX_LOG = LoggerFactory.getLogger(LogEnum.TX.name()); - /** - * The value to use for fork050Number when calling constructor {@link - * #BeaconHashValidator(IAionBlockchain, long)} if beacon hash validation is - * disabled. - */ - public static final long FORK_050_DISABLED = Long.MAX_VALUE; - /** * Constructor. * * @param blockchain blockchain - * @param fork050Number the block number at which hard fork 0.5.0 takes effect. Use - * {@link #FORK_050_DISABLED} if validation should be disabled; - * i.e. behave as if fork050 never happens and always return true - * when {@link #validateTxForBlock(AionTransaction, Block)} or - * {@link #validateTxForPendingState(AionTransaction)} is called */ - public BeaconHashValidator(IAionBlockchain blockchain, long fork050Number) { - Preconditions.checkNotNull(blockchain, "Blockstore can't be null"); - Preconditions.checkArgument(fork050Number >= 0, "Invalid fork0.5.0 block number: must be >= 0"); + public BeaconHashValidator(IAionBlockchain blockchain, ForkUtility forkUtility) { + Preconditions.checkNotNull(blockchain, "Blockchain cannot be null"); + Preconditions.checkNotNull(forkUtility, "Unity fork utility cannot be null"); this.blockchain = blockchain; - this.fork050Number = fork050Number; + this.forkUtility = forkUtility; } /** @@ -69,7 +57,7 @@ public boolean validateTxForBlock(AionTransaction tx, Block block) { return true; } - if (!isAfterFork050(block.getNumber()) && beaconHash != null) { + if (!forkUtility.isUnityForkActive(block.getNumber()) && beaconHash != null) { return false; } @@ -123,7 +111,7 @@ public boolean validateTxForPendingState(AionTransaction tx) { // the next block number might be larger than (current best + 1), but // we will tolerate false negatives long minNextBlockNumber = blockchain.getBestBlock().getNumber() + 1; - if (!isAfterFork050(minNextBlockNumber) && beaconHash != null) { + if (!forkUtility.isUnityForkActive(minNextBlockNumber) && beaconHash != null) { return false; } @@ -137,14 +125,6 @@ public boolean validateTxForPendingState(AionTransaction tx) { } } - /** - * @return true if blockNumber >= fork050Number and fork050 not disabled; - * false in all other cases - */ - @VisibleForTesting boolean isAfterFork050(long blockNumber) { - return blockNumber >= fork050Number && fork050Number != FORK_050_DISABLED; - } - private boolean checkSideChain(byte[] beaconHash, Block sideChainHead) { Block beaconBlock = blockchain.getBlockByHash(beaconHash); if(beaconBlock == null) { diff --git a/modAionImpl/test/org/aion/zero/impl/UnityHardForkTest.java b/modAionImpl/test/org/aion/zero/impl/UnityHardForkTest.java index 01dcb33dfd..c1e94f8293 100644 --- a/modAionImpl/test/org/aion/zero/impl/UnityHardForkTest.java +++ b/modAionImpl/test/org/aion/zero/impl/UnityHardForkTest.java @@ -61,12 +61,12 @@ public void setup() throws Exception { @After public void shutdown() { AvmTestConfig.clearConfigurations(); - bc.setUnityForkNumber(Long.MAX_VALUE); + bc.forkUtility.disableUnityFork(); } @Test public void testBlockUnityHardFork() { - bc.setUnityForkNumber(2); + bc.forkUtility.enableUnityFork(2); Block genesis = bc.getBestBlock(); Block blockOnePOW = bc.createNewMiningBlock(genesis, Collections.emptyList(), true); diff --git a/modAionImpl/test/org/aion/zero/impl/valid/BeaconHashValidatorTest.java b/modAionImpl/test/org/aion/zero/impl/valid/BeaconHashValidatorTest.java index 9b8b4cac6b..21db56c196 100644 --- a/modAionImpl/test/org/aion/zero/impl/valid/BeaconHashValidatorTest.java +++ b/modAionImpl/test/org/aion/zero/impl/valid/BeaconHashValidatorTest.java @@ -5,10 +5,10 @@ import org.aion.types.AionAddress; import org.aion.util.bytes.ByteUtil; import org.aion.zero.impl.blockchain.IAionBlockchain; +import org.aion.zero.impl.forks.ForkUtility; import org.junit.Test; import java.util.LinkedList; -import java.util.List; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.Mockito.mock; @@ -18,6 +18,9 @@ public class BeaconHashValidatorTest { @Test public void validateTxForBlockOnMainchainAndBeaconHashOnMainchain() { long fork050Number = 7; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); byte[] goodBlockHash = ByteUtil.hexStringToBytes("0xcafecafecafecafecafecafecafecafecafecafecafecafecafecafecafecafe"); @@ -42,16 +45,16 @@ public void validateTxForBlockOnMainchainAndBeaconHashOnMainchain() { goodBlockHash // beacon hash - exists in blockstore ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); when(block.getNumber()).thenReturn(fork050Number - 1); assertWithMessage("any beacon hash should fail if block_number < fork050_number") .that(unit.validateTxForBlock(tx, block)) .isFalse(); when(block.getNumber()).thenReturn(fork050Number); - assertWithMessage("beacon hash on chain of new block should pass if block_number = fork050_number") + assertWithMessage("beacon hash on chain of new block should fail if block_number = fork050_number") .that(unit.validateTxForBlock(tx, block)) - .isTrue(); + .isFalse(); when(block.getNumber()).thenReturn(fork050Number + 1); assertWithMessage("beacon hash on chain of new block should pass if block_number > fork050_number") .that(unit.validateTxForBlock(tx, block)) @@ -61,6 +64,9 @@ public void validateTxForBlockOnMainchainAndBeaconHashOnMainchain() { @Test public void validateTxForBlockOnMainchainAndBeaconHashNotOnMainchain() { long fork050Number = 13; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); byte[] nonExistentBlockhash = ByteUtil.hexStringToBytes("0xbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad0"); @@ -85,7 +91,7 @@ public void validateTxForBlockOnMainchainAndBeaconHashNotOnMainchain() { nonExistentBlockhash // beacon hash - does not exist in blockstore ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); when(block.getNumber()).thenReturn(fork050Number - 1); assertWithMessage("any beacon hash should fail if block_number < fork050_number") @@ -104,6 +110,9 @@ public void validateTxForBlockOnMainchainAndBeaconHashNotOnMainchain() { @Test public void validateTxForBlockOnMainchainAndBeaconHashNotGiven() { long fork050Number = 2; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); Block block = mock(Block.class); @@ -125,7 +134,7 @@ public void validateTxForBlockOnMainchainAndBeaconHashNotGiven() { null // beacon hash - not present ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); when(block.getNumber()).thenReturn(fork050Number - 1); assertWithMessage("validation should pass if beacon hash not present") @@ -151,6 +160,9 @@ public void validateTxForBlockOnSidechainAndBeaconHashOnSideChain() { * \--x--o--* side chain */ long fork050Number = 5; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); LinkedList sideChain = mockChain(fork050Number, 4, blockchain); @@ -171,7 +183,7 @@ public void validateTxForBlockOnSidechainAndBeaconHashOnSideChain() { beaconHash // beacon hash ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); assertWithMessage("beacon hash on chain of new block should pass if block_number = fork050_number") .that(unit.validateTxForBlock(tx, sideChain.get(0))) .isTrue(); @@ -189,10 +201,13 @@ public void validateTxForBlockOnSidechainAndBeaconHashOnMainchainBeforeSidechain * | * \--o--o--* side chain */ - long fork050Number = 0; + long fork050Number = 2; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); - LinkedList sideChain = mockChain(0, 5, blockchain); + LinkedList sideChain = mockChain(fork050Number, 7, blockchain); when(blockchain.isMainChain(sideChain.get(2).getHash(), sideChain.get(2).getNumber())).thenReturn(true); when(blockchain.isMainChain(sideChain.get(4).getHash())).thenReturn(true); @@ -213,7 +228,7 @@ public void validateTxForBlockOnSidechainAndBeaconHashOnMainchainBeforeSidechain beaconHash // beacon hash ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); assertWithMessage("beacon hash on chain of new block should pass if block_number = fork050_number") .that(unit.validateTxForBlock(tx, sideChain.get(0))) .isTrue(); @@ -231,7 +246,10 @@ public void validateTxForBlockOnSidechainAndBeaconHashOnMainchainAfterSidechainF * | * \--o--o--* side chain */ - long fork050Number = 0; + long fork050Number = 2; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); // mocking up a block as if it were on the main chain but not the sidechain @@ -244,7 +262,7 @@ public void validateTxForBlockOnSidechainAndBeaconHashOnMainchainAfterSidechainF when(blockchain.getBlockByHash(mainchainOnlyBlockHash)).thenReturn(mainchainOnlyBlock); // need to set up the side chain so that its head block number is > mainchainOnlyBlockNum - LinkedList sideChain = mockChain(0, 4, blockchain); + LinkedList sideChain = mockChain(fork050Number, 6, blockchain); byte[] beaconHash = new byte[32]; System.arraycopy(mainchainOnlyBlockHash, 0, beaconHash, 0, 32); @@ -262,7 +280,7 @@ public void validateTxForBlockOnSidechainAndBeaconHashOnMainchainAfterSidechainF beaconHash // beacon hash ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); assertWithMessage("beacon hash not on chain of new block should fail if block_number = fork050_number") .that(unit.validateTxForBlock(tx, sideChain.get(0))) .isFalse(); @@ -280,13 +298,16 @@ public void validateTxForBlockOnSidechainAndBeaconHashNotInDb() { * | * \--o--o--* side chain */ - long fork050Number = 0; + long fork050Number = 2; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); byte[] nonExistentBlockhash = ByteUtil.hexStringToBytes( "0xbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad0"); - LinkedList sideChain = mockChain(0, 4, blockchain); + LinkedList sideChain = mockChain(fork050Number, 6, blockchain); AionTransaction tx = AionTransaction.createWithoutKey( new byte[] { 1 }, // nonce - any val @@ -302,7 +323,7 @@ public void validateTxForBlockOnSidechainAndBeaconHashNotInDb() { nonExistentBlockhash // beacon hash ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); assertWithMessage("beacon hash not on chain of new block should fail if block_number = fork050_number") .that(unit.validateTxForBlock(tx, sideChain.get(0))) .isFalse(); @@ -311,6 +332,9 @@ public void validateTxForBlockOnSidechainAndBeaconHashNotInDb() { @Test public void validateTxForPendingStateWhenBeaconHashOnMainchain() { long fork050Number = 7; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); Block bestBlock = mock(Block.class); @@ -333,16 +357,16 @@ public void validateTxForPendingStateWhenBeaconHashOnMainchain() { goodBlockHash // beacon hash - exists in blockstore ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); when(bestBlock.getNumber()).thenReturn(fork050Number - 2); assertWithMessage("any beacon hash should fail for pending state if best_block_number+1 < fork050_number") .that(unit.validateTxForPendingState(tx)) .isFalse(); when(bestBlock.getNumber()).thenReturn(fork050Number - 1); - assertWithMessage("mainchain hash should pass for pending state if best_block_number+1 = fork050_number") + assertWithMessage("mainchain hash should fail for pending state if best_block_number+1 == fork050_number") .that(unit.validateTxForPendingState(tx)) - .isTrue(); + .isFalse(); when(bestBlock.getNumber()).thenReturn(fork050Number); assertWithMessage("mainchain hash should pass for pending state if best_block_number+1 > fork050_number") .that(unit.validateTxForPendingState(tx)) @@ -352,6 +376,9 @@ public void validateTxForPendingStateWhenBeaconHashOnMainchain() { @Test public void validateTxForPendingStateWhenBeaconHashNotOnMainchain() { long fork050Number = 3; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); Block bestBlock = mock(Block.class); @@ -374,7 +401,7 @@ public void validateTxForPendingStateWhenBeaconHashNotOnMainchain() { badBlockhash // beacon hash - does not exist in blockstore ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); when(bestBlock.getNumber()).thenReturn(fork050Number - 2); assertWithMessage("any beacon hash should fail for pending state if best_block_number+1 < fork050_number") @@ -393,6 +420,9 @@ public void validateTxForPendingStateWhenBeaconHashNotOnMainchain() { @Test public void validateTxForPendingStateWithoutBeaconHash() { long fork050Number = 3; + ForkUtility forkUtility = new ForkUtility(); + forkUtility.enableUnityFork(fork050Number); + IAionBlockchain blockchain = mock(IAionBlockchain.class); Block bestBlock = mock(Block.class); @@ -412,7 +442,7 @@ public void validateTxForPendingStateWithoutBeaconHash() { null // beacon hash - absent ); - BeaconHashValidator unit = new BeaconHashValidator(blockchain, fork050Number); + BeaconHashValidator unit = new BeaconHashValidator(blockchain, forkUtility); when(bestBlock.getNumber()).thenReturn(fork050Number - 2); assertWithMessage("validation should pass for pending state if beacon hash absent") @@ -429,28 +459,26 @@ public void validateTxForPendingStateWithoutBeaconHash() { } @Test - public void isAfterFork050AllCombinations() { - IAionBlockchain blockchain = mock(IAionBlockchain.class); - - assertWithMessage("isAfterFork050 should always be false if fork 0.5.0 disabled") - .that(new BeaconHashValidator(blockchain, BeaconHashValidator.FORK_050_DISABLED) - .isAfterFork050(312l)) + public void isUnityForkActiveAllCombinations() { + // unity fork disabled + ForkUtility forkUtility = new ForkUtility(); + assertWithMessage("isUnityForkActive should always be false if fork 0.5.0 disabled") + .that(forkUtility.isUnityForkActive(312l)) .isFalse(); - assertWithMessage("isAfterFork050 should always be false if fork 0.5.0 disabled") - .that(new BeaconHashValidator(blockchain, BeaconHashValidator.FORK_050_DISABLED) - .isAfterFork050(BeaconHashValidator.FORK_050_DISABLED)) + assertWithMessage("isUnityForkActive should always be false if fork 0.5.0 disabled") + .that(forkUtility.isUnityForkActive(Long.MAX_VALUE)) .isFalse(); - assertWithMessage("isAfterFork050 should be false if block_number < fork050_number") - .that(new BeaconHashValidator(blockchain, 1337) - .isAfterFork050(1336)) + + // unity fork enabled after block 1337 + forkUtility.enableUnityFork(1337); + assertWithMessage("isUnityForkActive should be false if block_number < fork050_number") + .that(forkUtility.isUnityForkActive(1336)) .isFalse(); - assertWithMessage("isAfterFork050 should be true if block_number = fork050_number") - .that(new BeaconHashValidator(blockchain, 1337) - .isAfterFork050(1337)) - .isTrue(); - assertWithMessage("isAfterFork050 should be true if block_number > fork050_number") - .that(new BeaconHashValidator(blockchain, 1337) - .isAfterFork050(1338)) + assertWithMessage("isUnityForkActive should be false if block_number = fork050_number") + .that(forkUtility.isUnityForkActive(1337)) + .isFalse(); + assertWithMessage("isUnityForkActive should be true if block_number > fork050_number") + .that(forkUtility.isUnityForkActive(1338)) .isTrue(); } diff --git a/modAionImpl/test/org/aion/zero/impl/vm/ContractIntegTest.java b/modAionImpl/test/org/aion/zero/impl/vm/ContractIntegTest.java index c8e5a23c49..a2c333dc53 100644 --- a/modAionImpl/test/org/aion/zero/impl/vm/ContractIntegTest.java +++ b/modAionImpl/test/org/aion/zero/impl/vm/ContractIntegTest.java @@ -1,25 +1,3 @@ -/* - * Copyright (c) 2017-2018 Aion foundation. - * - * This file is part of the aion network project. - * - * The aion network project is free software: you can redistribute it - * and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or any later version. - * - * The aion network project is distributed in the hope that it will - * be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the aion network project source files. - * If not, see . - * - * Contributors: - * Aion foundation. - */ package org.aion.zero.impl.vm; import static com.google.common.truth.Truth.assertThat; @@ -55,6 +33,7 @@ import org.aion.log.AionLoggerFactory; import org.aion.log.LogEnum; import org.aion.mcf.blockchain.Block; +import org.aion.zero.impl.blockchain.BlockchainTestUtils; import org.aion.zero.impl.core.ImportResult; import org.aion.mcf.db.RepositoryCache; import org.aion.base.TransactionTypeRule; @@ -127,11 +106,12 @@ public void setup() throws Exception { resourceProvider = TestResourceProvider.initializeAndCreateNewProvider(AvmPathManager.getPathOfProjectRootDirectory()); AvmTestConfig.supportOnlyAvmVersion1(); + blockchain.forkUtility.disableUnityFork(); } @After public void tearDown() throws Exception { - blockchain.setUnityForkNumber(Long.MAX_VALUE); + blockchain.forkUtility.disableUnityFork(); blockchain = null; deployerKey = null; deployer = null; @@ -2063,8 +2043,11 @@ public void testFvmEmptyContractWith200KEnrygyBeforeUnity() throws IOException, } @Test (expected = InvalidParameterException.class) - public void testFvmEmptyContractWith200KEnrygyAfterUnity() throws IOException { - blockchain.setUnityForkNumber(1); + public void testFvmEmptyContractWith200KEnergyAfterUnity() throws IOException { + blockchain.forkUtility.enableUnityFork(2); + // populating the chain to be above the Unity for point + BlockchainTestUtils.generateRandomChainWithoutTransactions(blockchain, 2, 1); + String contractName = "EmptyContract"; byte[] deployCode = getDeployCode(contractName); long nrg = 200_000; @@ -2092,9 +2075,9 @@ public void testFvmEmptyContractWith200KEnrygyAfterUnity() throws IOException { AionBlock block = makeBlock(tx); } - @Test - public void testFvmEmptyContractWith221KEnrygyAfterUnity() throws IOException, VmFatalException { - blockchain.setUnityForkNumber(1); + @Test // TODO: determine why this test passes without the correct blockchain height + public void testFvmEmptyContractWith221KEnergyAfterUnity() throws IOException, VmFatalException { + blockchain.forkUtility.enableUnityFork(2); String contractName = "EmptyContract"; byte[] deployCode = getDeployCode(contractName); long nrg = 221_000; diff --git a/modApiServer/src/org/aion/api/server/rpc2/RpcImpl.java b/modApiServer/src/org/aion/api/server/rpc2/RpcImpl.java index 6df4ae7846..697c5c106f 100644 --- a/modApiServer/src/org/aion/api/server/rpc2/RpcImpl.java +++ b/modApiServer/src/org/aion/api/server/rpc2/RpcImpl.java @@ -33,7 +33,7 @@ public class RpcImpl implements Rpc { @Override public byte[] getseed() { - if (!ac.getAionHub().getBlockchain().isUnityForkEnabled()) { + if (!ac.getAionHub().getBlockchain().isUnityForkEnabledAtNextBlock()) { return null; } @@ -42,7 +42,7 @@ public byte[] getseed() { @Override public byte[] submitseed(byte[] newSeed, byte[] signingPublicKey, byte[] coinbase) throws NullReturnRpcException { - if (!ac.getAionHub().getBlockchain().isUnityForkEnabled()) { + if (!ac.getAionHub().getBlockchain().isUnityForkEnabledAtNextBlock()) { throw new NullReturnRpcException("unity fork is not enabled"); } @@ -81,7 +81,7 @@ public byte[] submitseed(byte[] newSeed, byte[] signingPublicKey, byte[] coinbas @Override public boolean submitsignature(byte[] signature, byte[] sealhash) throws NullReturnRpcException { - if (! ac.getAionHub().getBlockchain().isUnityForkEnabled()) { + if (! ac.getAionHub().getBlockchain().isUnityForkEnabledAtNextBlock()) { throw new NullReturnRpcException("unity fork is not enabled"); } diff --git a/modApiServer/test/org/aion/api/server/rpc2/RpcTest.java b/modApiServer/test/org/aion/api/server/rpc2/RpcTest.java index 2e7df36d23..85a42d8ca0 100644 --- a/modApiServer/test/org/aion/api/server/rpc2/RpcTest.java +++ b/modApiServer/test/org/aion/api/server/rpc2/RpcTest.java @@ -1,11 +1,18 @@ package org.aion.api.server.rpc2; import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.util.Collections; +import java.util.List; import org.aion.api.server.AvmTestConfig; import org.aion.api.server.rpc2.autogen.Rpc; +import org.aion.base.AionTransaction; +import org.aion.mcf.blockchain.Block; import org.aion.zero.impl.blockchain.AionImpl; +import org.aion.zero.impl.blockchain.IAionBlockchain; +import org.aion.zero.impl.core.ImportResult; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -20,24 +27,33 @@ public void setup() { AvmTestConfig.supportOnlyAvmVersion1(); impl = AionImpl.instForTest(); - impl.aionHub.getBlockchain().setUnityForkNumber(0); + impl.aionHub.enableUnityFork(2); api = new RpcImpl(impl); } @After public void tearDown() { - impl.aionHub.getBlockchain().setUnityForkNumber(Long.MAX_VALUE); + impl.aionHub.disableUnityFork(); AvmTestConfig.clearConfigurations(); } @Test public void testGetSeed() { + IAionBlockchain chain = impl.aionHub.getBlockchain(); + Block block = chain.getBestBlock(); + List txs = Collections.emptyList(); + + // expand chain to reach fork point + block = chain.createNewMiningBlock(block, txs, false); + ImportResult result = chain.tryToConnect(block); + assertEquals(ImportResult.IMPORTED_BEST, result); + block = chain.createNewMiningBlock(block, txs, false); + result = chain.tryToConnect(block); + assertEquals(ImportResult.IMPORTED_BEST, result); byte[] seed = api.getseed(); assertNotNull(seed); assertArrayEquals(seed, new byte[64]); } - - }