From 7469351279afa8afcac3e274dc1ed8ec90b5a8b4 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 6 Nov 2017 14:23:14 +0100 Subject: [PATCH] Increase coverage for .config.blockchain - Replaced System.out with Logger in TransactionTest - TransactionTest.afterEIP158Test() seems to expose a modeling issue. `v = CHAIN_ID * 2 + 36` is never stored in Transaction.signature field. --- .../config/blockchain/BlockHeaderBuilder.java | 69 ++++++ .../blockchain/ByzantiumConfigTest.java | 224 ++++++++++++++++++ .../config/blockchain/ETCFork3MTest.java | 201 ++++++++++++++++ .../config/blockchain/Eip150HFConfigTest.java | 13 +- .../config/blockchain/Eip160HFConfigTest.java | 132 +++++++++++ .../blockchain/TestBlockchainConfig.java | 68 +++++- .../org/ethereum/core/TransactionTest.java | 99 ++++---- 7 files changed, 746 insertions(+), 60 deletions(-) create mode 100644 ethereumj-core/src/test/java/org/ethereum/config/blockchain/BlockHeaderBuilder.java create mode 100644 ethereumj-core/src/test/java/org/ethereum/config/blockchain/ByzantiumConfigTest.java create mode 100644 ethereumj-core/src/test/java/org/ethereum/config/blockchain/ETCFork3MTest.java create mode 100644 ethereumj-core/src/test/java/org/ethereum/config/blockchain/Eip160HFConfigTest.java diff --git a/ethereumj-core/src/test/java/org/ethereum/config/blockchain/BlockHeaderBuilder.java b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/BlockHeaderBuilder.java new file mode 100644 index 0000000000..f846e6bf70 --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/BlockHeaderBuilder.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) [2017] [ ] + * This file is part of the ethereumJ library. + * + * The ethereumJ library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The ethereumJ library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the ethereumJ library. If not, see . + * + * + */ +package org.ethereum.config.blockchain; + +import org.apache.commons.lang3.StringUtils; +import org.ethereum.core.BlockHeader; + +import java.math.BigInteger; + +class BlockHeaderBuilder { + private byte[] EMPTY_ARRAY = new byte[0]; + + private byte[] parentHash; + private long blockNumber; + private BigInteger difficulty = BigInteger.ZERO; + private long timestamp = 2L; + private byte[] unclesHash = EMPTY_ARRAY; + + BlockHeaderBuilder(byte[] parentHash, long blockNumber, String difficulty) { + this(parentHash, blockNumber, parse(difficulty)); + } + + BlockHeaderBuilder(byte[] parentHash, long blockNumber, int difficulty) { + this(parentHash, blockNumber, BigInteger.valueOf(difficulty)); + } + + BlockHeaderBuilder(byte[] parentHash, long blockNumber, BigInteger difficulty) { + this.parentHash = parentHash; + this.blockNumber = blockNumber; + this.difficulty = difficulty; + } + + BlockHeaderBuilder withTimestamp(long timestamp) { + this.timestamp = timestamp; + return this; + } + + BlockHeaderBuilder withUncles(byte[] unclesHash) { + this.unclesHash = unclesHash; + return this; + } + + BlockHeader build() { + return new BlockHeader(parentHash, unclesHash, EMPTY_ARRAY, EMPTY_ARRAY, + difficulty.toByteArray(), blockNumber, EMPTY_ARRAY, 1L, timestamp, EMPTY_ARRAY, EMPTY_ARRAY, EMPTY_ARRAY); + } + + public static BigInteger parse(String val) { + return new BigInteger(StringUtils.replace(val, ",", "")); + } + +} diff --git a/ethereumj-core/src/test/java/org/ethereum/config/blockchain/ByzantiumConfigTest.java b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/ByzantiumConfigTest.java new file mode 100644 index 0000000000..76eadbe520 --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/ByzantiumConfigTest.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) [2017] [ ] + * This file is part of the ethereumJ library. + * + * The ethereumJ library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The ethereumJ library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the ethereumJ library. If not, see . + * + * + */ + +package org.ethereum.config.blockchain; + +import org.ethereum.config.Constants; +import org.ethereum.config.ConstantsAdapter; +import org.ethereum.core.BlockHeader; +import org.junit.Ignore; +import org.junit.Test; +import org.spongycastle.util.encoders.Hex; + +import java.math.BigInteger; + +import static org.junit.Assert.*; + +@SuppressWarnings("SameParameterValue") +public class ByzantiumConfigTest { + + private static final byte[] FAKE_HASH = {11, 12}; + + @Test + public void testPredefinedChainId() throws Exception { + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(new TestBlockchainConfig()); + assertEquals(1, (int) byzantiumConfig.getChainId()); + } + + @Test + public void testRelatedEip() throws Exception { + TestBlockchainConfig parent = new TestBlockchainConfig(); + + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(parent); + // Inherited from parent + assertTrue(byzantiumConfig.eip198()); + assertTrue(byzantiumConfig.eip206()); + assertTrue(byzantiumConfig.eip211()); + assertTrue(byzantiumConfig.eip212()); + assertTrue(byzantiumConfig.eip213()); + assertTrue(byzantiumConfig.eip214()); + assertTrue(byzantiumConfig.eip658()); + + // Always false + assertTrue(byzantiumConfig.eip161()); + } + + + @Test + public void testDifficultyWithoutExplosion() throws Exception { + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(new TestBlockchainConfig()); + + BlockHeader parent = new BlockHeaderBuilder(new byte[]{11, 12}, 0L, 1_000_000).build(); + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 1L, -1).build(); + + BigInteger difficulty = byzantiumConfig.calcDifficulty(current, parent); + assertEquals(BigInteger.valueOf(1_000_976), difficulty); + } + + @Test + public void testDifficultyAdjustedForParentBlockHavingUncles() throws Exception { + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(new TestBlockchainConfig()); + BlockHeader parent = new BlockHeaderBuilder(new byte[]{11, 12}, 0L, 0) + .withTimestamp(0L) + .withUncles(new byte[]{1, 2}) + .build(); + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 1L, 0) + .withTimestamp(9L) + .build(); + assertEquals(1, byzantiumConfig.getCalcDifficultyMultiplier(current, parent).intValue()); + } + + @Test + @Ignore + public void testEtherscanIoBlock4490790() throws Exception { + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(new TestBlockchainConfig()); + + // https://etherscan.io/block/4490788 + String parentHash = "fd9d7467e933ff2975c33ea3045ddf8773c87c4cec4e7da8de1bcc015361b38b"; + BlockHeader parent = new BlockHeaderBuilder(parentHash.getBytes(), 4490788, "1,377,255,445,606,146") + .withTimestamp(1509827488) + // Actually an empty list hash, so _no_ uncles + .withUncles(Hex.decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")) + .build(); + + // https://etherscan.io/block/4490789 + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 4490789, BigInteger.ZERO) + .withTimestamp(1509827494) + .build(); + + BigInteger minimumDifficulty = byzantiumConfig.calcDifficulty(current, parent); + assertEquals(BlockHeaderBuilder.parse("1,378,600,421,631,340"), minimumDifficulty); + + BigInteger actualDifficultyOnEtherscan = BlockHeaderBuilder.parse("1,377,927,933,620,791"); + assertTrue(actualDifficultyOnEtherscan.compareTo(minimumDifficulty) > -1); + } + + @Test + public void testDifficultyWithExplosionShouldBeImpactedByBlockTimestamp() throws Exception { + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(new TestBlockchainConfig()); + + BlockHeader parent = new BlockHeaderBuilder(new byte[]{11, 12}, 2_500_000, 8_388_608) + .withTimestamp(0) + .build(); + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 2_500_001, 8_388_608) + .withTimestamp(10 * 60) // 10 minutes later, longer time: lowers difficulty + .build(); + + BigInteger difficulty = byzantiumConfig.calcDifficulty(current, parent); + assertEquals(BigInteger.valueOf(8126464), difficulty); + + + parent = new BlockHeaderBuilder(new byte[]{11, 12}, 2_500_000, 8_388_608) + .withTimestamp(0) + .build(); + current = new BlockHeaderBuilder(parent.getHash(), 2_500_001, 8_388_608) + .withTimestamp(5) // 5 seconds later, shorter time: higher difficulty + .build(); + + difficulty = byzantiumConfig.calcDifficulty(current, parent); + assertEquals(BigInteger.valueOf(8396800), difficulty); + } + + @Test + public void testDifficultyAboveBlock3MShouldTriggerExplosion() throws Exception { + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(new TestBlockchainConfig()); + + int parentDifficulty = 268_435_456; + BlockHeader parent = new BlockHeaderBuilder(FAKE_HASH, 4_000_000, parentDifficulty).build(); + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 4_000_001, -1).build(); + int actualDifficulty = byzantiumConfig.calcDifficulty(current, parent).intValue(); + int differenceWithoutExplosion = actualDifficulty - parentDifficulty; + assertEquals(262_400, differenceWithoutExplosion); + + parent = new BlockHeaderBuilder(FAKE_HASH, 5_000_000, parentDifficulty).build(); + current = new BlockHeaderBuilder(parent.getHash(), 5_000_001, -1).build(); + actualDifficulty = byzantiumConfig.calcDifficulty(current, parent).intValue(); + differenceWithoutExplosion = actualDifficulty - parentDifficulty; + assertEquals(524_288, differenceWithoutExplosion); + + parent = new BlockHeaderBuilder(FAKE_HASH, 6_000_000, parentDifficulty).build(); + current = new BlockHeaderBuilder(parent.getHash(), 6_000_001, -1).build(); + actualDifficulty = byzantiumConfig.calcDifficulty(current, parent).intValue(); + differenceWithoutExplosion = actualDifficulty - parentDifficulty; + assertEquals(268_697_600, differenceWithoutExplosion); + } + + @Test + @SuppressWarnings("PointlessArithmeticExpression") + public void testCalcDifficultyMultiplier() throws Exception { + // Note; timestamps are in seconds + assertCalcDifficultyMultiplier(0L, 1L, 2); + assertCalcDifficultyMultiplier(0L, 5, 2); // 5 seconds + assertCalcDifficultyMultiplier(0L, 1 * 10, 1); // 10 seconds + assertCalcDifficultyMultiplier(0L, 2 * 10, -0); // 20 seconds + assertCalcDifficultyMultiplier(0L, 10 * 10, -9); // 100 seconds + assertCalcDifficultyMultiplier(0L, 60 * 10, -64); // 10 mins + assertCalcDifficultyMultiplier(0L, 60 * 12, -78); // 12 mins + } + + private void assertCalcDifficultyMultiplier(long parentBlockTimestamp, long curBlockTimestamp, int expectedMultiplier) { + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(new TestBlockchainConfig()); + BlockHeader parent = new BlockHeaderBuilder(new byte[]{11, 12}, 0L, 0) + .withTimestamp(parentBlockTimestamp) + .build(); + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 1L, 0) + .withTimestamp(curBlockTimestamp) + .build(); + assertEquals(expectedMultiplier, byzantiumConfig.getCalcDifficultyMultiplier(current, parent).intValue()); + } + + + @Test + public void testExplosionChanges() throws Exception { + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(new TestBlockchainConfig()); + + BlockHeader beforePauseBlock = new BlockHeaderBuilder(FAKE_HASH, 2_000_000, 0).build(); + assertEquals(-2, byzantiumConfig.getExplosion(beforePauseBlock, null)); + + BlockHeader endOfIceAge = new BlockHeaderBuilder(FAKE_HASH, 3_000_000, 0).build(); + assertEquals(-2, byzantiumConfig.getExplosion(endOfIceAge, null)); + + BlockHeader startExplodingBlock = new BlockHeaderBuilder(FAKE_HASH, 3_200_000, 0).build(); + assertEquals(0, byzantiumConfig.getExplosion(startExplodingBlock, null)); + + startExplodingBlock = new BlockHeaderBuilder(FAKE_HASH, 4_000_000, 0).build(); + assertEquals(8, byzantiumConfig.getExplosion(startExplodingBlock, null)); + + startExplodingBlock = new BlockHeaderBuilder(FAKE_HASH, 6_000_000, 0).build(); + assertEquals(28, byzantiumConfig.getExplosion(startExplodingBlock, null)); + } + + @Test + public void testBlockReward() throws Exception { + ByzantiumConfig byzantiumConfig = new ByzantiumConfig(new TestBlockchainConfig() { + @Override + public Constants getConstants() { + return new ConstantsAdapter(super.getConstants()) { + @Override + public BigInteger getBLOCK_REWARD() { + // Make sure ByzantiumConfig is not using parent's block reward + return BigInteger.TEN; + } + }; + } + }); + assertEquals(new BigInteger("3000000000000000000"), byzantiumConfig.getConstants().getBLOCK_REWARD()); + } +} \ No newline at end of file diff --git a/ethereumj-core/src/test/java/org/ethereum/config/blockchain/ETCFork3MTest.java b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/ETCFork3MTest.java new file mode 100644 index 0000000000..93235c1b95 --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/ETCFork3MTest.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) [2017] [ ] + * This file is part of the ethereumJ library. + * + * The ethereumJ library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The ethereumJ library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the ethereumJ library. If not, see . + * + * + */ + +package org.ethereum.config.blockchain; + +import org.apache.commons.lang3.StringUtils; +import org.ethereum.core.BlockHeader; +import org.junit.Test; + +import java.math.BigInteger; + +import static org.junit.Assert.*; + +@SuppressWarnings("SameParameterValue") +public class ETCFork3MTest { + /** + * Ethereum Classic's Chain ID should be '61' according to + * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md + */ + @Test + public void textPredefinedChainId() throws Exception { + ETCFork3M etcFork3M = new ETCFork3M(new TestBlockchainConfig()); + assertEquals(61, (int) etcFork3M.getChainId()); + } + + @Test + public void testRelatedEip() throws Exception { + TestBlockchainConfig parent = new TestBlockchainConfig(); + + ETCFork3M etcFork3M = new ETCFork3M(parent); + // Inherited from parent + assertFalse(etcFork3M.eip198()); + assertFalse(etcFork3M.eip206()); + assertFalse(etcFork3M.eip211()); + assertFalse(etcFork3M.eip212()); + assertFalse(etcFork3M.eip213()); + assertFalse(etcFork3M.eip214()); + assertFalse(etcFork3M.eip658()); + + // Always false + assertFalse(etcFork3M.eip161()); + + /* + * By flipping parent's eip values, we assert that + * ETCFork3M delegates respective eip calls to parent. + */ + parent.enableAllEip(); + + // Inherited from parent + assertTrue(etcFork3M.eip198()); + assertFalse(etcFork3M.eip206()); + assertFalse(etcFork3M.eip211()); + assertTrue(etcFork3M.eip212()); + assertTrue(etcFork3M.eip213()); + assertFalse(etcFork3M.eip214()); + assertFalse(etcFork3M.eip658()); + + // Always false + assertFalse(etcFork3M.eip161()); + } + + + @Test + public void testDifficultyWithoutExplosion() throws Exception { + ETCFork3M etcFork3M = new ETCFork3M(new TestBlockchainConfig()); + + BlockHeader parent = new BlockHeaderBuilder(new byte[]{11, 12}, 0L, 1_000_000).build(); + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 1L, -1).build(); + + BigInteger difficulty = etcFork3M.calcDifficulty(current, parent); + assertEquals(BigInteger.valueOf(269435944), difficulty); + } + + @Test + public void testDifficultyWithExplosionShouldBeImpactedByBlockTimestamp() throws Exception { + ETCFork3M etcFork3M = new ETCFork3M(new TestBlockchainConfig()); + + BlockHeader parent = new BlockHeaderBuilder(new byte[]{11, 12}, 2_500_000, 8_388_608) + .withTimestamp(0) + .build(); + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 2_500_001, -1) + .withTimestamp(10 * 60) // 10 minutes later, longer time: lowers difficulty + .build(); + + BigInteger difficulty = etcFork3M.calcDifficulty(current, parent); + assertEquals(BigInteger.valueOf(276582400), difficulty); + + + parent = new BlockHeaderBuilder(new byte[]{11, 12}, 2_500_000, 8_388_608) + .withTimestamp(0) + .build(); + current = new BlockHeaderBuilder(parent.getHash(), 2_500_001, -1) + .withTimestamp(5) // 5 seconds later, shorter time: higher difficulty + .build(); + + difficulty = etcFork3M.calcDifficulty(current, parent); + assertEquals(BigInteger.valueOf(276828160), difficulty); + } + + @Test + public void testDifficultyAboveBlock5MShouldTriggerExplosion() throws Exception { + ETCFork3M etcFork3M = new ETCFork3M(new TestBlockchainConfig()); + + BlockHeader parent = new BlockHeaderBuilder(new byte[]{11, 12}, 5_000_000, 268_435_456).build(); + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 5_000_001, -1).build(); + assertEquals(BigInteger.valueOf(537_001_984), etcFork3M.calcDifficulty(current, parent)); + + parent = new BlockHeaderBuilder(new byte[]{11, 12}, 5_199_999, 1_073_872_896).build(); + current = new BlockHeaderBuilder(parent.getHash(), 5_200_000, 1_073_872_896).build(); + assertEquals(BlockHeaderBuilder.parse("2,148,139,072"), etcFork3M.calcDifficulty(current, parent)); + } + + @Test + @SuppressWarnings("PointlessArithmeticExpression") + public void testCalcDifficultyMultiplier() throws Exception { + // Note; timestamps are in seconds + assertCalcDifficultyMultiplier(0L, 1L, 1); + assertCalcDifficultyMultiplier(0L, 5, 1); // 5 seconds + assertCalcDifficultyMultiplier(0L, 1 * 10, 0); // 10 seconds + assertCalcDifficultyMultiplier(0L, 2 * 10, -1); // 20 seconds + assertCalcDifficultyMultiplier(0L, 10 * 10, -9); // 100 seconds + assertCalcDifficultyMultiplier(0L, 60 * 10, -59); // 10 mins + assertCalcDifficultyMultiplier(0L, 60 * 12, -71); // 12 mins + } + + private void assertCalcDifficultyMultiplier(long parentBlockTimestamp, long curBlockTimestamp, int expectedMultiplier) { + ETCFork3M etcFork3M = new ETCFork3M(new TestBlockchainConfig()); + BlockHeader parent = new BlockHeaderBuilder(new byte[]{11, 12}, 0L, 0) + .withTimestamp(parentBlockTimestamp) + .build(); + BlockHeader current = new BlockHeaderBuilder(parent.getHash(), 1L, 0) + .withTimestamp(curBlockTimestamp) + .build(); + assertEquals(expectedMultiplier, etcFork3M.getCalcDifficultyMultiplier(current, parent).intValue()); + } + + + /** + * https://github.com/ethereumproject/ECIPs/blob/master/ECIPs/ECIP-1010.md + * +
+     if (block.number < pause_block) {
+        explosion = (block.number / 100000) - 2
+     } else if (block.number < cont_block) {
+        explosion = fixed_diff
+     } else { // block.number >= cont_block
+        explosion = (block.number / 100000) - delay - 2
+     }
+     
+ + * Expected explosion values would be: +
+     Block 3,000,000 == 2**28 == 268,435,456
+     Block 4,000,000 == 2**28 == 268,435,456
+     Block 5,000,000 == 2**28 == 268,435,456
+     Block 5,200,000 == 2**30 == 1 TH
+     Block 6,000,000 == 2**38 == 274 TH
+     
+ Where the explosion is the value after '**'. + */ + @Test + public void testEcip1010ExplosionChanges() throws Exception { + ETCFork3M etcFork3M = new ETCFork3M(new TestBlockchainConfig()); + + /* + * Technically, a block number < 3_000_000 should result in an explosion < fixed_diff, or explosion < 28 + * + * Block number 3_000_000 occurred on Jan 15, 2017. The ETCFork3M configuration was committed a day after. It + * is therefor not necessary to have block.number < pause_block be implemented + */ + BlockHeader beforePauseBlock = new BlockHeaderBuilder(new byte[]{11, 12}, 2_500_000, 0).build(); + int unimplementedPrePauseBlockExplosion = 28; + assertEquals(unimplementedPrePauseBlockExplosion, etcFork3M.getExplosion(beforePauseBlock, null)); + + BlockHeader endOfIceAge = new BlockHeaderBuilder(new byte[]{11, 12}, 5_000_000, 0).build(); + assertEquals(28, etcFork3M.getExplosion(endOfIceAge, null)); + + BlockHeader startExplodingBlock = new BlockHeaderBuilder(new byte[]{11, 12}, 5_200_000, 0).build(); + assertEquals(30, etcFork3M.getExplosion(startExplodingBlock, null)); + + startExplodingBlock = new BlockHeaderBuilder(new byte[]{11, 12}, 6_000_000, 0).build(); + assertEquals(38, etcFork3M.getExplosion(startExplodingBlock, null)); + } +} \ No newline at end of file diff --git a/ethereumj-core/src/test/java/org/ethereum/config/blockchain/Eip150HFConfigTest.java b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/Eip150HFConfigTest.java index 8e2a793858..e16bcff793 100644 --- a/ethereumj-core/src/test/java/org/ethereum/config/blockchain/Eip150HFConfigTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/Eip150HFConfigTest.java @@ -104,7 +104,11 @@ private void assertCallGas(Eip150HFConfig eip150, long requestedGas, long availa @Test public void testRelatedEip() { - TestBlockchainConfig parentAllDependentEipTrue = new TestBlockchainConfig(true, true, true, true); + TestBlockchainConfig parentAllDependentEipTrue = new TestBlockchainConfig() + .enableEip161() + .enableEip198() + .enableEip212() + .enableEip213(); Eip150HFConfig eip150 = new Eip150HFConfig(parentAllDependentEipTrue); // Inherited from parent assertTrue(eip150.eip161()); @@ -118,8 +122,11 @@ public void testRelatedEip() { assertFalse(eip150.eip214()); assertFalse(eip150.eip658()); - - parentAllDependentEipTrue = new TestBlockchainConfig(false, false, false, false); + /* + * By flipping parent's eip values, we assert that + * Eip150 delegates respective eip calls to parent. + */ + parentAllDependentEipTrue = new TestBlockchainConfig(); eip150 = new Eip150HFConfig(parentAllDependentEipTrue); // Inherited from parent assertFalse(eip150.eip161()); diff --git a/ethereumj-core/src/test/java/org/ethereum/config/blockchain/Eip160HFConfigTest.java b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/Eip160HFConfigTest.java new file mode 100644 index 0000000000..3116510538 --- /dev/null +++ b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/Eip160HFConfigTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) [2017] [ ] + * This file is part of the ethereumJ library. + * + * The ethereumJ library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The ethereumJ library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the ethereumJ library. If not, see . + * + * + */ + +package org.ethereum.config.blockchain; + +import org.ethereum.config.Constants; +import org.ethereum.core.Transaction; +import org.ethereum.crypto.ECKey; +import org.ethereum.util.ByteUtil; +import org.junit.Test; +import org.spongycastle.util.BigIntegers; +import org.spongycastle.util.encoders.Hex; + +import java.math.BigInteger; +import java.util.Arrays; + +import static java.math.BigInteger.valueOf; +import static org.junit.Assert.*; + +/** +
+ Specification
+
+ If block.number >= FORK_BLKNUM, increase the gas cost of EXP from 10 + 10 per byte in the
+ exponent to 10 + 50 per byte in the exponent.
+ 
+ * + * https://github.com/ethereum/EIPs/blob/master/EIPS/eip-160.md + */ +public class Eip160HFConfigTest { + private byte[] emptyBytes = new byte[]{}; + + @Test + public void testGetGasCost() throws Exception { + TestBlockchainConfig parent = new TestBlockchainConfig(); + Eip160HFConfig config = new Eip160HFConfig(parent); + assertTrue(config.getGasCost() instanceof Eip160HFConfig.GasCostEip160HF); + assertEquals(50, config.getGasCost().getEXP_BYTE_GAS()); + } + + @Test + public void testMaxContractSizeIsOnlyChangeInEip160() throws Exception { + TestBlockchainConfig parent = new TestBlockchainConfig(); + Eip160HFConfig config = new Eip160HFConfig(parent); + assertEquals(0x6000, config.getConstants().getMAX_CONTRACT_SZIE()); + + Constants expected = parent.getConstants(); + Constants actual = config.getConstants(); + assertEquals(expected.getInitialNonce(), actual.getInitialNonce()); + assertEquals(expected.getMINIMUM_DIFFICULTY(), actual.getMINIMUM_DIFFICULTY()); + assertEquals(expected.getDIFFICULTY_BOUND_DIVISOR(), actual.getDIFFICULTY_BOUND_DIVISOR()); + assertEquals(expected.getMAXIMUM_EXTRA_DATA_SIZE(), actual.getMAXIMUM_EXTRA_DATA_SIZE()); + assertEquals(expected.getBLOCK_REWARD(), actual.getBLOCK_REWARD()); + assertEquals(expected.getEXP_DIFFICULTY_PERIOD(), actual.getEXP_DIFFICULTY_PERIOD()); + assertEquals(expected.getGAS_LIMIT_BOUND_DIVISOR(), actual.getGAS_LIMIT_BOUND_DIVISOR()); + assertEquals(expected.getUNCLE_GENERATION_LIMIT(), actual.getUNCLE_GENERATION_LIMIT()); + assertEquals(expected.getUNCLE_LIST_LIMIT(), actual.getUNCLE_LIST_LIMIT()); + assertEquals(expected.getDURATION_LIMIT(), actual.getDURATION_LIMIT()); + assertEquals(expected.getBEST_NUMBER_DIFF_LIMIT(), actual.getBEST_NUMBER_DIFF_LIMIT()); + assertEquals(expected.createEmptyContractOnOOG(), actual.createEmptyContractOnOOG()); + assertEquals(expected.hasDelegateCallOpcode(), actual.hasDelegateCallOpcode()); + } + + /** + * See also {@link org.ethereum.core.TransactionTest#afterEIP158Test} which tests the + * EIP-155 'signature.v' specification changes. + */ + @Test + public void testAcceptTransactionWithSignature() throws Exception { + Transaction tx = Transaction.create( + "3535353535353535353535353535353535353535", + new BigInteger("1000000000000000000"), + new BigInteger("9"), + new BigInteger("20000000000"), + new BigInteger("21000"), + 1); + + ECKey ecKey = ECKey.fromPrivate(Hex.decode("4646464646464646464646464646464646464646464646464646464646464646")); + tx.sign(ecKey); + + TestBlockchainConfig parent = new TestBlockchainConfig(); + Eip160HFConfig config = new Eip160HFConfig(parent); + assertTrue(config.acceptTransactionSignature(tx)); + } + + @Test + public void testDenyTransactionWithInvalidSignature() throws Exception { + Transaction tx = Transaction.create( + "3535353535353535353535353535353535353535", + new BigInteger("1000000000000000000"), + new BigInteger("9"), + new BigInteger("20000000000"), + new BigInteger("21000"), + 1); + + ECKey ecKey = ECKey.fromPrivate(Hex.decode("4646464646464646464646464646464646464646464646464646464646464646")); + tx.sign(ecKey); + + TestBlockchainConfig parent = new TestBlockchainConfig(); + Eip160HFConfig config = new Eip160HFConfig(parent); + + // Corrupt the signature and assert it's *not* accepted + tx.getSignature().v = 99; + assertFalse(config.acceptTransactionSignature(tx)); + } + + @Test + public void testDenyTransactionWithoutSignature() throws Exception { + TestBlockchainConfig parent = new TestBlockchainConfig(); + Eip160HFConfig config = new Eip160HFConfig(parent); + Transaction txWithoutSignature = new Transaction(emptyBytes, emptyBytes, emptyBytes, emptyBytes, emptyBytes, emptyBytes, null); + assertFalse(config.acceptTransactionSignature(txWithoutSignature)); + } + +} \ No newline at end of file diff --git a/ethereumj-core/src/test/java/org/ethereum/config/blockchain/TestBlockchainConfig.java b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/TestBlockchainConfig.java index 8821ea9784..9dc1d2ce20 100644 --- a/ethereumj-core/src/test/java/org/ethereum/config/blockchain/TestBlockchainConfig.java +++ b/ethereumj-core/src/test/java/org/ethereum/config/blockchain/TestBlockchainConfig.java @@ -39,10 +39,14 @@ @SuppressWarnings("WeakerAccess") class TestBlockchainConfig extends AbstractConfig { - boolean eip161; - boolean eip198; - boolean eip212; - boolean eip213; + boolean eip161 = false; + boolean eip198 = false; + boolean eip206 = false; + boolean eip211 = false; + boolean eip212 = false; + boolean eip213 = false; + boolean eip214 = false; + boolean eip658 = false; Constants constants = new Constants(); MinerIfc minerIfc = new TestMinerIfc(); @@ -54,15 +58,56 @@ class TestBlockchainConfig extends AbstractConfig { byte[] extraData = new byte[]{}; List> headerValidators = newArrayList(); - TestBlockchainConfig() { - this(false, false, false, false); + public TestBlockchainConfig enableAllEip() { + return this + .enableEip161() + .enableEip198() + .enableEip206() + .enableEip211() + .enableEip212() + .enableEip213() + .enableEip214() + .enableEip658(); } - TestBlockchainConfig(boolean eip161, boolean eip198, boolean eip212, boolean eip213) { - this.eip161 = eip161; - this.eip198 = eip198; - this.eip212 = eip212; - this.eip213 = eip213; + public TestBlockchainConfig enableEip161() { + this.eip161 = true; + return this; + } + + public TestBlockchainConfig enableEip198() { + this.eip198 = true; + return this; + } + + public TestBlockchainConfig enableEip206() { + this.eip206 = true; + return this; + } + + public TestBlockchainConfig enableEip211() { + this.eip211 = true; + return this; + } + + public TestBlockchainConfig enableEip212() { + this.eip212 = true; + return this; + } + + public TestBlockchainConfig enableEip213() { + this.eip213 = true; + return this; + } + + public TestBlockchainConfig enableEip214() { + this.eip214 = true; + return this; + } + + public TestBlockchainConfig enableEip658() { + this.eip658 = true; + return this; } @Override @@ -130,6 +175,7 @@ public boolean eip213() { return eip213; } + public class TestMinerIfc implements MinerIfc { @Override public ListenableFuture mine(Block block) { diff --git a/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java b/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java index 79b89ebd28..0866fb71c6 100644 --- a/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/core/TransactionTest.java @@ -35,6 +35,8 @@ import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.spongycastle.util.BigIntegers; import org.spongycastle.util.encoders.Hex; @@ -47,12 +49,14 @@ import java.util.Arrays; import java.util.List; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.ethereum.solidity.SolidityType.*; +import static org.slf4j.LoggerFactory.getLogger; public class TransactionTest { - + private final static Logger logger = getLogger(TransactionTest.class); @Test /* sign transaction https://tools.ietf.org/html/rfc6979 */ public void test1() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException, IOException { @@ -71,7 +75,7 @@ public void test1() throws NoSuchProviderException, NoSuchAlgorithmException, In byte[] txHash = HashUtil.sha3(data); String signature = key.doSign(txHash).toBase64(); - System.out.println(signature); + logger.info(signature); } @Ignore @@ -98,25 +102,25 @@ public void test2() throws Exception { tx.sign(ECKey.fromPrivate(senderPrivKey)); - System.out.println("v\t\t\t: " + Hex.toHexString(new byte[]{tx.getSignature().v})); - System.out.println("r\t\t\t: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(tx.getSignature().r))); - System.out.println("s\t\t\t: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(tx.getSignature().s))); + logger.info("v\t\t\t: " + Hex.toHexString(new byte[]{tx.getSignature().v})); + logger.info("r\t\t\t: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(tx.getSignature().r))); + logger.info("s\t\t\t: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(tx.getSignature().s))); - System.out.println("RLP encoded tx\t\t: " + Hex.toHexString(tx.getEncoded())); + logger.info("RLP encoded tx\t\t: " + Hex.toHexString(tx.getEncoded())); // retrieve the signer/sender of the transaction ECKey key = ECKey.signatureToKey(tx.getHash(), tx.getSignature()); - System.out.println("Tx unsigned RLP\t\t: " + Hex.toHexString(tx.getEncodedRaw())); - System.out.println("Tx signed RLP\t\t: " + Hex.toHexString(tx.getEncoded())); + logger.info("Tx unsigned RLP\t\t: " + Hex.toHexString(tx.getEncodedRaw())); + logger.info("Tx signed RLP\t\t: " + Hex.toHexString(tx.getEncoded())); - System.out.println("Signature public key\t: " + Hex.toHexString(key.getPubKey())); - System.out.println("Sender is\t\t: " + Hex.toHexString(key.getAddress())); + logger.info("Signature public key\t: " + Hex.toHexString(key.getPubKey())); + logger.info("Sender is\t\t: " + Hex.toHexString(key.getAddress())); assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826", Hex.toHexString(key.getAddress())); - System.out.println(tx.toString()); + logger.info(tx.toString()); } @@ -140,20 +144,20 @@ public void test3() throws Exception { tx.sign(ECKey.fromPrivate(senderPrivKey)); - System.out.println("v\t\t\t: " + Hex.toHexString(new byte[]{tx.getSignature().v})); - System.out.println("r\t\t\t: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(tx.getSignature().r))); - System.out.println("s\t\t\t: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(tx.getSignature().s))); + logger.info("v\t\t\t: " + Hex.toHexString(new byte[]{tx.getSignature().v})); + logger.info("r\t\t\t: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(tx.getSignature().r))); + logger.info("s\t\t\t: " + Hex.toHexString(BigIntegers.asUnsignedByteArray(tx.getSignature().s))); - System.out.println("RLP encoded tx\t\t: " + Hex.toHexString(tx.getEncoded())); + logger.info("RLP encoded tx\t\t: " + Hex.toHexString(tx.getEncoded())); // retrieve the signer/sender of the transaction ECKey key = ECKey.signatureToKey(tx.getHash(), tx.getSignature()); - System.out.println("Tx unsigned RLP\t\t: " + Hex.toHexString(tx.getEncodedRaw())); - System.out.println("Tx signed RLP\t\t: " + Hex.toHexString(tx.getEncoded())); + logger.info("Tx unsigned RLP\t\t: " + Hex.toHexString(tx.getEncodedRaw())); + logger.info("Tx signed RLP\t\t: " + Hex.toHexString(tx.getEncoded())); - System.out.println("Signature public key\t: " + Hex.toHexString(key.getPubKey())); - System.out.println("Sender is\t\t: " + Hex.toHexString(key.getAddress())); + logger.info("Signature public key\t: " + Hex.toHexString(key.getPubKey())); + logger.info("Sender is\t\t: " + Hex.toHexString(key.getAddress())); assertEquals("cd2a3d9f938e13cd947ec05abc7fe734df8dd826", Hex.toHexString(key.getAddress())); @@ -291,7 +295,7 @@ public void testTransactionCreateContract() { byte[] payload = tx1.getEncoded(); - System.out.println(Hex.toHexString(payload)); + logger.info(Hex.toHexString(payload)); Transaction tx2 = new Transaction(payload); // tx2.getSender(); @@ -300,13 +304,13 @@ public void testTransactionCreateContract() { // Transaction tx = new Transaction(Hex.decode(rlp)); - System.out.println("tx1.hash: " + Hex.toHexString(tx1.getHash())); - System.out.println("tx2.hash: " + Hex.toHexString(tx2.getHash())); - System.out.println(); - System.out.println("plainTx1: " + plainTx1); - System.out.println("plainTx2: " + plainTx2); + logger.info("tx1.hash: " + Hex.toHexString(tx1.getHash())); + logger.info("tx2.hash: " + Hex.toHexString(tx2.getHash())); + logger.info(""); + logger.info("plainTx1: " + plainTx1); + logger.info("plainTx2: " + plainTx2); - System.out.println(Hex.toHexString(tx2.getSender())); + logger.info(Hex.toHexString(tx2.getSender())); } @@ -449,7 +453,7 @@ protected ProgramResult executeTransaction(Transaction tx) { track.rollback(); - System.out.println("Return value: " + new IntType("uint").decode(executor.getResult().getHReturn())); + logger.info("Return value: " + new IntType("uint").decode(executor.getResult().getHReturn())); } // now executing the JSON test transaction @@ -537,7 +541,7 @@ function get() returns (uint) { StateTestSuite stateTestSuite = new StateTestSuite(json.replaceAll("'", "\"")); - System.out.println(json.replaceAll("'", "\"")); + logger.info(json.replaceAll("'", "\"")); try { SystemProperties.getDefault().setBlockchainConfig(new HomesteadConfig()); @@ -567,14 +571,14 @@ public void multiSuicideTest() throws IOException, InterruptedException { "}"; SolidityCompiler.Result res = SolidityCompiler.compile( contract.getBytes(), true, SolidityCompiler.Options.ABI, SolidityCompiler.Options.BIN); - System.out.println(res.errors); + logger.info(res.errors); CompilationResult cres = CompilationResult.parse(res.output); BlockchainImpl blockchain = ImportLightTest.createBlockchain(GenesisLoader.loadGenesis( getClass().getResourceAsStream("/genesis/genesis-light.json"))); ECKey sender = ECKey.fromPrivate(Hex.decode("3ec771c31cac8c0dba77a69e503765701d3c2bb62435888d4ffa38fed60c445c")).compress(); - System.out.println("address: " + Hex.toHexString(sender.getAddress())); + logger.info("address: " + Hex.toHexString(sender.getAddress())); if (cres.contracts.get("PsychoKiller") != null) { Transaction tx = createTx(blockchain, sender, new byte[0], @@ -615,7 +619,7 @@ public void receiptErrorTest() throws Exception { Transaction tx = createTx(blockchain, sender, new byte[32], new byte[0], 100); TransactionReceipt receipt = executeTransaction(blockchain, tx).getReceipt(); - System.out.println(Hex.toHexString(receipt.getEncoded())); + logger.info(Hex.toHexString(receipt.getEncoded())); receipt = new TransactionReceipt(receipt.getEncoded()); @@ -660,7 +664,7 @@ public void receiptErrorTest() throws Exception { "}"; SolidityCompiler.Result res = SolidityCompiler.compile( contract.getBytes(), true, SolidityCompiler.Options.ABI, SolidityCompiler.Options.BIN); - System.out.println(res.errors); + logger.info(res.errors); CompilationResult cres = CompilationResult.parse(res.output); Transaction tx = createTx(blockchain, sender, new byte[0], Hex.decode(cres.contracts.get("GasConsumer").bin), 0); TransactionReceipt receipt = executeTransaction(blockchain, tx).getReceipt(); @@ -672,11 +676,11 @@ public void receiptErrorTest() throws Exception { } } - protected Transaction createTx(BlockchainImpl blockchain, ECKey sender, byte[] receiveAddress, byte[] data) { + private Transaction createTx(BlockchainImpl blockchain, ECKey sender, byte[] receiveAddress, byte[] data) { return createTx(blockchain, sender, receiveAddress, data, 0); } - protected Transaction createTx(BlockchainImpl blockchain, ECKey sender, byte[] receiveAddress, - byte[] data, long value) { + private Transaction createTx(BlockchainImpl blockchain, ECKey sender, byte[] receiveAddress, + byte[] data, long value) { BigInteger nonce = blockchain.getRepository().getNonce(sender.getAddress()); Transaction tx = new Transaction( ByteUtil.bigIntegerToBytes(nonce), @@ -689,7 +693,7 @@ protected Transaction createTx(BlockchainImpl blockchain, ECKey sender, byte[] r return tx; } - public TransactionExecutor executeTransaction(BlockchainImpl blockchain, Transaction tx) { + private TransactionExecutor executeTransaction(BlockchainImpl blockchain, Transaction tx) { Repository track = blockchain.getRepository().startTracking(); TransactionExecutor executor = new TransactionExecutor(tx, new byte[32], blockchain.getRepository(), blockchain.getBlockStore(), blockchain.getProgramInvokeFactory(), blockchain.getBestBlock()); @@ -723,36 +727,39 @@ public void afterEIP158Test() throws Exception { ); // Checking RLP of unsigned transaction and its hash - assert Arrays.equals(Hex.decode(rlpUnsigned), tx.getEncoded()); - assert Arrays.equals(Hex.decode(unsignedHash), tx.getHash()); + assertArrayEquals(Hex.decode(rlpUnsigned), tx.getEncoded()); + assertArrayEquals(Hex.decode(unsignedHash), tx.getHash()); + assertNull(tx.getSignature()); ECKey ecKey = ECKey.fromPrivate(Hex.decode(privateKey)); tx.sign(ecKey); // Checking modified signature - assert tx.getSignature().r.equals(signatureR); - assert tx.getSignature().s.equals(signatureS); + assertEquals(signatureR, tx.getSignature().r); + assertEquals(signatureS, tx.getSignature().s); + // TODO: Strange, it's still 27. Why is signature used for getEncoded() never assigned to signature field? + assertEquals(27, tx.getSignature().v); // Checking that we get correct TX in the end - assert Arrays.equals(Hex.decode(signedTxRlp), tx.getEncoded()); + assertArrayEquals(Hex.decode(signedTxRlp), tx.getEncoded()); // Check that we could correctly extract tx from new RLP Transaction txSigned = new Transaction(Hex.decode(signedTxRlp)); - assert txSigned.getChainId() == chainId; + assertEquals((int)txSigned.getChainId(), chainId); } @Test public void etcChainIdTest() { Transaction tx = new Transaction(Hex.decode("f871830617428504a817c80083015f90940123286bd94beecd40905321f5c3202c7628d685880ecab7b2bae2c27080819ea021355678b1aa704f6ad4706fb8647f5125beadd1d84c6f9cf37dda1b62f24b1aa06b4a64fd29bb6e54a2c5107e8be42ac039a8ffb631e16e7bcbd15cdfc0015ee2")); Integer chainId = tx.getChainId(); - assert 61 == chainId; + assertEquals(61, chainId.intValue()); } @Test public void longChainIdTest() { Transaction tx = new Transaction(Hex.decode("f8ae82477b8504a817c80083015f9094977ddf44438d540892d1b8618fea65395399971680b844eceb6e3e57696e6454757262696e655f30310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007827e19a025f55532f5cebec362f3f750a3b9c47ab76322622eb3a26ad24c80f9c388c15ba02dcc7ebcfb6ad6ae09f56a29d710cc4115e960a83b98405cf98f7177c14d8a51")); Integer chainId = tx.getChainId(); - assert 16123 == chainId; + assertEquals(16123, chainId.intValue()); Transaction tx1 = Transaction.create( "3535353535353535353535353535353535353535", @@ -767,8 +774,8 @@ public void longChainIdTest() { tx1.sign(key); Transaction tx2 = new Transaction(tx1.getEncoded()); - assert 333333 == tx2.getChainId(); - assert Arrays.equals(tx2.getSender(), key.getAddress()); + assertEquals(333333, tx2.getChainId().intValue()); + assertArrayEquals(tx2.getSender(), key.getAddress()); } }