From 7302420df1a9f601925aa1ead2b20879e8540633 Mon Sep 17 00:00:00 2001 From: Ilan Date: Mon, 3 Oct 2022 19:19:52 -0300 Subject: [PATCH 01/89] Header version based on activation block --- .../blockchain/upgrades/ActivationConfig.java | 5 ++ .../blockchain/upgrades/ConsensusRule.java | 3 +- .../java/org/ethereum/core/BlockFactory.java | 1 + .../java/org/ethereum/core/BlockHeader.java | 9 +++- .../org/ethereum/core/BlockHeaderBuilder.java | 1 + .../java/org/ethereum/core/GenesisHeader.java | 2 + rskj-core/src/main/resources/expected.conf | 1 + rskj-core/src/main/resources/reference.conf | 1 + .../java/co/rsk/core/BlockHeaderTest.java | 30 +++++++++++- .../co/rsk/mine/BlockToMineBuilderTest.java | 48 ++++++++++--------- .../java/co/rsk/remasc/RemascTestRunner.java | 1 + .../upgrades/ActivationConfigTest.java | 14 +++++- .../ethereum/core/BlockHeaderBuilderTest.java | 28 ++++++++++- 13 files changed, 117 insertions(+), 27 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java index d5af5f544ae..4584543cb7c 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java @@ -44,6 +44,11 @@ public ActivationConfig(Map activationHeights) { this.activationHeights = activationHeights; } + public int getHeaderVersion(long blockNumber) { + if (this.isActive(ConsensusRule.RSKIP351, blockNumber)) return 1; + return 0; + } + public boolean isActive(ConsensusRule consensusRule, long blockNumber) { long activationHeight = activationHeights.get(consensusRule); return 0 <= activationHeight && activationHeight <= blockNumber; diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java index 50b77cbcea0..18267f0a194 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java @@ -76,7 +76,8 @@ public enum ConsensusRule { RSKIP290("rskip290"), // Testnet difficulty should drop to a higher difficulty RSKIP293("rskip293"), // Flyover improvements RSKIP294("rskip294"), - RSKIP297("rskip297"); // Increase max timestamp difference between btc and rsk blocks for Testnet + RSKIP297("rskip297"), // Increase max timestamp difference between btc and rsk blocks for Testnet + RSKIP351("rskip351"); // block header extension v1 private String configKey; diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java index f6e6ae3eb5e..1b315fd2001 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java @@ -196,6 +196,7 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { } return new BlockHeader( + activationConfig.getHeaderVersion(blockNumber), parentHash, unclesHash, coinbase, stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, blockNumber, glBytes, gasUsed, timestamp, extraData, diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index 7bbf0c7434a..29f374fa930 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -49,6 +49,9 @@ public class BlockHeader { private static final int FORK_DETECTION_DATA_LENGTH = 12; private static final int UMM_LEAVES_LENGTH = 20; + /* RSKIP 351 version */ + private final int version; + /* The SHA3 256-bit hash of the parent block, in its entirety */ private final byte[] parentHash; /* The SHA3 256-bit hash of the uncles list portion of this block */ @@ -123,13 +126,15 @@ public class BlockHeader { /* Indicates if Block hash for merged mining should have the format described in RSKIP-110 */ private final boolean includeForkDetectionData; - public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, + public BlockHeader(int version, + byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] logsBloom, BlockDifficulty difficulty, long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, Coin minimumGasPrice, int uncleCount, boolean sealed, boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot) { + this.version = version; this.parentHash = parentHash; this.unclesHash = unclesHash; this.coinbase = coinbase; @@ -157,6 +162,8 @@ public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, by this.ummRoot = ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; } + public int getVersion() { return version; } + @VisibleForTesting public boolean isSealed() { return this.sealed; diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java index ac62a6a600f..9dbef0db92e 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java @@ -310,6 +310,7 @@ public BlockHeader build() { } return new BlockHeader( + activationConfig.getHeaderVersion(number), parentHash, unclesHash, coinbase, stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, number, diff --git a/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java b/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java index 1419ff2c978..2e8690ecb35 100644 --- a/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java @@ -29,6 +29,7 @@ public GenesisHeader(byte[] parentHash, byte[] coinbase, byte[] stateRootHash) { super( + 0, parentHash, unclesHash, new RskAddress(coinbase), @@ -72,6 +73,7 @@ public GenesisHeader(byte[] parentHash, boolean useRskip92Encoding, byte[] coinbase) { super( + 0, parentHash, unclesHash, new RskAddress(coinbase), diff --git a/rskj-core/src/main/resources/expected.conf b/rskj-core/src/main/resources/expected.conf index c8a53424d34..bb28fe1d07f 100644 --- a/rskj-core/src/main/resources/expected.conf +++ b/rskj-core/src/main/resources/expected.conf @@ -74,6 +74,7 @@ blockchain = { rskip293 = rskip294 = rskip297 = + rskip351 = } } gc = { diff --git a/rskj-core/src/main/resources/reference.conf b/rskj-core/src/main/resources/reference.conf index 89df2ad25bd..33be970faaf 100644 --- a/rskj-core/src/main/resources/reference.conf +++ b/rskj-core/src/main/resources/reference.conf @@ -63,6 +63,7 @@ blockchain = { rskip293 = hop400 rskip294 = hop400 rskip297 = hop400 + rskip351 = fingerroot500 } } gc = { diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java index c519d17172b..b12482079c7 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java @@ -325,6 +325,19 @@ void verifyRecalculatedHashForAmendedBlocks() { }); } + private void testHeaderVersion(int version) { + BlockHeader header = createBlockHeaderWithVersion(version); + assertEquals(version, header.getVersion()); + } + + @Test + public void getVersion0() { this.testHeaderVersion(0); } + + @Test + public void getVersion1() { + this.testHeaderVersion(1); + } + private BlockHeader createBlockHeaderWithMergedMiningFields( byte[] forkDetectionData, boolean includeForkDetectionData, byte[] ummRoot) { @@ -352,7 +365,7 @@ private BlockHeader createBlockHeaderWithUmmRoot(byte[] ummRoot, byte[] forkDete private BlockHeader createBlockHeader(byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] forkDetectionData, boolean includeForkDetectionData, byte[] ummRoot, boolean sealed) { - return createBlockHeader(bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, + return createBlockHeader(0, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, forkDetectionData, includeForkDetectionData, ummRoot, true, sealed); } @@ -360,12 +373,27 @@ private BlockHeader createBlockHeader(byte[] bitcoinMergedMiningHeader, byte[] b byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] forkDetectionData, boolean includeForkDetectionData, byte[] ummRoot, boolean useRskip92Encoding, boolean sealed) { + return createBlockHeader(0, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, + forkDetectionData, includeForkDetectionData, ummRoot, useRskip92Encoding, sealed); + } + + private BlockHeader createBlockHeaderWithVersion(int version) { + return createBlockHeader(version, new byte[80], new byte[32], new byte[128], + new byte[0], false, null, false, false); + } + + private BlockHeader createBlockHeader(int version, + byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, + byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] forkDetectionData, + boolean includeForkDetectionData, byte[] ummRoot, boolean useRskip92Encoding, + boolean sealed) { BlockDifficulty difficulty = new BlockDifficulty(BigInteger.ONE); long number = 1; BigInteger gasLimit = BigInteger.valueOf(6800000); long timestamp = 7731067; // Friday, 10 May 2019 6:04:05 return new BlockHeader( + version, PegTestUtils.createHash3().getBytes(), HashUtil.keccak256(RLP.encodeList()), new RskAddress(TestUtils.randomAddress().getBytes()), diff --git a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java index 99d523cf7d4..f69bfdbb36c 100644 --- a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java @@ -51,6 +51,7 @@ import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.*; @@ -140,8 +141,7 @@ void BuildBlockHasUnclesWhenCreateAnInvalidBlock() { runMocked(test); } - @Test - void buildBlockBeforeUMMActivation() { + private Block prepareForActivationTest() { Keccak256 parentHash = TestUtils.randomHash(); BlockHeader parent = mock(BlockHeader.class); @@ -151,8 +151,6 @@ void buildBlockBeforeUMMActivation() { when(parent.getMinimumGasPrice()).thenReturn(mock(Coin.class)); when(validationRules.isValid(any())).thenReturn(true); - when(activationConfig.isActive(ConsensusRule.RSKIPUMM, 501L)).thenReturn(false); - when(activationConfig.isActive(ConsensusRule.RSKIP252, 501L)).thenReturn(false); BlockResult expectedResult = mock(BlockResult.class); ArgumentCaptor blockCaptor = ArgumentCaptor.forClass(Block.class); @@ -160,32 +158,37 @@ void buildBlockBeforeUMMActivation() { blockBuilder.build(new ArrayList<>(Collections.singletonList(parent)), new byte[0]); - Block actualBlock = blockCaptor.getValue(); - assertNull(actualBlock.getHeader().getUmmRoot()); + return blockCaptor.getValue(); } @Test - void buildBlockAfterUMMActivation() { - Keccak256 parentHash = TestUtils.randomHash(); - - BlockHeader parent = mock(BlockHeader.class); - when(parent.getNumber()).thenReturn(500L); - when(parent.getHash()).thenReturn(parentHash); - when(parent.getGasLimit()).thenReturn(new byte[0]); - when(parent.getMinimumGasPrice()).thenReturn(mock(Coin.class)); + public void buildBlockBeforeUMMActivation() { + when(activationConfig.isActive(ConsensusRule.RSKIPUMM, 501L)).thenReturn(false); + when(activationConfig.isActive(ConsensusRule.RSKIP252, 501L)).thenReturn(false); + Block actualBlock = this.prepareForActivationTest(); + assertNull(actualBlock.getHeader().getUmmRoot()); + } - when(validationRules.isValid(any())).thenReturn(true); + @Test + public void buildBlockAfterUMMActivation() { when(activationConfig.isActive(ConsensusRule.RSKIPUMM, 501L)).thenReturn(true); when(activationConfig.isActive(ConsensusRule.RSKIP252, 501L)).thenReturn(false); + Block actualBlock = this.prepareForActivationTest(); + assertThat(actualBlock.getHeader().getUmmRoot(), is(new byte[0])); + } - BlockResult expectedResult = mock(BlockResult.class); - ArgumentCaptor blockCaptor = ArgumentCaptor.forClass(Block.class); - when(blockExecutor.executeAndFill(blockCaptor.capture(), any())).thenReturn(expectedResult); - - blockBuilder.build(new ArrayList<>(Collections.singletonList(parent)), new byte[0]); + @Test + public void buildBlockBeforeRskip351() { + when(activationConfig.getHeaderVersion(501L)).thenReturn(0); + Block actualBlock = this.prepareForActivationTest(); + assertEquals(0, actualBlock.getHeader().getVersion()); + } - Block actualBlock = blockCaptor.getValue(); - assertThat(actualBlock.getHeader().getUmmRoot(), is(new byte[0])); + @Test + public void buildBlockAfterRskip351() { + when(activationConfig.getHeaderVersion(501L)).thenReturn(1); + Block actualBlock = this.prepareForActivationTest(); + assertEquals(1, actualBlock.getHeader().getVersion()); } private void runMocked(Consumer task) { @@ -208,6 +211,7 @@ private void runMocked(Consumer task) { private BlockHeader createBlockHeader() { return new BlockHeader( + 0, EMPTY_BYTE_ARRAY, EMPTY_BYTE_ARRAY, TestUtils.randomAddress(), EMPTY_TRIE_HASH, null, EMPTY_TRIE_HASH, new Bloom().getData(), BlockDifficulty.ZERO, 1L, diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java index 997ce1c4d35..185904f410a 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java @@ -288,6 +288,7 @@ public HardcodedHashBlockHeader( Block parentBlock, RskAddress coinbase, Block genesis, List txs, BlockDifficulty finalDifficulty, Coin paidFees, List uncles, Keccak256 blockHash) { super( + 0, parentBlock.getHash().getBytes(), RemascTestRunner.EMPTY_LIST_HASH, coinbase, genesis.getStateRoot(), BlockHashesHelper.getTxTrieRoot(txs, true), HashUtil.EMPTY_TRIE_HASH, new Bloom().getData(), finalDifficulty, parentBlock.getNumber() + 1, diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index 04d736339f9..b7f0c5542aa 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -26,7 +26,6 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.Matchers.is; -import static org.hamcrest.MatcherAssert.assertThat; class ActivationConfigTest { private static final Config BASE_CONFIG = ConfigFactory.parseString(String.join("\n", @@ -102,6 +101,7 @@ class ActivationConfigTest { " rskip293: hop400", " rskip294: hop400", " rskip297: hop400", + " rskip351: fingerroot500", "}" )); @@ -168,4 +168,16 @@ void failsReadingWithUnknownUpgradeConfiguration() { Config config = BASE_CONFIG.withValue("consensusRules.rskip420", ConfigValueFactory.fromAnyRef("orchid")); Assertions.assertThrows(IllegalArgumentException.class, () -> ActivationConfig.read(config)); } + + @Test + public void headerVersion0() { + ActivationConfig config = ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351); + Assertions.assertEquals(0, config.getHeaderVersion(10)); + } + + @Test + public void headerVersion1() { + ActivationConfig config = ActivationConfigsForTest.all(); + Assertions.assertEquals(1, config.getHeaderVersion(10)); + } } diff --git a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java index e9881de3d1b..238d8a3421e 100644 --- a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java @@ -23,6 +23,7 @@ import co.rsk.core.RskAddress; import co.rsk.crypto.Keccak256; import org.ethereum.TestUtils; +import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.crypto.HashUtil; @@ -35,12 +36,15 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; +import org.mockito.Mockito; import java.math.BigInteger; import java.util.Arrays; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.AdditionalMatchers.geq; +import static org.mockito.Mockito.when; class BlockHeaderBuilderTest { private static final byte[] EMPTY_UNCLES_LIST_HASH = HashUtil.keccak256(RLP.encodeList(new byte[0])); @@ -401,7 +405,6 @@ void createsHeaderWithNullUmmrootButUmmCompliantHeaderOffAndRskipUmmOff() { } private static class CreateHeaderArgumentsProvider implements ArgumentsProvider { - @Override public Stream provideArguments(ExtensionContext context) { return Stream.of( @@ -411,4 +414,27 @@ public Stream provideArguments(ExtensionContext context) { ); } } + + @Test + public void createsHeaderWithVersion0BeforeRskip351() { + // RSKIP351 = -1 + BlockHeader header = new BlockHeaderBuilder(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)).build(); + assertEquals(0, header.getVersion()); + } + + @Test + public void createHeaderWithVersion0BeforeRskip351() { + // RSKIP351 > header number + ActivationConfig activationConfig = Mockito.mock(ActivationConfig.class); + when(activationConfig.getHeaderVersion(geq(2))).thenReturn(0); + BlockHeader header = new BlockHeaderBuilder(activationConfig).setNumber(1).build(); + assertEquals(0, header.getVersion()); + } + + @Test + public void createHeaderWithVersion1AfterRskip351() { + // RSKIP351 = 0 + BlockHeader header = new BlockHeaderBuilder(ActivationConfigsForTest.all()).build(); + assertEquals(1, header.getVersion()); + } } From 8bb88599409676545f6539ce18ac77684552c4c9 Mon Sep 17 00:00:00 2001 From: Ilan Date: Thu, 6 Oct 2022 20:20:08 -0300 Subject: [PATCH 02/89] Use byte type for header version --- .../blockchain/upgrades/ActivationConfig.java | 6 +++--- .../main/java/org/ethereum/core/BlockHeader.java | 6 +++--- .../main/java/org/ethereum/core/GenesisHeader.java | 4 ++-- .../src/test/java/co/rsk/core/BlockHeaderTest.java | 14 +++++++------- .../java/co/rsk/mine/BlockToMineBuilderTest.java | 6 +++--- .../test/java/co/rsk/remasc/RemascTestRunner.java | 2 +- .../blockchain/upgrades/ActivationConfigTest.java | 4 ++-- .../org/ethereum/core/BlockHeaderBuilderTest.java | 8 ++++---- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java index 4584543cb7c..941f4f55913 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java @@ -44,9 +44,9 @@ public ActivationConfig(Map activationHeights) { this.activationHeights = activationHeights; } - public int getHeaderVersion(long blockNumber) { - if (this.isActive(ConsensusRule.RSKIP351, blockNumber)) return 1; - return 0; + public byte getHeaderVersion(long blockNumber) { + if (this.isActive(ConsensusRule.RSKIP351, blockNumber)) return 0x1; + return 0x0; } public boolean isActive(ConsensusRule consensusRule, long blockNumber) { diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index 29f374fa930..d5d1d5720cf 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -50,7 +50,7 @@ public class BlockHeader { private static final int UMM_LEAVES_LENGTH = 20; /* RSKIP 351 version */ - private final int version; + private final byte version; /* The SHA3 256-bit hash of the parent block, in its entirety */ private final byte[] parentHash; @@ -126,7 +126,7 @@ public class BlockHeader { /* Indicates if Block hash for merged mining should have the format described in RSKIP-110 */ private final boolean includeForkDetectionData; - public BlockHeader(int version, + public BlockHeader(byte version, byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] logsBloom, BlockDifficulty difficulty, long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, @@ -162,7 +162,7 @@ public BlockHeader(int version, this.ummRoot = ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; } - public int getVersion() { return version; } + public byte getVersion() { return version; } @VisibleForTesting public boolean isSealed() { diff --git a/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java b/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java index 2e8690ecb35..5a49c64d04b 100644 --- a/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java @@ -29,7 +29,7 @@ public GenesisHeader(byte[] parentHash, byte[] coinbase, byte[] stateRootHash) { super( - 0, + (byte) 0x0, parentHash, unclesHash, new RskAddress(coinbase), @@ -73,7 +73,7 @@ public GenesisHeader(byte[] parentHash, boolean useRskip92Encoding, byte[] coinbase) { super( - 0, + (byte) 0x0, parentHash, unclesHash, new RskAddress(coinbase), diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java index b12482079c7..001867993ac 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java @@ -325,17 +325,17 @@ void verifyRecalculatedHashForAmendedBlocks() { }); } - private void testHeaderVersion(int version) { + private void testHeaderVersion(byte version) { BlockHeader header = createBlockHeaderWithVersion(version); assertEquals(version, header.getVersion()); } @Test - public void getVersion0() { this.testHeaderVersion(0); } + public void getVersion0() { this.testHeaderVersion((byte) 0x0); } @Test public void getVersion1() { - this.testHeaderVersion(1); + this.testHeaderVersion((byte) 0x1); } private BlockHeader createBlockHeaderWithMergedMiningFields( @@ -365,7 +365,7 @@ private BlockHeader createBlockHeaderWithUmmRoot(byte[] ummRoot, byte[] forkDete private BlockHeader createBlockHeader(byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] forkDetectionData, boolean includeForkDetectionData, byte[] ummRoot, boolean sealed) { - return createBlockHeader(0, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, + return createBlockHeader((byte) 0x0, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, forkDetectionData, includeForkDetectionData, ummRoot, true, sealed); } @@ -373,16 +373,16 @@ private BlockHeader createBlockHeader(byte[] bitcoinMergedMiningHeader, byte[] b byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] forkDetectionData, boolean includeForkDetectionData, byte[] ummRoot, boolean useRskip92Encoding, boolean sealed) { - return createBlockHeader(0, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, + return createBlockHeader((byte) 0x0, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, forkDetectionData, includeForkDetectionData, ummRoot, useRskip92Encoding, sealed); } - private BlockHeader createBlockHeaderWithVersion(int version) { + private BlockHeader createBlockHeaderWithVersion(byte version) { return createBlockHeader(version, new byte[80], new byte[32], new byte[128], new byte[0], false, null, false, false); } - private BlockHeader createBlockHeader(int version, + private BlockHeader createBlockHeader(byte version, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] forkDetectionData, boolean includeForkDetectionData, byte[] ummRoot, boolean useRskip92Encoding, diff --git a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java index f69bfdbb36c..540ad282c39 100644 --- a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java @@ -179,14 +179,14 @@ public void buildBlockAfterUMMActivation() { @Test public void buildBlockBeforeRskip351() { - when(activationConfig.getHeaderVersion(501L)).thenReturn(0); + when(activationConfig.getHeaderVersion(501L)).thenReturn((byte) 0x0); Block actualBlock = this.prepareForActivationTest(); assertEquals(0, actualBlock.getHeader().getVersion()); } @Test public void buildBlockAfterRskip351() { - when(activationConfig.getHeaderVersion(501L)).thenReturn(1); + when(activationConfig.getHeaderVersion(501L)).thenReturn((byte) 0x1); Block actualBlock = this.prepareForActivationTest(); assertEquals(1, actualBlock.getHeader().getVersion()); } @@ -211,7 +211,7 @@ private void runMocked(Consumer task) { private BlockHeader createBlockHeader() { return new BlockHeader( - 0, + (byte) 0x0, EMPTY_BYTE_ARRAY, EMPTY_BYTE_ARRAY, TestUtils.randomAddress(), EMPTY_TRIE_HASH, null, EMPTY_TRIE_HASH, new Bloom().getData(), BlockDifficulty.ZERO, 1L, diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java index 185904f410a..7c6af98fa74 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java @@ -288,7 +288,7 @@ public HardcodedHashBlockHeader( Block parentBlock, RskAddress coinbase, Block genesis, List txs, BlockDifficulty finalDifficulty, Coin paidFees, List uncles, Keccak256 blockHash) { super( - 0, + (byte) 0x0, parentBlock.getHash().getBytes(), RemascTestRunner.EMPTY_LIST_HASH, coinbase, genesis.getStateRoot(), BlockHashesHelper.getTxTrieRoot(txs, true), HashUtil.EMPTY_TRIE_HASH, new Bloom().getData(), finalDifficulty, parentBlock.getNumber() + 1, diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index b7f0c5542aa..897a07bf951 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -172,12 +172,12 @@ void failsReadingWithUnknownUpgradeConfiguration() { @Test public void headerVersion0() { ActivationConfig config = ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351); - Assertions.assertEquals(0, config.getHeaderVersion(10)); + Assertions.assertEquals((byte) 0x0, config.getHeaderVersion(10)); } @Test public void headerVersion1() { ActivationConfig config = ActivationConfigsForTest.all(); - Assertions.assertEquals(1, config.getHeaderVersion(10)); + Assertions.assertEquals((byte) 0x1,, config.getHeaderVersion(10)); } } diff --git a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java index 238d8a3421e..0c686985145 100644 --- a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java @@ -419,22 +419,22 @@ public Stream provideArguments(ExtensionContext context) { public void createsHeaderWithVersion0BeforeRskip351() { // RSKIP351 = -1 BlockHeader header = new BlockHeaderBuilder(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)).build(); - assertEquals(0, header.getVersion()); + assertEquals((byte) 0x0, header.getVersion()); } @Test public void createHeaderWithVersion0BeforeRskip351() { // RSKIP351 > header number ActivationConfig activationConfig = Mockito.mock(ActivationConfig.class); - when(activationConfig.getHeaderVersion(geq(2))).thenReturn(0); + when(activationConfig.getHeaderVersion(geq(2))).thenReturn((byte) 0x0); BlockHeader header = new BlockHeaderBuilder(activationConfig).setNumber(1).build(); - assertEquals(0, header.getVersion()); + assertEquals((byte) 0x0, header.getVersion()); } @Test public void createHeaderWithVersion1AfterRskip351() { // RSKIP351 = 0 BlockHeader header = new BlockHeaderBuilder(ActivationConfigsForTest.all()).build(); - assertEquals(1, header.getVersion()); + assertEquals((byte) 0x1, header.getVersion()); } } From 2a1186f5d5957b324b54c8842ea683f08edc9d11 Mon Sep 17 00:00:00 2001 From: Ilan Date: Thu, 6 Oct 2022 20:22:27 -0300 Subject: [PATCH 03/89] Test genesis version --- rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java index d96191b3c6d..f8c262270a4 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java @@ -289,6 +289,11 @@ void decodeBlockRskip110OffRskipUMMOnAndMergedMiningFieldsNullUmmRoot() { Assertions.assertThrows(IllegalArgumentException.class, () -> factory.decodeHeader(encodedHeader)); } + @Test + public void genesisHasVersion0() { + assertEquals((byte) 0x0, factory.decodeBlock(genesisRaw()).getHeader().getVersion()); + } + private void enableRulesAt(long number, ConsensusRule... consensusRules) { for (ConsensusRule consensusRule : consensusRules) { when(activationConfig.isActive(eq(consensusRule), geq(number))).thenReturn(true); From e24f59995182ed5d8415dd590f7df300df30f1c3 Mon Sep 17 00:00:00 2001 From: Ilan Date: Thu, 27 Oct 2022 12:50:32 -0300 Subject: [PATCH 04/89] Fix merge --- .../config/blockchain/upgrades/ActivationConfigTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index 897a07bf951..749c2d2055c 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -178,6 +178,6 @@ public void headerVersion0() { @Test public void headerVersion1() { ActivationConfig config = ActivationConfigsForTest.all(); - Assertions.assertEquals((byte) 0x1,, config.getHeaderVersion(10)); + Assertions.assertEquals((byte) 0x1, config.getHeaderVersion(10)); } } From 0ead12c7c078d4e64783905b41edd8b60c7f8b43 Mon Sep 17 00:00:00 2001 From: Ilan Date: Tue, 1 Nov 2022 19:20:43 -0300 Subject: [PATCH 05/89] RSKIP 351 implementation Versioned header + block header compression + body response message with header extension --- .../java/co/rsk/net/NodeBlockProcessor.java | 2 +- .../messages/BlockHeadersResponseMessage.java | 2 +- .../rsk/net/messages/BodyResponseMessage.java | 15 +++- .../java/co/rsk/net/messages/MessageType.java | 6 +- .../DownloadingBackwardsBodiesSyncState.java | 1 + .../net/sync/DownloadingBodiesSyncState.java | 1 + .../java/org/ethereum/core/BlockFactory.java | 13 ++- .../java/org/ethereum/core/BlockHeader.java | 82 +++++++------------ .../org/ethereum/core/BlockHeaderBuilder.java | 17 +++- .../ethereum/core/BlockHeaderExtension.java | 14 ++++ .../ethereum/core/BlockHeaderExtensionV1.java | 37 +++++++++ .../java/org/ethereum/core/BlockHeaderV0.java | 70 ++++++++++++++++ .../java/org/ethereum/core/BlockHeaderV1.java | 57 +++++++++++++ .../java/org/ethereum/core/GenesisHeader.java | 4 +- .../java/co/rsk/cli/tools/CliToolsTest.java | 5 +- .../java/co/rsk/core/BlockFactoryTest.java | 6 +- .../co/rsk/core/BlockHeaderExtensionTest.java | 73 +++++++++++++++++ .../java/co/rsk/core/BlockHeaderTest.java | 59 +++++++++++-- .../co/rsk/mine/BlockToMineBuilderTest.java | 3 +- .../java/co/rsk/net/SyncProcessorTest.java | 14 ++-- .../ThreeAsyncNodeUsingSyncProcessorTest.java | 2 +- .../net/messages/BodyResponseMessageTest.java | 4 +- .../java/co/rsk/net/messages/MessageTest.java | 5 +- ...wnloadingBackwardsBodiesSyncStateTest.java | 4 +- .../sync/DownloadingBodiesSyncStateTest.java | 4 +- .../java/co/rsk/remasc/RemascTestRunner.java | 3 +- .../EthSubscriptionNotificationTest.java | 7 +- .../co/rsk/test/builders/BlockBuilder.java | 11 ++- .../org/ethereum/rpc/Web3ImplLogsTest.java | 6 +- .../java/org/ethereum/rpc/Web3ImplTest.java | 19 ++++- 30 files changed, 441 insertions(+), 105 deletions(-) create mode 100644 rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java create mode 100644 rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java create mode 100644 rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java create mode 100644 rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java create mode 100644 rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java diff --git a/rskj-core/src/main/java/co/rsk/net/NodeBlockProcessor.java b/rskj-core/src/main/java/co/rsk/net/NodeBlockProcessor.java index dfb72604ee6..381e078232d 100644 --- a/rskj-core/src/main/java/co/rsk/net/NodeBlockProcessor.java +++ b/rskj-core/src/main/java/co/rsk/net/NodeBlockProcessor.java @@ -226,7 +226,7 @@ public void processBodyRequest(@Nonnull final Peer sender, long requestId, @Nonn return; } - Message responseMessage = new BodyResponseMessage(requestId, block.getTransactionsList(), block.getUncleList()); + Message responseMessage = new BodyResponseMessage(requestId, block.getTransactionsList(), block.getUncleList(), block.getHeader().getExtension()); sender.sendMessage(responseMessage); } diff --git a/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java b/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java index ada7749f442..bfc7ec472cd 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java @@ -32,7 +32,7 @@ public BlockHeadersResponseMessage(long id, List headers) { @Override protected byte[] getEncodedMessageWithoutId() { byte[][] rlpHeaders = this.blockHeaders.stream() - .map(BlockHeader::getFullEncoded) + .map(BlockHeader::getEncodedForHeaderMessage) .toArray(byte[][]::new); return RLP.encodeList(RLP.encodeList(rlpHeaders)); diff --git a/rskj-core/src/main/java/co/rsk/net/messages/BodyResponseMessage.java b/rskj-core/src/main/java/co/rsk/net/messages/BodyResponseMessage.java index 41c73c76de7..610aff31b78 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/BodyResponseMessage.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/BodyResponseMessage.java @@ -1,6 +1,8 @@ package co.rsk.net.messages; +import com.google.common.collect.Lists; import org.ethereum.core.BlockHeader; +import org.ethereum.core.BlockHeaderExtension; import org.ethereum.core.Transaction; import org.ethereum.util.RLP; @@ -13,11 +15,13 @@ public class BodyResponseMessage extends MessageWithId { private long id; private List transactions; private List uncles; + private BlockHeaderExtension blockHeaderExtension; - public BodyResponseMessage(long id, List transactions, List uncles) { + public BodyResponseMessage(long id, List transactions, List uncles, BlockHeaderExtension blockHeaderExtension) { this.id = id; this.transactions = transactions; this.uncles = uncles; + this.blockHeaderExtension = blockHeaderExtension; } @Override @@ -26,6 +30,7 @@ public BodyResponseMessage(long id, List transactions, List getTransactions() { return this.transactions; } public List getUncles() { return this.uncles; } + public BlockHeaderExtension getBlockHeaderExtension() { return this.blockHeaderExtension; } @Override protected byte[] getEncodedMessageWithoutId() { @@ -40,7 +45,13 @@ protected byte[] getEncodedMessageWithoutId() { rlpUncles[k] = this.uncles.get(k).getFullEncoded(); } - return RLP.encodeList(RLP.encodeList(rlpTransactions), RLP.encodeList(rlpUncles)); + List elements = Lists.newArrayList(RLP.encodeList(rlpTransactions), RLP.encodeList(rlpUncles)); + + if (this.blockHeaderExtension != null) { + elements.add(this.blockHeaderExtension.getEncoded()); + } + + return RLP.encodeList(elements.toArray(new byte[][]{})); } @Override diff --git a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java index a7e9ad21fb5..e7e602d43d6 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java @@ -232,7 +232,11 @@ public Message createMessage(BlockFactory blockFactory, RLPList list) { uncles.add(blockFactory.decodeHeader(element.getRLPData())); } - return new BodyResponseMessage(id, transactions, uncles); + BlockHeaderExtension blockHeaderExtension = message.size() == 3 + ? BlockHeaderExtension.fromEncoded((RLPList) RLP.decode2(message.get(2).getRLPData()).get(0)) + : null; + + return new BodyResponseMessage(id, transactions, uncles, blockHeaderExtension); } }, SKELETON_REQUEST_MESSAGE(16) { diff --git a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncState.java b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncState.java index e3a663c4939..e59da6fcaac 100644 --- a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncState.java +++ b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncState.java @@ -94,6 +94,7 @@ public void newBody(BodyResponseMessage body, Peer peer) { return; } + requestedHeader.setExtension(body.getBlockHeaderExtension()); Block block = blockFactory.newBlock(requestedHeader, body.getTransactions(), body.getUncles()); block.seal(); diff --git a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java index dc1eedf0dd4..1eb1d3e1255 100644 --- a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java +++ b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java @@ -116,6 +116,7 @@ public void newBody(BodyResponseMessage message, Peer peer) { // we already checked that this message was expected BlockHeader header = pendingBodyResponses.remove(requestId).header; + header.setExtension(message.getBlockHeaderExtension()); Block block; try { block = blockFactory.newBlock(header, message.getTransactions(), message.getUncles()); diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java index 1b315fd2001..10106902953 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java @@ -195,8 +195,17 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { stateRoot); } - return new BlockHeader( - activationConfig.getHeaderVersion(blockNumber), + if (activationConfig.getHeaderVersion(blockNumber) == 1) return new BlockHeaderV1( + parentHash, unclesHash, coinbase, stateRoot, + txTrieRoot, receiptTrieRoot, logsBloom, difficulty, + blockNumber, glBytes, gasUsed, timestamp, extraData, + paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, + bitcoinMergedMiningCoinbaseTransaction, new byte[0], + minimumGasPrice, uncleCount, sealed, useRskip92Encoding, includeForkDetectionData, + ummRoot + ); + + return new BlockHeaderV0( parentHash, unclesHash, coinbase, stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, blockNumber, glBytes, gasUsed, timestamp, extraData, diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index d5d1d5720cf..5a899be0867 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -1,21 +1,3 @@ -/* - * This file is part of RskJ - * Copyright (C) 2017 RSK Labs Ltd. - * (derived from ethereumJ library, Copyright (c) 2016 ) - * - * This program 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. - * - * This program 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 this program. If not, see . - */ package org.ethereum.core; import co.rsk.config.RskMiningConstants; @@ -43,15 +25,21 @@ * Block header is a value object containing * the basic information of a block */ -public class BlockHeader { +public abstract class BlockHeader { + /* RSKIP 351 */ + public abstract byte getVersion(); + public abstract BlockHeaderExtension getExtension(); + public abstract void setExtension(BlockHeaderExtension extension); + // fields from block header extension + public abstract byte[] getLogsBloom(); + public abstract void setLogsBloom(byte[] logsBloom); + // encoding to use in logs bloom field on header response message + public abstract byte[] getLogsBloomFieldEncoded(); private static final int HASH_FOR_MERGED_MINING_PREFIX_LENGTH = 20; private static final int FORK_DETECTION_DATA_LENGTH = 12; private static final int UMM_LEAVES_LENGTH = 20; - /* RSKIP 351 version */ - private final byte version; - /* The SHA3 256-bit hash of the parent block, in its entirety */ private final byte[] parentHash; /* The SHA3 256-bit hash of the uncles list portion of this block */ @@ -72,8 +60,6 @@ public class BlockHeader { * list portion, the trie is populate by [key, val] --> [rlp(index), rlp(tx_recipe)] * of the block */ private byte[] receiptTrieRoot; - /* The bloom filter for the logs of the block */ - private byte[] logsBloom; /** * A scalar value corresponding to the difficulty level of this block. * This can be calculated from the previous block’s difficulty level @@ -115,10 +101,10 @@ public class BlockHeader { private final int uncleCount; /* Indicates if this block header cannot be changed */ - private volatile boolean sealed; + protected volatile boolean sealed; /* Holds calculated block hash */ - private Keccak256 hash; + protected Keccak256 hash; /* Indicates if the block was mined according to RSKIP-92 rules */ private final boolean useRskip92Encoding; @@ -126,22 +112,19 @@ public class BlockHeader { /* Indicates if Block hash for merged mining should have the format described in RSKIP-110 */ private final boolean includeForkDetectionData; - public BlockHeader(byte version, - byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, - byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] logsBloom, BlockDifficulty difficulty, + public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, + byte[] txTrieRoot, byte[] receiptTrieRoot, BlockDifficulty difficulty, long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, Coin minimumGasPrice, int uncleCount, boolean sealed, boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot) { - this.version = version; this.parentHash = parentHash; this.unclesHash = unclesHash; this.coinbase = coinbase; this.stateRoot = stateRoot; this.txTrieRoot = txTrieRoot; this.receiptTrieRoot = receiptTrieRoot; - this.logsBloom = logsBloom; this.difficulty = difficulty; this.number = number; this.gasLimit = gasLimit; @@ -162,8 +145,6 @@ public BlockHeader(byte version, this.ummRoot = ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; } - public byte getVersion() { return version; } - @VisibleForTesting public boolean isSealed() { return this.sealed; @@ -235,11 +216,6 @@ public void setTransactionsRoot(byte[] stateRoot) { this.txTrieRoot = stateRoot; } - - public byte[] getLogsBloom() { - return logsBloom; - } - public BlockDifficulty getDifficulty() { // some blocks have zero encoded as null, but if we altered the internal field then re-encoding the value would // give a different value than the original. @@ -304,19 +280,9 @@ public byte[] getExtraData() { return extraData; } - public void setLogsBloom(byte[] logsBloom) { - /* A sealed block header is immutable, cannot be changed */ - if (this.sealed) { - throw new SealedBlockHeaderException("trying to alter logs bloom"); - } - this.hash = null; - - this.logsBloom = logsBloom; - } - public Keccak256 getHash() { if (this.hash == null) { - this.hash = new Keccak256(HashUtil.keccak256(getEncoded())); + this.hash = new Keccak256(HashUtil.keccak256(getEncodedForHash())); } return this.hash; @@ -325,12 +291,20 @@ public Keccak256 getHash() { public byte[] getFullEncoded() { // the encoded block header must include all fields, even the bitcoin PMT and coinbase which are not used for // calculating RSKIP92 block hashes - return this.getEncoded(true, true); + return this.getEncoded(true, true, false); } public byte[] getEncoded() { // the encoded block header used for calculating block hashes including RSKIP92 - return this.getEncoded(true, !useRskip92Encoding); + return this.getEncoded(true, !useRskip92Encoding, false); + } + + public byte[] getEncodedForHeaderMessage() { + return this.getEncoded(true, true, true); + } + + public byte[] getEncodedForHash() { + return this.getEncoded(true, !useRskip92Encoding, true); } @Nullable @@ -338,7 +312,7 @@ public Coin getMinimumGasPrice() { return this.minimumGasPrice; } - public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProofAndCoinbase) { + public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProofAndCoinbase, boolean useExtensionEncoding) { byte[] parentHash = RLP.encodeElement(this.parentHash); byte[] unclesHash = RLP.encodeElement(this.unclesHash); @@ -358,7 +332,7 @@ public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProof byte[] receiptTrieRoot = RLP.encodeElement(this.receiptTrieRoot); - byte[] logsBloom = RLP.encodeElement(this.logsBloom); + byte[] logsBloom = useExtensionEncoding ? this.getLogsBloomFieldEncoded() : RLP.encodeElement(this.getLogsBloom()); byte[] difficulty = encodeBlockDifficulty(this.difficulty); byte[] number = RLP.encodeBigInteger(BigInteger.valueOf(this.number)); byte[] gasLimit = RLP.encodeElement(this.gasLimit); @@ -601,7 +575,7 @@ public byte[] getMiningForkDetectionData() { * @return The computed hash for merged mining */ private byte[] getBaseHashForMergedMining() { - byte[] encodedBlock = getEncoded(false, false); + byte[] encodedBlock = getEncoded(false, false, false); byte[] hashForMergedMining = HashUtil.keccak256(encodedBlock); if (isUMMBlock()) { diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java index 9dbef0db92e..b50cfa2a1f2 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java @@ -309,8 +309,21 @@ public BlockHeader build() { } } - return new BlockHeader( - activationConfig.getHeaderVersion(number), + if (activationConfig.getHeaderVersion(number) == 0x1) return new BlockHeaderV1( + parentHash, unclesHash, coinbase, + stateRoot, txTrieRoot, receiptTrieRoot, + logsBloom, difficulty, number, + gasLimit, gasUsed, timestamp, extraData, paidFees, + bitcoinMergedMiningHeader, + bitcoinMergedMiningMerkleProof, + bitcoinMergedMiningCoinbaseTransaction, + mergedMiningForkDetectionData, + minimumGasPrice, uncleCount, + false, useRskip92Encoding, + includeForkDetectionData, ummRoot + ); + + return new BlockHeaderV0( parentHash, unclesHash, coinbase, stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, number, diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java new file mode 100644 index 00000000000..bf0fcd8537f --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java @@ -0,0 +1,14 @@ +package org.ethereum.core; + +import org.ethereum.util.RLPList; + +public abstract class BlockHeaderExtension { + public abstract byte getHeaderVersion(); + public abstract byte[] getEncoded(); + + public static BlockHeaderExtension fromEncoded(RLPList encoded) { + byte version = encoded.get(0).getRLPData()[0]; + if (version == 0x1) return BlockHeaderExtensionV1.fromEncoded(encoded.getRLPData()); + return null; + } +} diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java new file mode 100644 index 00000000000..9809413d352 --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java @@ -0,0 +1,37 @@ +package org.ethereum.core; + +import org.ethereum.crypto.HashUtil; +import org.ethereum.util.RLP; +import org.ethereum.util.RLPList; + +public class BlockHeaderExtensionV1 extends BlockHeaderExtension { + @Override + public byte getHeaderVersion() { return 0x1; } + + private byte[] logsBloom; + public byte[] getLogsBloom() { return this.logsBloom; } + public void setLogsBloom(byte[] logsBloom) { this.logsBloom = logsBloom; } + + public BlockHeaderExtensionV1(byte[] logsBloom) { + this.logsBloom = logsBloom; + } + + public byte[] getHash() { + return HashUtil.keccak256(this.getEncoded()); + } + + @Override + public byte[] getEncoded() { + return RLP.encodeList( + RLP.encodeByte(this.getHeaderVersion()), + RLP.encodeElement(this.getLogsBloom()) + ); + } + + public static BlockHeaderExtensionV1 fromEncoded(byte[] encoded) { + RLPList rlpExtension = RLP.decodeList(encoded); + return new BlockHeaderExtensionV1( + rlpExtension.get(1).getRLPData() + ); + } +} diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java new file mode 100644 index 00000000000..1dcf5a45a8e --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java @@ -0,0 +1,70 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program 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. + * + * This program 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 this program. If not, see . + */ +package org.ethereum.core; + +import co.rsk.core.BlockDifficulty; +import co.rsk.core.Coin; +import co.rsk.core.RskAddress; +import org.ethereum.util.RLP; + +public class BlockHeaderV0 extends BlockHeader { + // block header for blocks before rskip351 + @Override + public byte getVersion() { return 0x0; } + @Override + public BlockHeaderExtension getExtension() { return null; } + @Override + public void setExtension(BlockHeaderExtension extension) {} + + private byte[] logsBloom; + + public BlockHeaderV0(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, + byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] logsBloom, BlockDifficulty difficulty, + long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, + Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, + byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, + Coin minimumGasPrice, int uncleCount, boolean sealed, + boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot) { + super(parentHash,unclesHash, coinbase, stateRoot, + txTrieRoot, receiptTrieRoot, difficulty, + number, gasLimit, gasUsed, timestamp, extraData, + paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, + bitcoinMergedMiningCoinbaseTransaction, mergedMiningForkDetectionData, + minimumGasPrice, uncleCount, sealed, + useRskip92Encoding, includeForkDetectionData, ummRoot); + this.logsBloom = logsBloom; + } + + @Override + public byte[] getLogsBloom() { return logsBloom; } + + public void setLogsBloom(byte[] logsBloom) { + if (this.sealed) { + throw new SealedBlockHeaderException("trying to alter logs bloom"); + } + this.hash = null; + + this.logsBloom = logsBloom; + } + + @Override + public byte[] getLogsBloomFieldEncoded() { + return RLP.encodeElement(this.logsBloom); + } +} diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java new file mode 100644 index 00000000000..ed8586471c8 --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java @@ -0,0 +1,57 @@ +package org.ethereum.core; + +import co.rsk.core.BlockDifficulty; +import co.rsk.core.Coin; +import co.rsk.core.RskAddress; +import org.ethereum.util.RLP; + +public class BlockHeaderV1 extends BlockHeader { + @Override + public byte getVersion() { return 0x1; } + + private BlockHeaderExtensionV1 extension; + public BlockHeaderExtensionV1 getExtension() { return this.extension; } + public void setExtension(BlockHeaderExtension extension) { this.extension = (BlockHeaderExtensionV1) extension; } + + public BlockHeaderV1(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, + byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] logsBloom, BlockDifficulty difficulty, + long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, + Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, + byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, + Coin minimumGasPrice, int uncleCount, boolean sealed, + boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot) { + super(parentHash,unclesHash, coinbase, stateRoot, + txTrieRoot, receiptTrieRoot, difficulty, + number, gasLimit, gasUsed, timestamp, extraData, + paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, + bitcoinMergedMiningCoinbaseTransaction, mergedMiningForkDetectionData, + minimumGasPrice, uncleCount, sealed, + useRskip92Encoding, includeForkDetectionData, ummRoot); + + this.extension = new BlockHeaderExtensionV1(logsBloom); + } + + @Override + public byte[] getLogsBloom() { + return this.extension.getLogsBloom(); + } + + public void setLogsBloom(byte[] logsBloom) { + /* A sealed block header is immutable, cannot be changed */ + if (this.sealed) { + throw new SealedBlockHeaderException("trying to alter logs bloom"); + } + this.hash = null; + + this.extension.setLogsBloom(logsBloom); + } + + @Override + public byte[] getLogsBloomFieldEncoded() { + byte[] ecnoded = new byte[Bloom.BLOOM_BYTES]; + ecnoded[0] = 0x1; + byte[] logsBloomHash = this.extension.getHash(); + System.arraycopy(logsBloomHash, 0, ecnoded, 1, logsBloomHash.length); + return RLP.encodeElement(ecnoded); + } +} diff --git a/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java b/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java index 5a49c64d04b..130db61b7c4 100644 --- a/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java @@ -8,7 +8,7 @@ import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; -public class GenesisHeader extends BlockHeader { +public class GenesisHeader extends BlockHeaderV0 { private final byte[] difficulty; @@ -29,7 +29,6 @@ public GenesisHeader(byte[] parentHash, byte[] coinbase, byte[] stateRootHash) { super( - (byte) 0x0, parentHash, unclesHash, new RskAddress(coinbase), @@ -73,7 +72,6 @@ public GenesisHeader(byte[] parentHash, boolean useRskip92Encoding, byte[] coinbase) { super( - (byte) 0x0, parentHash, unclesHash, new RskAddress(coinbase), diff --git a/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java b/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java index e9fc458a7b9..9ff36e38e5e 100644 --- a/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java +++ b/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java @@ -40,6 +40,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import org.ethereum.TestUtils; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.Block; import org.ethereum.core.BlockFactory; import org.ethereum.core.Blockchain; @@ -266,7 +267,7 @@ void connectBlocks() throws IOException, DslProcessorException { doReturn(world.getBlockStore()).when(rskContext).getBlockStore(); doReturn(world.getTrieStore()).when(rskContext).getTrieStore(); doReturn(receiptStore).when(rskContext).getReceiptStore(); - doReturn(new BlockFactory(ActivationConfigsForTest.all())).when(rskContext).getBlockFactory(); + doReturn(new BlockFactory(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))).when(rskContext).getBlockFactory(); doReturn(world.getTrieStore()).when(rskContext).getTrieStore(); doReturn(rskSystemProperties).when(rskContext).getRskSystemProperties(); doReturn(tempDir.toString()).when(rskSystemProperties).databaseDir(); @@ -320,7 +321,7 @@ void importBlocks() throws IOException, DslProcessorException { RskContext rskContext = mock(RskContext.class); RskSystemProperties rskSystemProperties = mock(RskSystemProperties.class); doReturn(world.getBlockStore()).when(rskContext).getBlockStore(); - doReturn(new BlockFactory(ActivationConfigsForTest.all())).when(rskContext).getBlockFactory(); + doReturn(new BlockFactory(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))).when(rskContext).getBlockFactory(); doReturn(rskSystemProperties).when(rskContext).getRskSystemProperties(); doReturn(tempDir.toString()).when(rskSystemProperties).databaseDir(); doReturn(DbKind.LEVEL_DB).when(rskSystemProperties).databaseKind(); diff --git a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java index f8c262270a4..8f8be6127ac 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java @@ -131,7 +131,7 @@ void decodeBlockAfterHeight449AndRskip110On() { BlockHeader header = createBlockHeaderWithMergedMiningFields(number, forkDetectionData, null); - byte[] encodedBlock = header.getEncoded(false, false); + byte[] encodedBlock = header.getEncoded(false, false, false); byte[] hashForMergedMining = Arrays.copyOfRange(HashUtil.keccak256(encodedBlock), 0, 20); byte[] coinbase = org.bouncycastle.util.Arrays.concatenate(hashForMergedMining, forkDetectionData); coinbase = org.bouncycastle.util.Arrays.concatenate(RskMiningConstants.RSK_TAG, coinbase); @@ -158,7 +158,7 @@ void decodeWithNoMergedMiningDataAndRskip110OffAndNoForkDetectionData() { BlockHeader header = createBlockHeader(number, new byte[0], null); - byte[] encodedHeader = header.getEncoded(false, false); + byte[] encodedHeader = header.getEncoded(false, false, false); RLPList headerRLP = RLP.decodeList(encodedHeader); MatcherAssert.assertThat(headerRLP.size(), is(16)); @@ -179,7 +179,7 @@ void decodeWithNoMergedMiningDataAndRskip110OffAndForkDetectionData() { byte[] forkDetectionData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; BlockHeader header = createBlockHeader(number, forkDetectionData, null); - byte[] encodedHeader = header.getEncoded(false, false); + byte[] encodedHeader = header.getEncoded(false, false, false); RLPList headerRLP = RLP.decodeList(encodedHeader); MatcherAssert.assertThat(headerRLP.size(), is(16)); diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java new file mode 100644 index 00000000000..5f8efe235aa --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java @@ -0,0 +1,73 @@ +package co.rsk.core; + +import org.ethereum.core.BlockHeaderExtensionV1; +import org.ethereum.core.Bloom; +import org.ethereum.util.RLP; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +public class BlockHeaderExtensionTest { + @Test + public void createFromBlockHeader() { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(logsBloom); + + Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); + } + + @Test + public void setLogsBloom() { + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(new byte[32]); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + extension.setLogsBloom(logsBloom); + + Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); + } + + @Test + public void hashIncludesLogsBloom() { + byte[] logsBloom1 = new byte[Bloom.BLOOM_BYTES]; + logsBloom1[0] = 0x01; + logsBloom1[1] = 0x02; + logsBloom1[2] = 0x03; + logsBloom1[3] = 0x04; + BlockHeaderExtensionV1 extension1 = new BlockHeaderExtensionV1(logsBloom1); + + byte[] logsBloom2 = new byte[Bloom.BLOOM_BYTES]; + logsBloom2[0] = 0x01; + logsBloom2[1] = 0x02; + logsBloom2[2] = 0x03; + logsBloom2[3] = 0x05; + BlockHeaderExtensionV1 extension2 = new BlockHeaderExtensionV1(logsBloom2); + + Assertions.assertFalse(Arrays.equals(extension1.getHash(), extension2.getHash())); + } + + @Test + public void encodeDecode() { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + BlockHeaderExtensionV1 extension = BlockHeaderExtensionV1.fromEncoded( + new BlockHeaderExtensionV1(logsBloom).getEncoded() + ); + + Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); + } +} diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java index 001867993ac..39294dc7abb 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java @@ -23,8 +23,7 @@ import co.rsk.peg.PegTestUtils; import com.google.common.primitives.Bytes; import org.ethereum.TestUtils; -import org.ethereum.core.BlockHeader; -import org.ethereum.core.Bloom; +import org.ethereum.core.*; import org.ethereum.crypto.HashUtil; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; @@ -54,7 +53,7 @@ class BlockHeaderTest { void getHashForMergedMiningWithForkDetectionDataAndIncludedOnAndMergedMiningFields() { BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], true, new byte[0]); - byte[] encodedBlock = header.getEncoded(false, false); + byte[] encodedBlock = header.getEncoded(false, false, false); byte[] hashForMergedMiningPrefix = Arrays.copyOfRange(HashUtil.keccak256(encodedBlock), 0, 20); byte[] forkDetectionData = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; byte[] hashForMergedMining = concatenate(hashForMergedMiningPrefix, forkDetectionData); @@ -169,7 +168,7 @@ void getEncodedEmptyUmmRootWithoutMergedMiningFields() { void getMiningForkDetectionData() { BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], true, new byte[0]); - byte[] encodedBlock = header.getEncoded(false, false); + byte[] encodedBlock = header.getEncoded(false, false, false); byte[] hashForMergedMining = Arrays.copyOfRange(HashUtil.keccak256(encodedBlock), 0, 20); byte[] forkDetectionData = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; byte[] coinbase = concatenate(hashForMergedMining, forkDetectionData); @@ -218,7 +217,7 @@ void getHashForMergedMiningWhenUmmRoot() { byte[] forkDetectionData = new byte[]{20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; BlockHeader header = createBlockHeaderWithUmmRoot(ummRoot, forkDetectionData); - byte[] encodedBlock = header.getEncoded(false, false); + byte[] encodedBlock = header.getEncoded(false, false, false); byte[] oldHashForMergedMining = HashUtil.keccak256(encodedBlock); byte[] leftHash = Arrays.copyOf(oldHashForMergedMining, 20); byte[] hashRoot = HashUtil.keccak256(concatenate(leftHash, ummRoot)); @@ -236,7 +235,7 @@ void getHashForMergedMiningWhenEmptyUmmRoot() { BlockHeader header = createBlockHeaderWithUmmRoot(ummRoot); - byte[] encodedBlock = header.getEncoded(false, false); + byte[] encodedBlock = header.getEncoded(false, false, false); byte[] hashForMergedMining = HashUtil.keccak256(encodedBlock); MatcherAssert.assertThat(header.getHashForMergedMining(), is(hashForMergedMining)); @@ -338,6 +337,25 @@ public void getVersion1() { this.testHeaderVersion((byte) 0x1); } + @Test + public void encodeForLogsBloomField() { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + BlockHeaderV1 header = (BlockHeaderV1) createBlockHeaderWithVersion((byte) 0x1); + header.setLogsBloom(logsBloom); + + byte[] logsBloomField = RLP.decode2(header.getLogsBloomFieldEncoded()).get(0).getRLPData(); + + Assertions.assertEquals(0x1, logsBloomField[0]); + Assertions.assertArrayEquals(header.getExtension().getHash(), Arrays.copyOfRange(logsBloomField, 1, 33)); + for (byte b:Arrays.copyOfRange(logsBloomField, 33, Bloom.BLOOM_BYTES)) Assertions.assertEquals(0x0, b); + Assertions.assertEquals(logsBloomField.length, Bloom.BLOOM_BYTES); + } + private BlockHeader createBlockHeaderWithMergedMiningFields( byte[] forkDetectionData, boolean includeForkDetectionData, byte[] ummRoot) { @@ -392,8 +410,33 @@ private BlockHeader createBlockHeader(byte version, BigInteger gasLimit = BigInteger.valueOf(6800000); long timestamp = 7731067; // Friday, 10 May 2019 6:04:05 - return new BlockHeader( - version, + if (version == 0x1) return new BlockHeaderV1( + PegTestUtils.createHash3().getBytes(), + HashUtil.keccak256(RLP.encodeList()), + new RskAddress(TestUtils.randomAddress().getBytes()), + HashUtil.EMPTY_TRIE_HASH, + "tx_trie_root".getBytes(), + HashUtil.EMPTY_TRIE_HASH, + new Bloom().getData(), + difficulty, + number, + gasLimit.toByteArray(), + 3000000, + timestamp, + new byte[0], + Coin.ZERO, + bitcoinMergedMiningHeader, + bitcoinMergedMiningMerkleProof, + bitcoinMergedMiningCoinbaseTransaction, + forkDetectionData, + Coin.valueOf(10L), + 0, + sealed, + useRskip92Encoding, + includeForkDetectionData, + ummRoot); + + return new BlockHeaderV0( PegTestUtils.createHash3().getBytes(), HashUtil.keccak256(RLP.encodeList()), new RskAddress(TestUtils.randomAddress().getBytes()), diff --git a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java index 540ad282c39..e29946f8b27 100644 --- a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java @@ -210,8 +210,7 @@ private void runMocked(Consumer task) { } private BlockHeader createBlockHeader() { - return new BlockHeader( - (byte) 0x0, + return new BlockHeaderV0( EMPTY_BYTE_ARRAY, EMPTY_BYTE_ARRAY, TestUtils.randomAddress(), EMPTY_TRIE_HASH, null, EMPTY_TRIE_HASH, new Bloom().getData(), BlockDifficulty.ZERO, 1L, diff --git a/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java b/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java index 085154e6b9e..7cc86313edb 100644 --- a/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java +++ b/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java @@ -577,7 +577,7 @@ DIFFICULTY_CALCULATOR, new PeersInformation(getChannelManager(), syncConfigurati mock(Genesis.class), mock(EthereumListener.class)); - BodyResponseMessage response = new BodyResponseMessage(new Random().nextLong(), null, null); + BodyResponseMessage response = new BodyResponseMessage(new Random().nextLong(), null, null, null); processor.registerExpectedMessage(response); processor.processBodyResponse(sender, response); @@ -614,8 +614,9 @@ DIFFICULTY_CALCULATOR, new PeersInformation(getChannelManager(), SyncConfigurati mock(EthereumListener.class)); List transactions = blockchain.getBestBlock().getTransactionsList(); List uncles = blockchain.getBestBlock().getUncleList(); + BlockHeaderExtension extension = blockchain.getBestBlock().getHeader().getExtension(); long lastRequestId = new Random().nextLong(); - BodyResponseMessage response = new BodyResponseMessage(lastRequestId, transactions, uncles); + BodyResponseMessage response = new BodyResponseMessage(lastRequestId, transactions, uncles, extension); processor.registerExpectedMessage(response); Deque headerStack = new ArrayDeque<>(); @@ -666,6 +667,7 @@ DIFFICULTY_CALCULATOR, new PeersInformation(getChannelManager(), SyncConfigurati mock(Genesis.class), listener); List uncles = blockchain.getBestBlock().getUncleList(); + BlockHeaderExtension extension = blockchain.getBestBlock().getHeader().getExtension(); Account senderAccount = createAccount("sender"); Account receiverAccount = createAccount("receiver"); Transaction tx = createTransaction(senderAccount, receiverAccount, BigInteger.valueOf(1000000), BigInteger.ZERO); @@ -673,7 +675,7 @@ DIFFICULTY_CALCULATOR, new PeersInformation(getChannelManager(), SyncConfigurati txs.add(tx); long lastRequestId = new Random().nextLong(); - BodyResponseMessage response = new BodyResponseMessage(lastRequestId, txs, uncles); + BodyResponseMessage response = new BodyResponseMessage(lastRequestId, txs, uncles, extension); processor.registerExpectedMessage(response); Deque headerStack = new ArrayDeque<>(); @@ -729,8 +731,9 @@ DIFFICULTY_CALCULATOR, new PeersInformation(getChannelManager(), SyncConfigurati mock(EthereumListener.class)); List transactions = blockchain.getBestBlock().getTransactionsList(); List uncles = blockchain.getBestBlock().getUncleList(); + BlockHeaderExtension extension = blockchain.getBestBlock().getHeader().getExtension(); long lastRequestId = new Random().nextLong(); - BodyResponseMessage response = new BodyResponseMessage(lastRequestId, transactions, uncles); + BodyResponseMessage response = new BodyResponseMessage(lastRequestId, transactions, uncles, extension); processor.registerExpectedMessage(response); Deque headerStack = new ArrayDeque<>(); @@ -820,8 +823,9 @@ DIFFICULTY_CALCULATOR, new PeersInformation(getChannelManager(), SyncConfigurati mock(EthereumListener.class)); List transactions = block.getTransactionsList(); List uncles = block.getUncleList(); + BlockHeaderExtension extension = block.getHeader().getExtension(); long lastRequestId = new Random().nextLong(); - BodyResponseMessage response = new BodyResponseMessage(lastRequestId, transactions, uncles); + BodyResponseMessage response = new BodyResponseMessage(lastRequestId, transactions, uncles, extension); processor.registerExpectedMessage(response); Deque headerStack = new ArrayDeque<>(); diff --git a/rskj-core/src/test/java/co/rsk/net/ThreeAsyncNodeUsingSyncProcessorTest.java b/rskj-core/src/test/java/co/rsk/net/ThreeAsyncNodeUsingSyncProcessorTest.java index 651b3c8378c..cb2dfa66462 100644 --- a/rskj-core/src/test/java/co/rsk/net/ThreeAsyncNodeUsingSyncProcessorTest.java +++ b/rskj-core/src/test/java/co/rsk/net/ThreeAsyncNodeUsingSyncProcessorTest.java @@ -289,7 +289,7 @@ public void synchronizeWithTwoPeers200AndOneFails() { node3.waitUntilNTasksWithTimeout(setupRequests); node3.waitUntilNTasksWithTimeout(5); // synchronize 200 (extra tasks are from old sync protocol messages) - BodyResponseMessage response = new BodyResponseMessage(new Random().nextLong(), null, null); + BodyResponseMessage response = new BodyResponseMessage(new Random().nextLong(), null, null, null); node3.getSyncProcessor().registerExpectedMessage(response); node3.getSyncProcessor().processBodyResponse(node1.getMessageChannel(node3), response); node3.waitExactlyNTasksWithTimeout(200 + setupRequests - 15); diff --git a/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java b/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java index 29c61376bec..7ecdfbcb2ac 100644 --- a/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java +++ b/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java @@ -36,7 +36,7 @@ void createMessage() { parent = block; } - BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles); + BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles, null); Assertions.assertEquals(100, message.getId()); @@ -69,7 +69,7 @@ void accept() { List transactions = new LinkedList<>(); List uncles = new LinkedList<>(); - BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles); + BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles, null); MessageVisitor visitor = mock(MessageVisitor.class); diff --git a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java index cb00e12714a..57fa3cb0389 100644 --- a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java +++ b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java @@ -26,6 +26,7 @@ import co.rsk.test.builders.TransactionBuilder; import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; import org.ethereum.crypto.HashUtil; import org.junit.jupiter.api.Assertions; @@ -194,6 +195,8 @@ void encodeDecodeBlockResponseMessage() { @Test void encodeDecodeBlockHeadersResponseMessage() { + BlockGenerator blockGenerator = new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)); + BlockFactory blockFactory = new BlockFactory(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)); List headers = new ArrayList<>(); for (int k = 1; k <= 4; k++) @@ -475,7 +478,7 @@ void encodeDecodeBodyResponseMessage() { parent = block; } - BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles); + BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles, null); byte[] encoded = message.getEncoded(); diff --git a/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncStateTest.java b/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncStateTest.java index e039b37a34b..98e91590dd4 100644 --- a/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncStateTest.java +++ b/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncStateTest.java @@ -182,7 +182,7 @@ void connectingUntilGenesis() { toRequest.addFirst(headerToRequest); when(syncEventsHandler.sendBodyRequest(any(), eq(headerToRequest))).thenReturn(i); - BodyResponseMessage response = new BodyResponseMessage(i, new LinkedList<>(), new LinkedList<>()); + BodyResponseMessage response = new BodyResponseMessage(i, new LinkedList<>(), new LinkedList<>(), null); responses.addFirst(response); Block block = mock(Block.class); @@ -248,7 +248,7 @@ void connecting_notGenesis() { toRequest.addFirst(headerToRequest); when(syncEventsHandler.sendBodyRequest(any(), eq(headerToRequest))).thenReturn(i); - BodyResponseMessage response = new BodyResponseMessage(i, new LinkedList<>(), new LinkedList<>()); + BodyResponseMessage response = new BodyResponseMessage(i, new LinkedList<>(), new LinkedList<>(), null); responses.addFirst(response); Block block = mock(Block.class); diff --git a/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBodiesSyncStateTest.java b/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBodiesSyncStateTest.java index 0b83d2dfd59..9a60de83fe0 100644 --- a/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBodiesSyncStateTest.java +++ b/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBodiesSyncStateTest.java @@ -85,7 +85,7 @@ void newBodyWhenUnexpectedMessageLogEvent() { pendingBodyResponses.put(messageId, pendingBodyResponse); TestUtils.setInternalState(state, "pendingBodyResponses", pendingBodyResponses); - BodyResponseMessage message = new BodyResponseMessage(33L, Collections.emptyList(), Collections.emptyList()); + BodyResponseMessage message = new BodyResponseMessage(33L, Collections.emptyList(), Collections.emptyList(), null); state.newBody(message, peer); verify(peersInformation, times(1)) .reportEventToPeerScoring(peer, EventType.UNEXPECTED_MESSAGE, @@ -111,7 +111,7 @@ void newBodyWhenUnexpectedMessageFromPeerLogEvent() { pendingBodyResponses.put(messageId, pendingBodyResponse); TestUtils.setInternalState(state, "pendingBodyResponses", pendingBodyResponses); - BodyResponseMessage message = new BodyResponseMessage(messageId, Collections.emptyList(), Collections.emptyList()); + BodyResponseMessage message = new BodyResponseMessage(messageId, Collections.emptyList(), Collections.emptyList(), null); state.newBody(message, peer); verify(peersInformation, times(1)) .reportEventToPeerScoring(peer, EventType.UNEXPECTED_MESSAGE, diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java index 7c6af98fa74..71537c7d379 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java @@ -281,14 +281,13 @@ public static Block createBlock(Block genesis, Block parentBlock, Keccak256 bloc ); } - private static class HardcodedHashBlockHeader extends BlockHeader { + private static class HardcodedHashBlockHeader extends BlockHeaderV0 { private final Keccak256 blockHash; public HardcodedHashBlockHeader( Block parentBlock, RskAddress coinbase, Block genesis, List txs, BlockDifficulty finalDifficulty, Coin paidFees, List uncles, Keccak256 blockHash) { super( - (byte) 0x0, parentBlock.getHash().getBytes(), RemascTestRunner.EMPTY_LIST_HASH, coinbase, genesis.getStateRoot(), BlockHashesHelper.getTxTrieRoot(txs, true), HashUtil.EMPTY_TRIE_HASH, new Bloom().getData(), finalDifficulty, parentBlock.getNumber() + 1, diff --git a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/subscribe/EthSubscriptionNotificationTest.java b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/subscribe/EthSubscriptionNotificationTest.java index dca4ae7a98f..f81ee5282ac 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/subscribe/EthSubscriptionNotificationTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/subscribe/EthSubscriptionNotificationTest.java @@ -20,6 +20,9 @@ import co.rsk.blockchain.utils.BlockGenerator; import co.rsk.rpc.JacksonBasedRpcSerializer; import co.rsk.rpc.JsonRpcSerializer; +import org.ethereum.config.Constants; +import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.Block; import org.junit.jupiter.api.Test; @@ -29,9 +32,9 @@ import static org.hamcrest.MatcherAssert.assertThat; class EthSubscriptionNotificationTest { - private static final Block TEST_BLOCK = new BlockGenerator().createBlock(12, 0); + private static final Block TEST_BLOCK = new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)).createBlock(12, 0); private static final String TEST_BLOCK_RESULT_JSON = "{\"difficulty\":\"0x20000\",\"extraData\":\"0x\",\"gasLimit\":\"0x2fefd8\",\"gasUsed\":\"0x0\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0xe94aef644e428941ee0a3741f28d80255fddba7f\",\"number\":\"0xc\",\"parentHash\":\"0xbe5de0c9c661653c979ec457f610444dcd0048007e683b2d04ce05729af56280\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x1\",\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"hash\":\"0x35b063d13f7d7b3c13ae508e2c2b3aa7e7ba110d4dda17f3d822ac24b1f952b7\"}"; - private static final Block TEST_BLOCK_2 = new BlockGenerator().createBlock(12, 0, 1000000L); + private static final Block TEST_BLOCK_2 = new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)).createBlock(12, 0, 1000000L); private static final String TEST_BLOCK_RESULT_JSON_2 = "{\"difficulty\":\"0x20000\",\"extraData\":\"0x\",\"gasLimit\":\"0xf4240\",\"gasUsed\":\"0x0\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0xe94aef644e428941ee0a3741f28d80255fddba7f\",\"number\":\"0xc\",\"parentHash\":\"0x82d804adc43b6382427216a764963f77c612694065f19b3b97c804338c6ceeec\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x1\",\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"hash\":\"0xa31b89729ad3f84b988a9418e629adf1de918dfcff8286ed65e06837574e80d8\"}"; private static final String TEST_SYNC_RESULT_JSON = "{\"syncing\":true,\"status\":{\"startingBlock\":0,\"currentBlock\":1,\"highestBlock\":1000}}"; diff --git a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java index b2814c8a3b8..778c538ce6f 100644 --- a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java +++ b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java @@ -28,6 +28,9 @@ import co.rsk.peg.BridgeSupportFactory; import co.rsk.trie.TrieStore; import org.bouncycastle.util.BigIntegers; +import org.ethereum.config.Constants; +import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; import org.ethereum.datasource.HashMapDB; import org.ethereum.db.BlockStore; @@ -53,13 +56,17 @@ public class BlockBuilder { private final BridgeSupportFactory bridgeSupportFactory; private BlockStore blockStore; - public BlockBuilder(Blockchain blockChain, BridgeSupportFactory bridgeSupportFactory, BlockStore blockStore) { + public BlockBuilder(Blockchain blockChain, BridgeSupportFactory bridgeSupportFactory, BlockStore blockStore, BlockGenerator blockGenerator) { this.blockChain = blockChain; - this.blockGenerator = new BlockGenerator(); + this.blockGenerator = blockGenerator; this.bridgeSupportFactory = bridgeSupportFactory; this.blockStore = blockStore; } + public BlockBuilder(Blockchain blockChain, BridgeSupportFactory bridgeSupportFactory, BlockStore blockStore) { + this(blockChain, bridgeSupportFactory, blockStore, new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))); + } + public BlockBuilder parent(Block parent) { this.parent = parent; this.gasLimit = parent.getGasLimit(); diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java index 35ff8ca85d5..27d140c3058 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java @@ -27,6 +27,10 @@ import java.util.ArrayList; import java.util.List; +import co.rsk.blockchain.utils.BlockGenerator; +import org.ethereum.config.Constants; +import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.Account; import org.ethereum.core.Block; import org.ethereum.core.Blockchain; @@ -1176,7 +1180,7 @@ public static void addEmptyBlockToBlockchain( List txs = new ArrayList<>(); txs.add(tx); - Block block1 = new BlockBuilder(blockChain, null, blockStore) + Block block1 = new BlockBuilder(blockChain, null, blockStore, new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))) .trieStore(trieStore).parent(genesis).transactions(txs).build(); assertEquals(ImportResult.IMPORTED_BEST, blockChain.tryToConnect(block1)); } diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java index d4944cdc027..21878db2045 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java @@ -18,6 +18,7 @@ package org.ethereum.rpc; +import co.rsk.blockchain.utils.BlockGenerator; import co.rsk.config.RskSystemProperties; import co.rsk.config.TestSystemProperties; import co.rsk.core.*; @@ -58,6 +59,9 @@ import co.rsk.util.TestContract; import org.bouncycastle.util.encoders.Hex; import org.ethereum.TestUtils; +import org.ethereum.config.Constants; +import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.HashUtil; @@ -1038,8 +1042,12 @@ void getBlockByNumber() { Block block1 = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(10).parent(genesis).build(); assertEquals(ImportResult.IMPORTED_BEST, world.getBlockChain().tryToConnect(block1)); - Block block1b = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), - world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(2).parent(genesis).build(); + Block block1b = new BlockBuilder( + world.getBlockChain(), + world.getBridgeSupportFactory(), + world.getBlockStore(), + new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))) + .trieStore(world.getTrieStore()).difficulty(2).parent(genesis).build(); block1b.setBitcoinMergedMiningHeader(new byte[]{0x01}); Block block2b = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(11).parent(block1b).build(); @@ -1180,8 +1188,11 @@ void getBlockByHash() { world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(10).parent(genesis).build(); block1.setBitcoinMergedMiningHeader(new byte[]{0x01}); assertEquals(ImportResult.IMPORTED_BEST, world.getBlockChain().tryToConnect(block1)); - Block block1b = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), - world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(block1.getDifficulty().asBigInteger().longValue() - 1).parent(genesis).build(); + Block block1b = new BlockBuilder( + world.getBlockChain(), + world.getBridgeSupportFactory(), + world.getBlockStore(), + new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))).trieStore(world.getTrieStore()).difficulty(block1.getDifficulty().asBigInteger().longValue() - 1).parent(genesis).build(); block1b.setBitcoinMergedMiningHeader(new byte[]{0x01}); Block block2b = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(2).parent(block1b).build(); From d9a053751af034dd4e3ee4fe3aa3dc524a6f0432 Mon Sep 17 00:00:00 2001 From: Ilan Date: Sun, 13 Nov 2022 21:03:16 -0300 Subject: [PATCH 06/89] Add tests --- .../java/co/rsk/cli/tools/CliToolsTest.java | 37 +++++- .../java/co/rsk/core/BlockFactoryTest.java | 23 ++++ .../co/rsk/core/BlockHeaderExtensionTest.java | 57 ++-------- .../rsk/core/BlockHeaderExtensionV1Test.java | 80 +++++++++++++ .../java/co/rsk/core/BlockHeaderTest.java | 103 +++++++++++++++++ .../java/co/rsk/core/BlockHeaderV0Test.java | 69 ++++++++++++ .../java/co/rsk/core/BlockHeaderV1Test.java | 105 ++++++++++++++++++ .../co/rsk/net/NodeBlockProcessorTest.java | 40 +++++++ .../java/co/rsk/net/SyncProcessorTest.java | 23 +++- .../net/messages/BodyResponseMessageTest.java | 25 +++-- .../java/co/rsk/net/messages/MessageTest.java | 103 ++++++++++++++--- ...wnloadingBackwardsBodiesSyncStateTest.java | 70 ++++++++++++ .../src/test/java/co/rsk/test/World.java | 7 ++ .../co/rsk/test/builders/BlockBuilder.java | 9 +- .../rsk/test/builders/BlockChainBuilder.java | 10 +- .../co/rsk/test/dsl/WorldDslProcessor.java | 4 +- .../ethereum/core/BlockHeaderBuilderTest.java | 4 +- .../java/org/ethereum/rpc/Web3ImplTest.java | 90 +++++++++------ 18 files changed, 736 insertions(+), 123 deletions(-) create mode 100644 rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java create mode 100644 rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java create mode 100644 rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java diff --git a/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java b/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java index 9ff36e38e5e..3c307270e5b 100644 --- a/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java +++ b/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java @@ -38,6 +38,7 @@ import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.typesafe.config.ConfigValueFactory; import org.ethereum.TestUtils; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; import org.ethereum.config.blockchain.upgrades.ConsensusRule; @@ -267,7 +268,7 @@ void connectBlocks() throws IOException, DslProcessorException { doReturn(world.getBlockStore()).when(rskContext).getBlockStore(); doReturn(world.getTrieStore()).when(rskContext).getTrieStore(); doReturn(receiptStore).when(rskContext).getReceiptStore(); - doReturn(new BlockFactory(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))).when(rskContext).getBlockFactory(); + doReturn(new BlockFactory(world.getConfig().getActivationConfig())).when(rskContext).getBlockFactory(); doReturn(world.getTrieStore()).when(rskContext).getTrieStore(); doReturn(rskSystemProperties).when(rskContext).getRskSystemProperties(); doReturn(tempDir.toString()).when(rskSystemProperties).databaseDir(); @@ -284,11 +285,8 @@ void connectBlocks() throws IOException, DslProcessorException { verify(stopper).stop(0); } - @Test - void importBlocks() throws IOException, DslProcessorException { + void testImportBlocks(World world) throws IOException, DslProcessorException { DslParser parser = DslParser.fromResource("dsl/blocks01b.txt"); - ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); - World world = new World(receiptStore); WorldDslProcessor processor = new WorldDslProcessor(world); processor.processCommands(parser); @@ -321,7 +319,7 @@ void importBlocks() throws IOException, DslProcessorException { RskContext rskContext = mock(RskContext.class); RskSystemProperties rskSystemProperties = mock(RskSystemProperties.class); doReturn(world.getBlockStore()).when(rskContext).getBlockStore(); - doReturn(new BlockFactory(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))).when(rskContext).getBlockFactory(); + doReturn(new BlockFactory(world.getConfig().getActivationConfig())).when(rskContext).getBlockFactory(); doReturn(rskSystemProperties).when(rskContext).getRskSystemProperties(); doReturn(tempDir.toString()).when(rskSystemProperties).databaseDir(); doReturn(DbKind.LEVEL_DB).when(rskSystemProperties).databaseKind(); @@ -336,6 +334,33 @@ void importBlocks() throws IOException, DslProcessorException { verify(stopper).stop(0); } + @Test + void importBlocks() throws IOException, DslProcessorException { + ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); + TestSystemProperties config = new TestSystemProperties(rawConfig -> + rawConfig.withValue("blockchain.config.hardforkActivationHeights.fingerroot500", ConfigValueFactory.fromAnyRef(-1)) + ); + World world = new World(receiptStore, config); + testImportBlocks(world); + } + + @Test + void importBlocksWithRskip351InMiddle() throws IOException, DslProcessorException { + ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); + TestSystemProperties config = new TestSystemProperties(rawConfig -> + rawConfig.withValue("blockchain.config.hardforkActivationHeights.fingerroot500", ConfigValueFactory.fromAnyRef(2)) + ); + World world = new World(receiptStore, config); + testImportBlocks(world); + } + + @Test + void importBlocksWithRskip351() throws IOException, DslProcessorException { + ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); + World world = new World(receiptStore); + testImportBlocks(world); + } + @Test void importState() throws IOException { byte[] value = new byte[42]; diff --git a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java index 8f8be6127ac..24072c62a11 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java @@ -41,6 +41,7 @@ import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.AdditionalMatchers.geq; +import static org.mockito.AdditionalMatchers.lt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -294,12 +295,34 @@ public void genesisHasVersion0() { assertEquals((byte) 0x0, factory.decodeBlock(genesisRaw()).getHeader().getVersion()); } + @Test + public void headerIsVersion0BeforeActivation () { + long number = 20L; + enableRskip351At(number); + BlockHeader header = factory.getBlockHeaderBuilder().setNumber(number - 1).build(); + Assertions.assertEquals(header.getVersion(), 0); + } + + @Test + public void headerIsVersion1AfterActivation () { + long number = 20L; + enableRskip351At(number); + BlockHeader header = factory.getBlockHeaderBuilder().setNumber(number).build(); + Assertions.assertEquals(header.getVersion(), 1); + } + private void enableRulesAt(long number, ConsensusRule... consensusRules) { for (ConsensusRule consensusRule : consensusRules) { when(activationConfig.isActive(eq(consensusRule), geq(number))).thenReturn(true); } } + private void enableRskip351At(long number) { + when(activationConfig.getHeaderVersion(lt(number))).thenReturn((byte) 0x0); + when(activationConfig.getHeaderVersion(geq(number))).thenReturn((byte) 0x1); + when(activationConfig.isActive(eq(RSKIP351), geq(number))).thenReturn(true); + } + private BlockHeader createBlockHeaderWithMergedMiningFields( long number, byte[] forkDetectionData, diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java index 5f8efe235aa..e9443c1c43c 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java @@ -1,8 +1,10 @@ package co.rsk.core; +import org.ethereum.core.BlockHeaderExtension; import org.ethereum.core.BlockHeaderExtensionV1; import org.ethereum.core.Bloom; import org.ethereum.util.RLP; +import org.ethereum.util.RLPList; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -10,64 +12,19 @@ public class BlockHeaderExtensionTest { @Test - public void createFromBlockHeader() { + public void decodeV1() { byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; logsBloom[0] = 0x01; logsBloom[1] = 0x02; logsBloom[2] = 0x03; logsBloom[3] = 0x04; - BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(logsBloom); - Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); - } - - @Test - public void setLogsBloom() { - BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(new byte[32]); - - byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; - logsBloom[0] = 0x01; - logsBloom[1] = 0x02; - logsBloom[2] = 0x03; - logsBloom[3] = 0x04; - - extension.setLogsBloom(logsBloom); - - Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); - } - - @Test - public void hashIncludesLogsBloom() { - byte[] logsBloom1 = new byte[Bloom.BLOOM_BYTES]; - logsBloom1[0] = 0x01; - logsBloom1[1] = 0x02; - logsBloom1[2] = 0x03; - logsBloom1[3] = 0x04; - BlockHeaderExtensionV1 extension1 = new BlockHeaderExtensionV1(logsBloom1); - - byte[] logsBloom2 = new byte[Bloom.BLOOM_BYTES]; - logsBloom2[0] = 0x01; - logsBloom2[1] = 0x02; - logsBloom2[2] = 0x03; - logsBloom2[3] = 0x05; - BlockHeaderExtensionV1 extension2 = new BlockHeaderExtensionV1(logsBloom2); - - Assertions.assertFalse(Arrays.equals(extension1.getHash(), extension2.getHash())); - } - - @Test - public void encodeDecode() { - byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; - logsBloom[0] = 0x01; - logsBloom[1] = 0x02; - logsBloom[2] = 0x03; - logsBloom[3] = 0x04; - - BlockHeaderExtensionV1 extension = BlockHeaderExtensionV1.fromEncoded( - new BlockHeaderExtensionV1(logsBloom).getEncoded() + BlockHeaderExtension decoded = BlockHeaderExtension.fromEncoded( + RLP.decodeList(extension.getEncoded()) ); - Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); + Assertions.assertEquals(extension.getHeaderVersion(), decoded.getHeaderVersion()); + Assertions.assertArrayEquals(extension.getHash(), extension.getHash()); } } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java new file mode 100644 index 00000000000..1caa1231146 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java @@ -0,0 +1,80 @@ +package co.rsk.core; + +import org.ethereum.TestUtils; +import org.ethereum.core.BlockHeaderExtension; +import org.ethereum.core.BlockHeaderExtensionV1; +import org.ethereum.core.Bloom; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +public class BlockHeaderExtensionV1Test { + @Test + public void hasVersion1 () { + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(TestUtils.randomBytes(256)); + Assertions.assertEquals(1, extension.getHeaderVersion()); + } + + @Test + public void createWithLogsBloom() { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(logsBloom); + + Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); + } + + @Test + public void setLogsBloom() { + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(new byte[32]); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + extension.setLogsBloom(logsBloom); + + Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); + } + + @Test + public void hashIncludesLogsBloom() { + byte[] logsBloom1 = new byte[Bloom.BLOOM_BYTES]; + logsBloom1[0] = 0x01; + logsBloom1[1] = 0x02; + logsBloom1[2] = 0x03; + logsBloom1[3] = 0x04; + BlockHeaderExtensionV1 extension1 = new BlockHeaderExtensionV1(logsBloom1); + + byte[] logsBloom2 = new byte[Bloom.BLOOM_BYTES]; + logsBloom2[0] = 0x01; + logsBloom2[1] = 0x02; + logsBloom2[2] = 0x03; + logsBloom2[3] = 0x05; + BlockHeaderExtensionV1 extension2 = new BlockHeaderExtensionV1(logsBloom2); + + Assertions.assertFalse(Arrays.equals(extension1.getHash(), extension2.getHash())); + } + + @Test + public void encodeDecode() { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + BlockHeaderExtensionV1 extension = BlockHeaderExtensionV1.fromEncoded( + new BlockHeaderExtensionV1(logsBloom).getEncoded() + ); + + Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); + } +} diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java index 39294dc7abb..bd723e54eab 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java @@ -356,6 +356,109 @@ public void encodeForLogsBloomField() { Assertions.assertEquals(logsBloomField.length, Bloom.BLOOM_BYTES); } + private BlockHeaderV1 createV1FromV0(BlockHeaderV0 headerV0) { + return new BlockHeaderV1( + headerV0.getParentHash().getBytes(), headerV0.getUnclesHash(), headerV0.getCoinbase(), headerV0.getStateRoot(), + headerV0.getTxTrieRoot(), headerV0.getReceiptsRoot(), headerV0.getLogsBloom(), headerV0.getDifficulty(), + headerV0.getNumber(), headerV0.getGasLimit(), headerV0.getGasUsed(), headerV0.getTimestamp(), headerV0.getExtraData(), + headerV0.getPaidFees(), headerV0.getBitcoinMergedMiningHeader(), headerV0.getBitcoinMergedMiningMerkleProof(), + headerV0.getBitcoinMergedMiningCoinbaseTransaction(), headerV0.getMiningForkDetectionData(), + headerV0.getMinimumGasPrice(), headerV0.getUncleCount(), headerV0.isSealed(), + false, false, headerV0.getUmmRoot() + ); + } + + @Test + public void encodedV0IsTheSameForV0andV1 () { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + BlockHeaderV0 headerV0 = (BlockHeaderV0) createBlockHeaderWithVersion((byte) 0x0); + headerV0.setLogsBloom(logsBloom); + + BlockHeaderV1 headerV1 = createV1FromV0(headerV0); + + Assertions.assertArrayEquals(headerV0.getEncoded(), headerV1.getEncoded()); + } + + @Test + public void fullEncodedV0IsTheSameForV0andV1 () { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + BlockHeaderV0 headerV0 = (BlockHeaderV0) createBlockHeaderWithVersion((byte) 0x0); + headerV0.setLogsBloom(logsBloom); + + BlockHeaderV1 headerV1 = createV1FromV0(headerV0); + + Assertions.assertArrayEquals(headerV0.getFullEncoded(), headerV1.getFullEncoded()); + } + + @Test + public void fullEncodedV0IsTheSameAsEncodedForHeaderMessage () { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + BlockHeaderV0 headerV0 = (BlockHeaderV0) createBlockHeaderWithVersion((byte) 0x0); + headerV0.setLogsBloom(logsBloom); + + Assertions.assertArrayEquals(headerV0.getFullEncoded(), headerV0.getEncodedForHeaderMessage()); + } + + @Test + public void fullEncodedV1IsTheSameAsEncodedForHeaderMessageButLogsBloom () { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + + BlockHeaderV1 headerV1 = (BlockHeaderV1) createBlockHeaderWithVersion((byte) 0x1); + headerV1.setLogsBloom(logsBloom); + + RLPList fullEncoded = RLP.decodeList(headerV1.getFullEncoded()); + RLPList encodedForHeaderMessage = RLP.decodeList(headerV1.getEncodedForHeaderMessage()); + + Assertions.assertEquals(fullEncoded.size(), encodedForHeaderMessage.size()); + + for (int i = 0; i < fullEncoded.size(); i++) + if (i != 6) // logs bloom field + Assertions.assertArrayEquals(fullEncoded.get(i).getRLPData(), encodedForHeaderMessage.get(i).getRLPData()); + + Assertions.assertFalse(Arrays.equals(fullEncoded.get(6).getRLPData(), encodedForHeaderMessage.get(6).getRLPData())); + } + + @Test + public void hashOfV1IncludesLogsBloom() { + BlockHeaderV1 headerV1 = (BlockHeaderV1) createBlockHeaderWithVersion((byte) 0x1); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + headerV1.setLogsBloom(logsBloom); + byte[] hash = headerV1.getHash().getBytes(); + + byte[] otherLogsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x05; + headerV1.setLogsBloom(otherLogsBloom); + + Assertions.assertFalse(Arrays.equals(hash, headerV1.getHash().getBytes())); + } + private BlockHeader createBlockHeaderWithMergedMiningFields( byte[] forkDetectionData, boolean includeForkDetectionData, byte[] ummRoot) { diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java new file mode 100644 index 00000000000..82525cd5cdf --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java @@ -0,0 +1,69 @@ +package co.rsk.core; + +import co.rsk.peg.PegTestUtils; +import org.ethereum.TestUtils; +import org.ethereum.core.BlockHeaderExtensionV1; +import org.ethereum.core.BlockHeaderV0; +import org.ethereum.core.BlockHeaderV1; +import org.ethereum.core.Bloom; +import org.ethereum.crypto.HashUtil; +import org.ethereum.util.RLP; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.util.Arrays; + +public class BlockHeaderV0Test { + private BlockHeaderV0 createBlockHeader(byte[] logsBloom) { + return new BlockHeaderV0( + PegTestUtils.createHash3().getBytes(), + HashUtil.keccak256(RLP.encodeList()), + new RskAddress(TestUtils.randomAddress().getBytes()), + HashUtil.EMPTY_TRIE_HASH, + "tx_trie_root".getBytes(), + HashUtil.EMPTY_TRIE_HASH, + logsBloom, + new BlockDifficulty(BigInteger.ONE), + 1, + BigInteger.valueOf(6800000).toByteArray(), + 3000000, + 7731067, + new byte[0], + Coin.ZERO, + new byte[80], + new byte[32], + new byte[128], + new byte[0], + Coin.valueOf(10L), + 0, + false, + false, + false, + null + ); + } + + @Test + void hasNullExtension() { + BlockHeaderV0 header = createBlockHeader(TestUtils.randomBytes(256)); + Assertions.assertEquals(null, header.getExtension()); + } + + @Test + void setsExtensionIsVoid() { + BlockHeaderV0 header = createBlockHeader(TestUtils.randomBytes(256)); + byte[] bloom = Arrays.copyOf(header.getLogsBloom(), header.getLogsBloom().length); + header.setExtension(new BlockHeaderExtensionV1(TestUtils.randomBytes(256))); + Assertions.assertEquals(null, header.getExtension()); + Assertions.assertArrayEquals(bloom, header.getLogsBloom()); + } + + @Test + void logsBloomFieldEncoded() { + byte[] bloom = TestUtils.randomBytes(256); + BlockHeaderV0 header = createBlockHeader(bloom); + byte[] field = RLP.decode2(header.getLogsBloomFieldEncoded()).get(0).getRLPData(); + Assertions.assertArrayEquals(bloom, field); + } +} diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java new file mode 100644 index 00000000000..ac909b15a18 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java @@ -0,0 +1,105 @@ +package co.rsk.core; + +import co.rsk.peg.PegTestUtils; +import org.ethereum.TestUtils; +import org.ethereum.core.BlockHeaderExtensionV1; +import org.ethereum.core.BlockHeaderV1; +import org.ethereum.core.Bloom; +import org.ethereum.crypto.HashUtil; +import org.ethereum.util.RLP; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.math.BigInteger; +import java.util.Arrays; + +public class BlockHeaderV1Test { + private BlockHeaderV1 createBlockHeader(byte[] logsBloom) { + return new BlockHeaderV1( + PegTestUtils.createHash3().getBytes(), + HashUtil.keccak256(RLP.encodeList()), + new RskAddress(TestUtils.randomAddress().getBytes()), + HashUtil.EMPTY_TRIE_HASH, + "tx_trie_root".getBytes(), + HashUtil.EMPTY_TRIE_HASH, + logsBloom, + new BlockDifficulty(BigInteger.ONE), + 1, + BigInteger.valueOf(6800000).toByteArray(), + 3000000, + 7731067, + new byte[0], + Coin.ZERO, + new byte[80], + new byte[32], + new byte[128], + new byte[0], + Coin.valueOf(10L), + 0, + false, + false, + false, + null + ); + } + + @Test + void createsAnExtensionWithGivenData() { + byte[] bloom = TestUtils.randomBytes(256); + BlockHeaderV1 header = createBlockHeader(bloom); + Assertions.assertArrayEquals(bloom, header.getExtension().getLogsBloom()); + } + + @Test + void setsExtension() { + byte[] bloom = TestUtils.randomBytes(256); + BlockHeaderV1 header = createBlockHeader(bloom); + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(bloom); + header.setExtension(extension); + Assertions.assertArrayEquals(extension.getEncoded(), header.getExtension().getEncoded()); + } + + @Test + void setsLogsBloomToExtension() { + byte[] bloom = TestUtils.randomBytes(256); + BlockHeaderV1 header = createBlockHeader(new byte[]{}); + header.setLogsBloom(bloom); + Assertions.assertArrayEquals(bloom, header.getExtension().getLogsBloom()); + } + + @Test + void logsBloomFieldEncoded() { + byte[] bloom = TestUtils.randomBytes(256); + BlockHeaderV1 header = createBlockHeader(bloom); + byte[] field = RLP.decode2(header.getLogsBloomFieldEncoded()).get(0).getRLPData(); + Assertions.assertEquals((byte) 0x1, field[0]); + for (int i = 33; i < 256; i++) Assertions.assertEquals((byte) 0x0, field[i]); + Assertions.assertEquals(field.length, Bloom.BLOOM_BYTES); + } + + BlockHeaderV1 encodedHeaderWithRandomLogsBloom() { + return createBlockHeader(TestUtils.randomBytes(256)); + } + + byte[] getLogsBloomFieldHashPart(byte[] encodedHeader) { + return Arrays.copyOfRange(encodedHeader, 1, 33); + } + + @Test + void logsBloomFieldEncodedIncludesExtensionHash() { + BlockHeaderV1 header = encodedHeaderWithRandomLogsBloom(); + BlockHeaderExtensionV1 extension = Mockito.mock(BlockHeaderExtensionV1.class); + byte[] hash = TestUtils.randomBytes(32); + Mockito.when(extension.getHash()).thenReturn(hash); + header.setExtension(extension); + byte[] encoded = header.getLogsBloomFieldEncoded(); + + BlockHeaderExtensionV1 otherExtension = Mockito.mock(BlockHeaderExtensionV1.class); + byte[] otherHash = TestUtils.randomBytes(32); + Mockito.when(otherExtension.getHash()).thenReturn(otherHash); + header.setExtension(otherExtension); + + Assertions.assertFalse(Arrays.equals(encoded, header.getLogsBloomFieldEncoded())); + } +} diff --git a/rskj-core/src/test/java/co/rsk/net/NodeBlockProcessorTest.java b/rskj-core/src/test/java/co/rsk/net/NodeBlockProcessorTest.java index 6214ffc079a..fb71ebb95a5 100644 --- a/rskj-core/src/test/java/co/rsk/net/NodeBlockProcessorTest.java +++ b/rskj-core/src/test/java/co/rsk/net/NodeBlockProcessorTest.java @@ -27,6 +27,7 @@ import co.rsk.net.sync.SyncConfiguration; import co.rsk.test.builders.BlockChainBuilder; import co.rsk.validators.DummyBlockValidator; +import com.typesafe.config.ConfigValueFactory; import org.ethereum.core.Block; import org.ethereum.core.BlockIdentifier; import org.ethereum.core.Blockchain; @@ -776,6 +777,40 @@ void processBlockRequestMessageUsingBlockInStore() throws UnknownHostException { Assertions.assertEquals(block.getHash(), bMessage.getBlock().getHash()); } + @Test + void processBodyRequestMessageUsingBlockInBlockchainWithoutRskip351() throws UnknownHostException { + TestSystemProperties config = new TestSystemProperties(rawConfig -> + rawConfig.withValue("blockchain.config.hardforkActivationHeights.fingerroot500", ConfigValueFactory.fromAnyRef(-1)) + ); + final Blockchain blockchain = new BlockChainBuilder() + .setConfig(config) + .ofSize(10, new BlockGenerator(config.getNetworkConstants(), config.getActivationConfig())); + final Block block = blockchain.getBlockByNumber(3); + final NetBlockStore store = new NetBlockStore(); + BlockNodeInformation nodeInformation = new BlockNodeInformation(); + SyncConfiguration syncConfiguration = SyncConfiguration.IMMEDIATE_FOR_TESTING; + BlockSyncService blockSyncService = new BlockSyncService(config, store, blockchain, nodeInformation, syncConfiguration, DummyBlockValidator.VALID_RESULT_INSTANCE); + final NodeBlockProcessor processor = new NodeBlockProcessor(store, blockchain, nodeInformation, blockSyncService, syncConfiguration); + + final SimplePeer sender = new SimplePeer(); + + processor.processBodyRequest(sender, 100, block.getHash().getBytes()); + + Assertions.assertFalse(sender.getMessages().isEmpty()); + Assertions.assertEquals(1, sender.getMessages().size()); + + final Message message = sender.getMessages().get(0); + + Assertions.assertEquals(MessageType.BODY_RESPONSE_MESSAGE, message.getMessageType()); + + final BodyResponseMessage bMessage = (BodyResponseMessage) message; + + Assertions.assertEquals(100, bMessage.getId()); + Assertions.assertEquals(block.getTransactionsList(), bMessage.getTransactions()); + Assertions.assertEquals(block.getUncleList(), bMessage.getUncles()); + Assertions.assertEquals(null, bMessage.getBlockHeaderExtension()); + } + @Test void processBodyRequestMessageUsingBlockInBlockchain() throws UnknownHostException { final Blockchain blockchain = new BlockChainBuilder().ofSize(10); @@ -803,6 +838,11 @@ void processBodyRequestMessageUsingBlockInBlockchain() throws UnknownHostExcepti Assertions.assertEquals(100, bMessage.getId()); Assertions.assertEquals(block.getTransactionsList(), bMessage.getTransactions()); Assertions.assertEquals(block.getUncleList(), bMessage.getUncles()); + Assertions.assertNotNull(bMessage.getBlockHeaderExtension()); + Assertions.assertArrayEquals( + block.getHeader().getExtension().getEncoded(), + bMessage.getBlockHeaderExtension().getEncoded() + ); } @Test diff --git a/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java b/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java index 7cc86313edb..a7f521c7e5f 100644 --- a/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java +++ b/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java @@ -18,6 +18,7 @@ import co.rsk.scoring.PeerScoringManager; import co.rsk.test.builders.BlockChainBuilder; import co.rsk.validators.*; +import com.typesafe.config.ConfigValueFactory; import org.bouncycastle.util.encoders.Hex; import org.ethereum.core.*; import org.ethereum.crypto.ECKey; @@ -585,21 +586,20 @@ DIFFICULTY_CALCULATOR, new PeersInformation(getChannelManager(), syncConfigurati Assertions.assertTrue(processor.getExpectedResponses().isEmpty()); } - @Test - void processBodyResponseAddsToBlockchain() { + void testProcessBodyResponseAddsToBlockchain(TestSystemProperties config) { final NetBlockStore store = new NetBlockStore(); - Blockchain blockchain = new BlockChainBuilder().ofSize(10); + Blockchain blockchain = new BlockChainBuilder().setConfig(config).ofSize(10, new BlockGenerator(config.getNetworkConstants(), config.getActivationConfig())); + SimplePeer sender = new SimplePeer(new byte[] { 0x01 }); Assertions.assertEquals(10, blockchain.getBestBlock().getNumber()); - Block block = new BlockGenerator().createChildBlock(blockchain.getBlockByNumber(10)); + Block block = new BlockGenerator(config.getNetworkConstants(), config.getActivationConfig()).createChildBlock(blockchain.getBlockByNumber(10)); Assertions.assertEquals(11, block.getNumber()); Assertions.assertArrayEquals(blockchain.getBestBlockHash(), block.getParentHash().getBytes()); BlockNodeInformation nodeInformation = new BlockNodeInformation(); - TestSystemProperties config = new TestSystemProperties(); BlockSyncService blockSyncService = new BlockSyncService(config, store, blockchain, nodeInformation, SyncConfiguration.IMMEDIATE_FOR_TESTING, DummyBlockValidator.VALID_RESULT_INSTANCE); SyncProcessor processor = new SyncProcessor( @@ -636,6 +636,19 @@ DIFFICULTY_CALCULATOR, new PeersInformation(getChannelManager(), SyncConfigurati Assertions.assertEquals(11, blockchain.getBestBlock().getNumber()); Assertions.assertArrayEquals(block.getHash().getBytes(), blockchain.getBestBlockHash()); Assertions.assertTrue(processor.getExpectedResponses().isEmpty()); + System.out.println(block.getHeader().getVersion()); + } + + @Test + void processBodyResponseAddsToBlockchain() { + testProcessBodyResponseAddsToBlockchain(new TestSystemProperties()); + } + + @Test + void processBodyResponseAddsToBlockchainWithoutFingerroot500() { + testProcessBodyResponseAddsToBlockchain(new TestSystemProperties(rawConfig -> + rawConfig.withValue("blockchain.config.hardforkActivationHeights.fingerroot500", ConfigValueFactory.fromAnyRef(-1)) + )); } @Test diff --git a/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java b/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java index 7ecdfbcb2ac..0c7644ebcde 100644 --- a/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java +++ b/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java @@ -3,10 +3,7 @@ import co.rsk.blockchain.utils.BlockGenerator; import co.rsk.test.builders.AccountBuilder; import co.rsk.test.builders.TransactionBuilder; -import org.ethereum.core.Account; -import org.ethereum.core.Block; -import org.ethereum.core.BlockHeader; -import org.ethereum.core.Transaction; +import org.ethereum.core.*; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -18,8 +15,7 @@ import static org.mockito.Mockito.*; class BodyResponseMessageTest { - @Test - void createMessage() { + BodyResponseMessage testCreateMessage(BlockHeaderExtension extension) { List transactions = new ArrayList<>(); for (int k = 1; k <= 10; k++) @@ -36,7 +32,7 @@ void createMessage() { parent = block; } - BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles, null); + BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles, extension); Assertions.assertEquals(100, message.getId()); @@ -52,6 +48,21 @@ void createMessage() { for (int k = 0; k < uncles.size(); k++) Assertions.assertArrayEquals(uncles.get(k).getFullEncoded(), message.getUncles().get(k).getFullEncoded()); + + return message; + } + @Test + void createMessage() { + BodyResponseMessage message = testCreateMessage(null); + Assertions.assertNull(message.getBlockHeaderExtension()); + } + + @Test + void createMessageWithExtension() { + Bloom bloom = new Bloom(); + BlockHeaderExtension extension = new BlockHeaderExtensionV1(bloom.getData()); + BodyResponseMessage message = testCreateMessage(extension); + Assertions.assertArrayEquals(extension.getEncoded(), message.getBlockHeaderExtension().getEncoded()); } private static Transaction createTransaction(int number) { diff --git a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java index 57fa3cb0389..5fa6e74d962 100644 --- a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java +++ b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java @@ -193,32 +193,53 @@ void encodeDecodeBlockResponseMessage() { Assertions.assertArrayEquals(block.getEncoded(), newmessage.getBlock().getEncoded()); } - @Test - void encodeDecodeBlockHeadersResponseMessage() { - BlockGenerator blockGenerator = new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)); - BlockFactory blockFactory = new BlockFactory(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)); - List headers = new ArrayList<>(); - - for (int k = 1; k <= 4; k++) - headers.add(blockGenerator.getBlock(k).getHeader()); - + private BlockHeadersResponseMessage testBlockHeadersResponseMessage(BlockFactory blockFactory, List headers) { BlockHeadersResponseMessage message = new BlockHeadersResponseMessage(100, headers); - byte[] encoded = message.getEncoded(); - Assertions.assertNotNull(encoded); + BlockHeadersResponseMessage result = (BlockHeadersResponseMessage) Message.create(blockFactory, encoded); - Message result = Message.create(blockFactory, encoded); + for (int k = 0; k < headers.size(); k++) { + result.getBlockHeaders().get(k).setExtension(headers.get(k).getExtension()); // identity applying on block header v0 + } + Assertions.assertNotNull(encoded); Assertions.assertNotNull(result); Assertions.assertArrayEquals(encoded, result.getEncoded()); Assertions.assertEquals(MessageType.BLOCK_HEADERS_RESPONSE_MESSAGE, result.getMessageType()); - BlockHeadersResponseMessage newmessage = (BlockHeadersResponseMessage) result; + Assertions.assertEquals(100, message.getId()); + Assertions.assertEquals(headers.size(), message.getBlockHeaders().size()); - Assertions.assertEquals(100, newmessage.getId()); + return result; + } - Assertions.assertEquals(headers.size(), newmessage.getBlockHeaders().size()); + @Test + void encodeDecodeBlockHeadersResponseMessageWithoutRSKIP351() { + BlockGenerator blockGenerator = new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)); + BlockFactory blockFactory = new BlockFactory(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)); + + List headers = new ArrayList<>(); + for (int k = 1; k <= 4; k++) + headers.add(blockGenerator.getBlock(k).getHeader()); + + BlockHeadersResponseMessage newmessage = testBlockHeadersResponseMessage(blockFactory, headers); + + for (int k = 0; k < headers.size(); k++) { + Assertions.assertEquals(headers.get(k).getNumber(), newmessage.getBlockHeaders().get(k).getNumber()); + Assertions.assertEquals(headers.get(k).getHash(), newmessage.getBlockHeaders().get(k).getHash()); + Assertions.assertArrayEquals(headers.get(k).getFullEncoded(), newmessage.getBlockHeaders().get(k).getFullEncoded()); + } + } + + @Test + void encodeDecodeBlockHeadersResponseMessage() { + List headers = new ArrayList<>(); + + for (int k = 1; k <= 4; k++) + headers.add(blockGenerator.getBlock(k).getHeader()); + + BlockHeadersResponseMessage newmessage = testBlockHeadersResponseMessage(blockFactory, headers); for (int k = 0; k < headers.size(); k++) { Assertions.assertEquals(headers.get(k).getNumber(), newmessage.getBlockHeaders().get(k).getNumber()); @@ -506,6 +527,58 @@ void encodeDecodeBodyResponseMessage() { Assertions.assertArrayEquals(uncles.get(k).getFullEncoded(), newmessage.getUncles().get(k).getFullEncoded()); } + @Test + void encodeDecodeBodyResponseMessageWithExtension() { + List transactions = new ArrayList<>(); + + for (int k = 1; k <= 10; k++) + transactions.add(createTransaction(k)); + + List uncles = new ArrayList<>(); + + BlockGenerator blockGenerator = this.blockGenerator; + Block parent = blockGenerator.getGenesisBlock(); + + for (int k = 1; k < 10; k++) { + Block block = blockGenerator.createChildBlock(parent); + uncles.add(block.getHeader()); + parent = block; + } + + byte[] bloom = new byte[]{ 1, 2, 3, 4 }; + BlockHeaderExtension extension = new BlockHeaderExtensionV1(bloom); + + BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles, extension); + + byte[] encoded = message.getEncoded(); + + Message result = Message.create(blockFactory, encoded); + + Assertions.assertNotNull(result); + Assertions.assertArrayEquals(encoded, result.getEncoded()); + Assertions.assertEquals(MessageType.BODY_RESPONSE_MESSAGE, result.getMessageType()); + + BodyResponseMessage newmessage = (BodyResponseMessage)result; + + Assertions.assertNotNull(newmessage); + + Assertions.assertEquals(100, newmessage.getId()); + + Assertions.assertNotNull(newmessage.getTransactions()); + Assertions.assertEquals(transactions.size(), newmessage.getTransactions().size()); + + Assertions.assertEquals(transactions, newmessage.getTransactions()); + + Assertions.assertNotNull(newmessage.getUncles()); + Assertions.assertEquals(uncles.size(), newmessage.getUncles().size()); + + for (int k = 0; k < uncles.size(); k++) + Assertions.assertArrayEquals(uncles.get(k).getFullEncoded(), newmessage.getUncles().get(k).getFullEncoded()); + + Assertions.assertNotNull(newmessage.getBlockHeaderExtension()); + Assertions.assertArrayEquals(extension.getEncoded(), newmessage.getBlockHeaderExtension().getEncoded()); + } + private static Transaction createTransaction(int number) { AccountBuilder acbuilder = new AccountBuilder(); acbuilder.name("sender" + number); diff --git a/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncStateTest.java b/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncStateTest.java index 98e91590dd4..997d1929873 100644 --- a/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncStateTest.java +++ b/rskj-core/src/test/java/co/rsk/net/sync/DownloadingBackwardsBodiesSyncStateTest.java @@ -228,6 +228,76 @@ void connectingUntilGenesis() { } } + @Test + void connectingWithBlockHeaderExtension() { + LinkedList toRequest = new LinkedList<>(); + LinkedList responses = new LinkedList<>(); + LinkedList expectedBlocks = new LinkedList<>(); + Function difficultyForBlockNumber = + (n) -> new BlockDifficulty(BigInteger.valueOf(n * (n + 1) / 2)); + + // This setup initializes responses and blocks so that the blocks have the same number and difficulty as + // their indexes and each one is the children of the previous block. + for (long i = 1; i <= 10; i++) { + BlockHeader headerToRequest = mock(BlockHeader.class); + Keccak256 headerHash = new Keccak256(ByteUtil.leftPadBytes(ByteUtil.longToBytes(i), 32)); + BlockHeaderExtension blockHeaderExtension = mock(BlockHeaderExtension.class); + + when(headerToRequest.getExtension()).thenReturn(blockHeaderExtension); + when(headerToRequest.getHash()).thenReturn(headerHash); + when(headerToRequest.getNumber()).thenReturn(i); + + toRequest.addFirst(headerToRequest); + + when(syncEventsHandler.sendBodyRequest(any(), eq(headerToRequest))).thenReturn(i); + + BodyResponseMessage response = new BodyResponseMessage(i, new LinkedList<>(), new LinkedList<>(), blockHeaderExtension); + responses.addFirst(response); + + Block block = mock(Block.class); + expectedBlocks.addFirst(block); + when(block.getNumber()).thenReturn(i); + when(block.getHash()).thenReturn(headerHash); + when(blockFactory.newBlock(headerToRequest, response.getTransactions(), response.getUncles())) + .thenReturn(block); + + when(block.isParentOf(any())).thenReturn(true); + when(blockStore.getTotalDifficultyForHash(headerHash.getBytes())) + .thenReturn(difficultyForBlockNumber.apply(i)); + when(block.getCumulativeDifficulty()).thenReturn(new BlockDifficulty(BigInteger.valueOf(i))); + + when(block.getHeader()).thenReturn(headerToRequest); + } + when(genesis.isParentOf(expectedBlocks.getLast())).thenReturn(true); + when(genesis.getCumulativeDifficulty()).thenReturn(new BlockDifficulty(BigInteger.valueOf(0L))); + + Keccak256 childHash = new Keccak256(ByteUtil.leftPadBytes(ByteUtil.intToBytes(11), 32)); + when(child.getHash()).thenReturn(childHash); + when(blockStore.getTotalDifficultyForHash(childHash.getBytes())) + .thenReturn(difficultyForBlockNumber.apply(11L)); + when(child.getCumulativeDifficulty()).thenReturn(new BlockDifficulty(BigInteger.valueOf(11L))); + when(child.getNumber()).thenReturn(11L); + + DownloadingBackwardsBodiesSyncState target = new DownloadingBackwardsBodiesSyncState( + syncConfiguration, + syncEventsHandler, + peersInformation, + genesis, + blockFactory, + blockStore, + child, + toRequest, + peer); + + while (!responses.isEmpty()) { + target.onEnter(); + BodyResponseMessage response = responses.pop(); + target.newBody(response, mock(Peer.class)); + Block block = expectedBlocks.pop(); + verify(block.getHeader()).setExtension(response.getBlockHeaderExtension()); + } + } + @Test void connecting_notGenesis() { LinkedList toRequest = new LinkedList<>(); diff --git a/rskj-core/src/test/java/co/rsk/test/World.java b/rskj-core/src/test/java/co/rsk/test/World.java index b23197c44ce..11b4b728723 100644 --- a/rskj-core/src/test/java/co/rsk/test/World.java +++ b/rskj-core/src/test/java/co/rsk/test/World.java @@ -80,6 +80,7 @@ public World() { this(new BlockChainBuilder()); } + public World(RskSystemProperties config) { this(new BlockChainBuilder().setConfig(config)); } @@ -88,6 +89,10 @@ public World(ReceiptStore receiptStore) { this(new BlockChainBuilder().setReceiptStore(receiptStore)); } + public World(ReceiptStore receiptStore, RskSystemProperties config) { + this(new BlockChainBuilder().setReceiptStore(receiptStore).setConfig(config)); + } + @VisibleForTesting public World(BlockChainBuilder blockChainBuilder) { this(blockChainBuilder.build(), blockChainBuilder.getBlockStore(), blockChainBuilder.getReceiptStore(), blockChainBuilder.getTrieStore(), blockChainBuilder.getRepository(), blockChainBuilder.getTransactionPool(), null, @@ -186,6 +191,8 @@ public BlockExecutor getBlockExecutor() { return this.blockExecutor; } + public RskSystemProperties getConfig() { return this.config; } + public StateRootHandler getStateRootHandler() { return this.stateRootHandler; } diff --git a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java index 778c538ce6f..5a4401f26f9 100644 --- a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java +++ b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java @@ -19,6 +19,7 @@ package co.rsk.test.builders; import co.rsk.blockchain.utils.BlockGenerator; +import co.rsk.config.RskSystemProperties; import co.rsk.config.TestSystemProperties; import co.rsk.core.TransactionExecutorFactory; import co.rsk.core.bc.BlockExecutor; @@ -64,7 +65,7 @@ public BlockBuilder(Blockchain blockChain, BridgeSupportFactory bridgeSupportFac } public BlockBuilder(Blockchain blockChain, BridgeSupportFactory bridgeSupportFactory, BlockStore blockStore) { - this(blockChain, bridgeSupportFactory, blockStore, new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))); + this(blockChain, bridgeSupportFactory, blockStore, new BlockGenerator()); } public BlockBuilder parent(Block parent) { @@ -107,10 +108,14 @@ public BlockBuilder trieStore(TrieStore store) { } public Block build() { + final TestSystemProperties config = new TestSystemProperties(); + return this.build(config); + } + + public Block build(final RskSystemProperties config) { Block block = blockGenerator.createChildBlock(parent, txs, uncles, difficulty, this.minGasPrice, gasLimit); if (blockChain != null) { - final TestSystemProperties config = new TestSystemProperties(); StateRootHandler stateRootHandler = new StateRootHandler(config.getActivationConfig(), new StateRootsStoreImpl(new HashMapDB())); BlockExecutor executor = new BlockExecutor( config.getActivationConfig(), diff --git a/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java b/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java index 60e55bf8a8f..02220bf906a 100644 --- a/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java +++ b/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java @@ -281,12 +281,20 @@ public Blockchain ofSize(int size) { return ofSize(size, false); } + public Blockchain ofSize(int size, BlockGenerator blockGenerator) { + return ofSize(size, false, Collections.emptyMap(), blockGenerator); + } + public Blockchain ofSize(int size, boolean mining) { return ofSize(size, mining, Collections.emptyMap()); } + public Blockchain ofSize(int size, boolean mining, Map accounts) { - BlockGenerator blockGenerator = new BlockGenerator(); + return ofSize(size, mining, accounts, new BlockGenerator()); + } + + public Blockchain ofSize(int size, boolean mining, Map accounts, BlockGenerator blockGenerator) { Genesis genesis = blockGenerator.getGenesisBlock(accounts); BlockChainImpl blockChain = setGenesis(genesis).build(); diff --git a/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java b/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java index 6536c1506a4..b757701fea0 100644 --- a/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java +++ b/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java @@ -18,6 +18,7 @@ package co.rsk.test.dsl; +import co.rsk.blockchain.utils.BlockGenerator; import co.rsk.config.TestSystemProperties; import co.rsk.core.Coin; import co.rsk.core.RskAddress; @@ -55,7 +56,8 @@ public class WorldDslProcessor { public WorldDslProcessor(World world) { this.world = world; BlockBuilder blockBuilder = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), - world.getBlockStore()).trieStore(world.getTrieStore()); + world.getBlockStore(), new BlockGenerator(world.getConfig().getNetworkConstants(), world.getConfig().getActivationConfig()) + ).trieStore(world.getTrieStore()); blockBuilder.parent(world.getBlockChain().getBestBlock()); this.blockBuilder = blockBuilder; } diff --git a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java index 0c686985145..4092196a548 100644 --- a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java @@ -416,14 +416,14 @@ public Stream provideArguments(ExtensionContext context) { } @Test - public void createsHeaderWithVersion0BeforeRskip351() { + public void createsHeaderWithVersion0WithNoRskip351() { // RSKIP351 = -1 BlockHeader header = new BlockHeaderBuilder(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)).build(); assertEquals((byte) 0x0, header.getVersion()); } @Test - public void createHeaderWithVersion0BeforeRskip351() { + public void createsHeaderWithVersion0BeforeRskip351() { // RSKIP351 > header number ActivationConfig activationConfig = Mockito.mock(ActivationConfig.class); when(activationConfig.getHeaderVersion(geq(2))).thenReturn((byte) 0x0); diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java index 21878db2045..401e93ec846 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java @@ -57,6 +57,7 @@ import co.rsk.test.builders.TransactionBuilder; import co.rsk.util.HexUtils; import co.rsk.util.TestContract; +import com.typesafe.config.ConfigValueFactory; import org.bouncycastle.util.encoders.Hex; import org.ethereum.TestUtils; import org.ethereum.config.Constants; @@ -1032,31 +1033,36 @@ void getTransactionCountByNonCanonicalBlockHash() { assertNonCanonicalBlockHash("0x1", chain.block, blockRef -> chain.web3.eth_getTransactionCount(chain.accountAddress, blockRef)); } - @Test - void getBlockByNumber() { - World world = new World(); + private BlockBuilder createBlockBuilder(World world) { + return new BlockBuilder( + world.getBlockChain(), + world.getBridgeSupportFactory(), + world.getBlockStore(), + new BlockGenerator(world.getConfig().getNetworkConstants(), world.getConfig().getActivationConfig())); + } + void testBlockByNumber(RskSystemProperties config) { + World world = new World(config); Web3Impl web3 = createWeb3(world); Block genesis = world.getBlockChain().getBestBlock(); - Block block1 = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), - world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(10).parent(genesis).build(); + Block block1 = createBlockBuilder(world) + .trieStore(world.getTrieStore()).difficulty(10).parent(genesis).build(config); + assertEquals(ImportResult.IMPORTED_BEST, world.getBlockChain().tryToConnect(block1)); - Block block1b = new BlockBuilder( - world.getBlockChain(), - world.getBridgeSupportFactory(), - world.getBlockStore(), - new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))) - .trieStore(world.getTrieStore()).difficulty(2).parent(genesis).build(); + + Block block1b = createBlockBuilder(world) + .trieStore(world.getTrieStore()).difficulty(2).parent(genesis).build(config); block1b.setBitcoinMergedMiningHeader(new byte[]{0x01}); - Block block2b = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), - world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(11).parent(block1b).build(); + + Block block2b = createBlockBuilder(world) + .trieStore(world.getTrieStore()).difficulty(11).parent(block1b).build(config); block2b.setBitcoinMergedMiningHeader(new byte[]{0x02}); + assertEquals(ImportResult.IMPORTED_NOT_BEST, world.getBlockChain().tryToConnect(block1b)); assertEquals(ImportResult.IMPORTED_BEST, world.getBlockChain().tryToConnect(block2b)); BlockResultDTO bresult = web3.eth_getBlockByNumber("0x1", false); - assertNotNull(bresult); String blockHash = "0x" + block1b.getHash(); @@ -1064,15 +1070,25 @@ void getBlockByNumber() { String bnOrId = "0x2"; bresult = web3.eth_getBlockByNumber("0x2", true); - assertNotNull(bresult); blockHash = "0x" + block2b.getHash(); assertEquals(blockHash, bresult.getHash()); String hexString = web3.rsk_getRawBlockHeaderByNumber(bnOrId).replace("0x", ""); - Keccak256 obtainedBlockHash = new Keccak256(HashUtil.keccak256(Hex.decode(hexString))); - assertEquals(blockHash, obtainedBlockHash.toJsonString()); + assertArrayEquals(block2b.getHeader().getEncoded(), Hex.decode(hexString)); + } + + @Test + void getBlockByNumberWithoutFingerroot500() { + testBlockByNumber(new TestSystemProperties(rawConfig -> + rawConfig.withValue("blockchain.config.hardforkActivationHeights.fingerroot500", ConfigValueFactory.fromAnyRef(2)) + )); + } + + @Test + void getBlockByNumber() { + testBlockByNumber(new TestSystemProperties()); } @Test @@ -1177,26 +1193,22 @@ void getBlockByNumberWhenNumberIsInvalidThrowsException() { Assertions.assertThrows(org.ethereum.rpc.exception.RskJsonRpcRequestException.class, () -> web3.eth_getBlockByNumber(bnOrId, false)); } - @Test - void getBlockByHash() { - World world = new World(); - + void testGetBlockByHash(RskSystemProperties config) { + World world = new World(config); Web3Impl web3 = createWeb3(world); Block genesis = world.getBlockChain().getBestBlock(); - Block block1 = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), - world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(10).parent(genesis).build(); + + Block block1 = createBlockBuilder(world).trieStore(world.getTrieStore()).difficulty(10).parent(genesis).build(); block1.setBitcoinMergedMiningHeader(new byte[]{0x01}); assertEquals(ImportResult.IMPORTED_BEST, world.getBlockChain().tryToConnect(block1)); - Block block1b = new BlockBuilder( - world.getBlockChain(), - world.getBridgeSupportFactory(), - world.getBlockStore(), - new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351))).trieStore(world.getTrieStore()).difficulty(block1.getDifficulty().asBigInteger().longValue() - 1).parent(genesis).build(); + + Block block1b = createBlockBuilder(world).trieStore(world.getTrieStore()).difficulty(block1.getDifficulty().asBigInteger().longValue() - 1).parent(genesis).build(); block1b.setBitcoinMergedMiningHeader(new byte[]{0x01}); - Block block2b = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), - world.getBlockStore()).trieStore(world.getTrieStore()).difficulty(2).parent(block1b).build(); + + Block block2b = createBlockBuilder(world).trieStore(world.getTrieStore()).difficulty(2).parent(block1b).build(); block2b.setBitcoinMergedMiningHeader(new byte[]{0x02}); + assertEquals(ImportResult.IMPORTED_NOT_BEST, world.getBlockChain().tryToConnect(block1b)); assertEquals(ImportResult.IMPORTED_BEST, world.getBlockChain().tryToConnect(block2b)); @@ -1219,8 +1231,7 @@ void getBlockByHash() { assertEquals(block1bHashString, bresult.getHash()); String hexString = web3.rsk_getRawBlockHeaderByHash(block1bHashString).replace("0x", ""); - Keccak256 blockHash = new Keccak256(HashUtil.keccak256(Hex.decode(hexString))); - assertEquals(blockHash.toJsonString(), block1bHashString); + assertArrayEquals(block1b.getHeader().getEncoded(), Hex.decode(hexString)); bresult = web3.eth_getBlockByHash(block2bHashString, true); @@ -1228,8 +1239,19 @@ void getBlockByHash() { assertEquals(block2bHashString, bresult.getHash()); hexString = web3.rsk_getRawBlockHeaderByHash(block2bHashString).replace("0x", ""); - blockHash = new Keccak256(HashUtil.keccak256(Hex.decode(hexString))); - assertEquals(blockHash.toJsonString(), block2bHashString); + assertArrayEquals(block2b.getHeader().getEncoded(), Hex.decode(hexString)); + } + + @Test + void getBlockByHashWithoutFingerroot500() { + testGetBlockByHash(new TestSystemProperties(rawConfig -> + rawConfig.withValue("blockchain.config.hardforkActivationHeights.fingerroot500", ConfigValueFactory.fromAnyRef(2)) + )); + } + + @Test + void getBlockByHash() { + testGetBlockByHash(new TestSystemProperties()); } @Test From 0d0e7d82e1eaed1b111d0b73ce8abb4a233c34c3 Mon Sep 17 00:00:00 2001 From: Ilan Date: Sun, 13 Nov 2022 21:45:08 -0300 Subject: [PATCH 07/89] Fix build --- rskj-core/src/test/java/co/rsk/test/World.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/test/World.java b/rskj-core/src/test/java/co/rsk/test/World.java index 39e15a2f8dd..a8b1ceb2e09 100644 --- a/rskj-core/src/test/java/co/rsk/test/World.java +++ b/rskj-core/src/test/java/co/rsk/test/World.java @@ -195,8 +195,6 @@ public BlockExecutor getBlockExecutor() { return this.blockExecutor; } - public RskSystemProperties getConfig() { return this.config; } - public StateRootHandler getStateRootHandler() { return this.stateRootHandler; } From febccd6a7c7128257f9db299096277c33a1a99e7 Mon Sep 17 00:00:00 2001 From: julianlen Date: Wed, 1 Jun 2022 12:46:04 -0300 Subject: [PATCH 08/89] Block header addition (#1790) * Added transaction execution lists edges to block header * Added parallel transaction execution (#1788) * Validates transaction execution lists edges (#1789) --- .../src/main/java/co/rsk/RskContext.java | 5 +- .../co/rsk/core/TransactionListExecutor.java | 159 +++++++++++ .../java/co/rsk/core/bc/BlockExecutor.java | 262 ++++++++++++++---- .../main/java/co/rsk/db/MutableTrieCache.java | 35 +-- .../java/co/rsk/mine/BlockToMineBuilder.java | 1 + .../main/java/co/rsk/util/FormatUtils.java | 4 +- .../ValidTxExecutionListsEdgesRule.java | 47 ++++ .../java/org/ethereum/config/Constants.java | 2 + .../blockchain/upgrades/ConsensusRule.java | 3 +- .../java/org/ethereum/core/BlockFactory.java | 39 +-- .../java/org/ethereum/core/BlockHeader.java | 19 +- .../org/ethereum/core/BlockHeaderBuilder.java | 32 ++- .../java/org/ethereum/core/GenesisHeader.java | 2 + .../vm/trace/ProgramTraceProcessor.java | 9 +- rskj-core/src/main/resources/expected.conf | 1 + rskj-core/src/main/resources/reference.conf | 1 + .../rsk/blockchain/utils/BlockGenerator.java | 16 +- .../java/co/rsk/core/BlockFactoryTest.java | 173 ++++++++---- .../java/co/rsk/core/BlockHeaderTest.java | 134 ++++++--- .../co/rsk/core/bc/BlockExecutorTest.java | 257 ++++++++++++++--- .../co/rsk/mine/BlockToMineBuilderTest.java | 2 +- .../java/co/rsk/remasc/RemascTestRunner.java | 2 +- .../EthSubscriptionNotificationTest.java | 4 +- .../ValidTxExecutionListsEdgesTest.java | 93 +++++++ .../src/test/java/org/ethereum/TestUtils.java | 19 ++ .../upgrades/ActivationConfigTest.java | 1 + .../ethereum/core/BlockHeaderBuilderTest.java | 100 ++++++- .../org/ethereum/rpc/Web3ImplLogsTest.java | 2 +- 28 files changed, 1190 insertions(+), 234 deletions(-) create mode 100644 rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java create mode 100644 rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java create mode 100644 rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionListsEdgesTest.java diff --git a/rskj-core/src/main/java/co/rsk/RskContext.java b/rskj-core/src/main/java/co/rsk/RskContext.java index dc4c49a1f7d..0961e6fcb57 100644 --- a/rskj-core/src/main/java/co/rsk/RskContext.java +++ b/rskj-core/src/main/java/co/rsk/RskContext.java @@ -1061,7 +1061,7 @@ public synchronized BlockValidationRule getBlockValidationRule() { rskSystemProperties.getActivationConfig(), rskSystemProperties.getNetworkConstants() ); - blockValidationRule = new BlockValidatorRule( + blockValidationRule = new BlockCompositeRule( new TxsMinGasPriceRule(), new BlockTxsMaxGasPriceRule(rskSystemProperties.getActivationConfig()), new BlockUnclesValidationRule( @@ -1088,7 +1088,8 @@ public synchronized BlockValidationRule getBlockValidationRule() { blockTimeStampValidationRule, new GasLimitRule(commonConstants.getMinGasLimit()), new ExtraDataRule(commonConstants.getMaximumExtraDataSize()), - getForkDetectionDataRule() + getForkDetectionDataRule(), + new ValidTxExecutionListsEdgesRule() ); } diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java new file mode 100644 index 00000000000..43048966c39 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -0,0 +1,159 @@ +package co.rsk.core; + +import co.rsk.crypto.Keccak256; +import org.ethereum.core.*; +import org.ethereum.vm.DataWord; +import org.ethereum.vm.program.ProgramResult; +import org.ethereum.vm.trace.ProgramTraceProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.LongAccumulator; + +public class TransactionListExecutor implements Callable { + + private static final Logger logger = LoggerFactory.getLogger("transactionlistexecutor"); + + private final TransactionExecutorFactory transactionExecutorFactory; + private final List transactions; + private final Block block; + private final Repository track; + private final boolean vmTrace; + private final int vmTraceOptions; + private final Set deletedAccounts; + private final boolean discardInvalidTxs; + private final boolean acceptInvalidTransactions; + private final Map executedTransactions; + private final Map receipts; + private final Map transactionResults; + private final ProgramTraceProcessor programTraceProcessor; + private final LongAccumulator accumulatedFees; + private final LongAccumulator accumulatedGas; + + private int i; + private boolean registerProgramResults; + + public TransactionListExecutor( + List transactions, + Block block, + TransactionExecutorFactory transactionExecutorFactory, + Repository track, + boolean vmTrace, + int vmTraceOptions, + Set deletedAccounts, + boolean discardInvalidTxs, + boolean acceptInvalidTransactions, + Map receipts, + Map executedTransactions, + Map transactionResults, + boolean registerProgramResults, + @Nullable ProgramTraceProcessor programTraceProcessor, + LongAccumulator accumulatedFees, + LongAccumulator accumulatedGas, + int firstTxIndex) { + this.block = block; + this.transactionExecutorFactory = transactionExecutorFactory; + this.track = track; + this.vmTrace = vmTrace; + this.vmTraceOptions = vmTraceOptions; + this.transactions = transactions; + this.deletedAccounts = deletedAccounts; + this.discardInvalidTxs = discardInvalidTxs; + this.acceptInvalidTransactions = acceptInvalidTransactions; + this.executedTransactions = executedTransactions; + this.receipts = receipts; + this.registerProgramResults = registerProgramResults; + this.transactionResults = transactionResults; + this.programTraceProcessor = programTraceProcessor; + this.accumulatedFees = accumulatedFees; + this.accumulatedGas = accumulatedGas; + this.i = firstTxIndex; + } + + @Override + public Boolean call() { + long totalGasUsed = 0; + Coin totalPaidFees = Coin.ZERO; + + for (Transaction tx : transactions) { + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( + tx, + i, + block.getCoinbase(), + track, + block, + totalGasUsed, + vmTrace, + vmTraceOptions, + deletedAccounts + ); + boolean transactionExecuted = txExecutor.executeTransaction(); + + if (!acceptInvalidTransactions && !transactionExecuted) { + if (discardInvalidTxs) { + logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); + continue; + } else { + logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", + block.getNumber(), tx.getHash() + ); + return false; + } + } + + executedTransactions.put(i, tx); + + if (this.registerProgramResults) { + this.transactionResults.put(tx.getHash(), txExecutor.getResult()); + } + + if (vmTrace) { + txExecutor.extractTrace(programTraceProcessor); + } + + logger.trace("tx[{}] executed", i + 1); + + // No need to commit the changes here. track.commit(); + + logger.trace("track commit"); + + long txGasUsed = txExecutor.getGasUsed(); + totalGasUsed += txGasUsed; + + Coin txPaidFees = txExecutor.getPaidFees(); + if (txPaidFees != null) { + totalPaidFees = totalPaidFees.add(txPaidFees); + } + + deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); + + TransactionReceipt receipt = new TransactionReceipt(); + receipt.setGasUsed(txGasUsed); + receipt.setCumulativeGas(totalGasUsed); + + receipt.setTxStatus(txExecutor.getReceipt().isSuccessful()); + receipt.setTransaction(tx); + receipt.setLogInfoList(txExecutor.getVMLogs()); + receipt.setStatus(txExecutor.getReceipt().getStatus()); + + logger.trace("block: [{}] executed tx: [{}]", block.getNumber(), tx.getHash()); + + logger.trace("tx[{}].receipt", i + 1); + + i++; + + receipts.put(i, receipt); + + logger.trace("tx[{}] done", i); + } + accumulatedGas.accumulate(totalGasUsed); + accumulatedFees.accumulate(totalPaidFees.asBigInteger().longValue()); + + return true; + } +} diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 7f41ae18bd7..265202d9c66 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -21,6 +21,7 @@ import co.rsk.core.Coin; import co.rsk.core.RskAddress; import co.rsk.core.TransactionExecutorFactory; +import co.rsk.core.TransactionListExecutor; import co.rsk.crypto.Keccak256; import co.rsk.db.RepositoryLocator; import co.rsk.metrics.profilers.Metric; @@ -39,6 +40,8 @@ import javax.annotation.Nullable; import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.LongAccumulator; import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP126; import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP85; @@ -48,10 +51,12 @@ * There are two main use cases: * - execute and validate the block final state * - execute and complete the block final state - * + *

* Note that this class IS NOT guaranteed to be thread safe because its dependencies might hold state. */ public class BlockExecutor { + private static final int THREAD_COUNT = 4; + private static final Logger logger = LoggerFactory.getLogger("blockexecutor"); private static final Profiler profiler = ProfilerFactory.getInstance(); @@ -59,7 +64,7 @@ public class BlockExecutor { private final TransactionExecutorFactory transactionExecutorFactory; private final ActivationConfig activationConfig; - private final Map transactionResults = new HashMap<>(); + private final Map transactionResults = new ConcurrentHashMap<>(); private boolean registerProgramResults; public BlockExecutor( @@ -71,11 +76,47 @@ public BlockExecutor( this.activationConfig = activationConfig; } + /** + * Precompiled contracts storage is setup like any other contract for consistency. Here, we apply this logic on the + * exact activation block. + * This method is called automatically for every block except for the Genesis (which makes an explicit call). + */ + public static void maintainPrecompiledContractStorageRoots(Repository track, ActivationConfig.ForBlock activations) { + if (activations.isActivating(RSKIP126)) { + for (RskAddress addr : PrecompiledContracts.GENESIS_ADDRESSES) { + if (!track.isExist(addr)) { + track.createAccount(addr); + } + track.setupContract(addr); + } + } + + for (Map.Entry e : PrecompiledContracts.CONSENSUS_ENABLED_ADDRESSES.entrySet()) { + ConsensusRule contractActivationRule = e.getValue(); + if (activations.isActivating(contractActivationRule)) { + RskAddress addr = e.getKey(); + track.createAccount(addr); + track.setupContract(addr); + } + } + } + + @VisibleForTesting + public static byte[] calculateLogsBloom(List receipts) { + Bloom logBloom = new Bloom(); + + for (TransactionReceipt receipt : receipts) { + logBloom.or(receipt.getBloomFilter()); + } + + return logBloom.getData(); + } + /** * Execute and complete a block. * - * @param block A block to execute and complete - * @param parent The parent of the block. + * @param block A block to execute and complete + * @param parent The parent of the block. */ public BlockResult executeAndFill(Block block, BlockHeader parent) { BlockResult result = execute(block, parent, true, false, false); @@ -116,8 +157,8 @@ private void fill(Block block, BlockResult result) { /** * Execute and validate the final state of a block. * - * @param block A block to execute and complete - * @param parent The parent of the block. + * @param block A block to execute and complete + * @param parent The parent of the block. * @return true if the block final state is equalBytes to the calculated final state. */ @VisibleForTesting @@ -130,8 +171,8 @@ public boolean executeAndValidate(Block block, BlockHeader parent) { /** * Validate the final state of a block. * - * @param block A block to validate - * @param result A block result (state root, receipts root, etc...) + * @param block A block to validate + * @param result A block result (state root, receipts root, etc...) * @return true if the block final state is equalBytes to the calculated final state. */ public boolean validate(Block block, BlockResult result) { @@ -172,7 +213,7 @@ public boolean validate(Block block, BlockResult result) { Coin paidFees = result.getPaidFees(); Coin feesPaidToMiner = block.getFeesPaidToMiner(); - if (!paidFees.equals(feesPaidToMiner)) { + if (!paidFees.equals(feesPaidToMiner)) { logger.error("Block {} [{}] given paidFees doesn't match: {} != {}", block.getNumber(), block.getPrintableHash(), feesPaidToMiner, paidFees); profiler.stop(metric); return false; @@ -181,7 +222,7 @@ public boolean validate(Block block, BlockResult result) { List executedTransactions = result.getExecutedTransactions(); List transactionsList = block.getTransactionsList(); - if (!executedTransactions.equals(transactionsList)) { + if (!executedTransactions.equals(transactionsList)) { logger.error("Block {} [{}] given txs doesn't match: {} != {}", block.getNumber(), block.getPrintableHash(), transactionsList, executedTransactions); profiler.stop(metric); return false; @@ -249,6 +290,151 @@ private BlockResult executeInternal( boolean discardInvalidTxs, boolean acceptInvalidTransactions, boolean saveState) { + + if (block.getHeader().getTxExecutionListsEdges() != null) { + return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); + } else { + return executeSequential(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); + } + } + + private BlockResult executeParallel( + @Nullable ProgramTraceProcessor programTraceProcessor, + int vmTraceOptions, + Block block, + BlockHeader parent, + boolean discardInvalidTxs, + boolean acceptInvalidTransactions, + boolean saveState) { + boolean vmTrace = programTraceProcessor != null; + logger.trace("Start executeInternal."); + logger.trace("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), block.getTransactionsList().size()); + + // Forks the repo, does not change "repository". It will have a completely different + // image of the repo, where the middle caches are immediately ignored. + // In fact, while cloning everything, it asserts that no cache elements remains. + // (see assertNoCache()) + // Which means that you must commit changes and save them to be able to recover + // in the next block processed. + // Note that creating a snapshot is important when the block is executed twice + // (e.g. once while building the block in tests/mining, and the other when trying + // to conect the block). This is because the first execution will change the state + // of the repository to the state post execution, so it's necessary to get it to + // the state prior execution again. + Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE); + + Repository track = repositoryLocator.startTrackingAt(parent); + + maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); + + LongAccumulator totalGasUsed = new LongAccumulator(Long::sum, 0); + LongAccumulator totalPaidFees = new LongAccumulator(Long::sum, 0); + Map receipts = new ConcurrentSkipListMap<>(); + Map executedTransactions = new ConcurrentSkipListMap<>(); + Set deletedAccounts = ConcurrentHashMap.newKeySet(); + + ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); + CompletionService completionService = new ExecutorCompletionService(executorService); + int nTasks = 0; + + // execute parallel subsets of transactions + short start = 0; + for (short end : block.getHeader().getTxExecutionListsEdges()) { + List sublist = block.getTransactionsList().subList(start, end); + TransactionListExecutor txListExecutor = new TransactionListExecutor( + sublist, + block, + transactionExecutorFactory, + track, + vmTrace, + vmTraceOptions, + deletedAccounts, + discardInvalidTxs, + acceptInvalidTransactions, + receipts, + executedTransactions, + transactionResults, + registerProgramResults, + programTraceProcessor, + totalPaidFees, + totalGasUsed, + start + ); + completionService.submit(txListExecutor); + nTasks++; + start = end; + } + executorService.shutdown(); + + for (int i = 0; i < nTasks; i++) { + try { + Future success = completionService.take(); + if (!success.get()) { + executorService.shutdownNow(); + profiler.stop(metric); + return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + } + } catch (InterruptedException e) { + logger.warn("block: [{}] execution was interrupted", block.getNumber()); + logger.trace("", e); + profiler.stop(metric); + return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + } catch (ExecutionException e) { + logger.warn("block: [{}] execution failed", block.getNumber()); + logger.trace("", e); + profiler.stop(metric); + return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + } + } + + // execute remaining transactions after the parallel subsets + List sublist = block.getTransactionsList().subList(start, block.getTransactionsList().size()); + TransactionListExecutor txListExecutor = new TransactionListExecutor( + sublist, + block, + transactionExecutorFactory, + track, + vmTrace, + vmTraceOptions, + deletedAccounts, + discardInvalidTxs, + acceptInvalidTransactions, + receipts, + executedTransactions, + transactionResults, + registerProgramResults, + programTraceProcessor, + totalPaidFees, + totalGasUsed, + start + ); + Boolean success = txListExecutor.call(); + if (!success) { + return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + } + + saveOrCommitTrackState(saveState, track); + BlockResult result = new BlockResult( + block, + new LinkedList(executedTransactions.values()), + new LinkedList(receipts.values()), + totalGasUsed.longValue(), + Coin.valueOf(totalPaidFees.longValue()), + vmTrace ? null : track.getTrie() + ); + profiler.stop(metric); + logger.trace("End executeInternal."); + return result; + } + + private BlockResult executeSequential( + @Nullable ProgramTraceProcessor programTraceProcessor, + int vmTraceOptions, + Block block, + BlockHeader parent, + boolean discardInvalidTxs, + boolean acceptInvalidTransactions, + boolean saveState) { boolean vmTrace = programTraceProcessor != null; logger.trace("Start executeInternal."); logger.trace("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), block.getTransactionsList().size()); @@ -351,18 +537,7 @@ private BlockResult executeInternal( logger.trace("tx done"); } - logger.trace("End txs executions."); - if (saveState) { - logger.trace("Saving track."); - track.save(); - logger.trace("End saving track."); - } else { - logger.trace("Committing track."); - track.commit(); - logger.trace("End committing track."); - } - - logger.trace("Building execution results."); + saveOrCommitTrackState(saveState, track); BlockResult result = new BlockResult( block, executedTransactions, @@ -376,40 +551,19 @@ private BlockResult executeInternal( return result; } - /** - * Precompiled contracts storage is setup like any other contract for consistency. Here, we apply this logic on the - * exact activation block. - * This method is called automatically for every block except for the Genesis (which makes an explicit call). - */ - public static void maintainPrecompiledContractStorageRoots(Repository track, ActivationConfig.ForBlock activations) { - if (activations.isActivating(RSKIP126)) { - for (RskAddress addr : PrecompiledContracts.GENESIS_ADDRESSES) { - if (!track.isExist(addr)) { - track.createAccount(addr); - } - track.setupContract(addr); - } - } - - for (Map.Entry e : PrecompiledContracts.CONSENSUS_ENABLED_ADDRESSES.entrySet()) { - ConsensusRule contractActivationRule = e.getValue(); - if (activations.isActivating(contractActivationRule)) { - RskAddress addr = e.getKey(); - track.createAccount(addr); - track.setupContract(addr); - } - } - } - - @VisibleForTesting - public static byte[] calculateLogsBloom(List receipts) { - Bloom logBloom = new Bloom(); - - for (TransactionReceipt receipt : receipts) { - logBloom.or(receipt.getBloomFilter()); + private void saveOrCommitTrackState(boolean saveState, Repository track) { + logger.trace("End txs executions."); + if (saveState) { + logger.trace("Saving track."); + track.save(); + logger.trace("End saving track."); + } else { + logger.trace("Committing track."); + track.commit(); + logger.trace("End committing track."); } - return logBloom.getData(); + logger.trace("Building execution results."); } public ProgramResult getProgramResult(Keccak256 txhash) { diff --git a/rskj-core/src/main/java/co/rsk/db/MutableTrieCache.java b/rskj-core/src/main/java/co/rsk/db/MutableTrieCache.java index 997b07defd2..64f8bc729e9 100644 --- a/rskj-core/src/main/java/co/rsk/db/MutableTrieCache.java +++ b/rskj-core/src/main/java/co/rsk/db/MutableTrieCache.java @@ -31,6 +31,7 @@ import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; public class MutableTrieCache implements MutableTrie { @@ -40,14 +41,14 @@ public class MutableTrieCache implements MutableTrie { private MutableTrie trie; // We use a single cache to mark both changed elements and removed elements. // null value means the element has been removed. - private final Map> cache; + private final Map>> cache; // this logs recursive delete operations to be performed at commit time private final Set deleteRecursiveLog; public MutableTrieCache(MutableTrie parentTrie) { trie = parentTrie; - cache = new HashMap<>(); + cache = new ConcurrentHashMap<>(); deleteRecursiveLog = new HashSet<>(); } @@ -75,7 +76,7 @@ private Optional internalGet( ByteArrayWrapper wrapper = new ByteArrayWrapper(key); ByteArrayWrapper accountWrapper = getAccountWrapper(wrapper); - Map accountItems = cache.get(accountWrapper); + Map> accountItems = cache.get(accountWrapper); boolean isDeletedAccount = deleteRecursiveLog.contains(accountWrapper); if (accountItems == null || !accountItems.containsKey(wrapper)) { if (isDeletedAccount) { @@ -85,14 +86,14 @@ private Optional internalGet( return Optional.ofNullable(trieRetriever.apply(key)); } - byte[] cacheItem = accountItems.get(wrapper); - if (cacheItem == null) { + Optional cacheItem = accountItems.get(wrapper); + if (!cacheItem.isPresent()) { // deleted account key return Optional.empty(); } // cached account key - return Optional.ofNullable(cacheTransformer.apply(cacheItem)); + return Optional.ofNullable(cacheTransformer.apply(cacheItem.get())); } public Iterator getStorageKeys(RskAddress addr) { @@ -100,7 +101,7 @@ public Iterator getStorageKeys(RskAddress addr) { ByteArrayWrapper accountWrapper = getAccountWrapper(new ByteArrayWrapper(accountStoragePrefixKey)); boolean isDeletedAccount = deleteRecursiveLog.contains(accountWrapper); - Map accountItems = cache.get(accountWrapper); + Map> accountItems = cache.get(accountWrapper); if (accountItems == null && isDeletedAccount) { return Collections.emptyIterator(); } @@ -139,8 +140,8 @@ public void put(ByteArrayWrapper wrapper, byte[] value) { // in cache with null or in deleteCache. Here we have the choice to // to add it to cache with null value or to deleteCache. ByteArrayWrapper accountWrapper = getAccountWrapper(wrapper); - Map accountMap = cache.computeIfAbsent(accountWrapper, k -> new HashMap<>()); - accountMap.put(wrapper, value); + Map> accountMap = cache.computeIfAbsent(accountWrapper, k -> new ConcurrentHashMap<>()); + accountMap.put(wrapper, Optional.ofNullable(value)); } @Override @@ -179,7 +180,7 @@ public void commit() { cache.forEach((accountKey, accountData) -> { if (accountData != null) { // cached account - accountData.forEach((realKey, value) -> this.trie.put(realKey, value)); + accountData.forEach((realKey, value) -> this.trie.put(realKey, value.orElse(null))); } }); @@ -242,7 +243,7 @@ public Optional getValueHash(byte[] key) { private static class StorageKeysIterator implements Iterator { private final Iterator keysIterator; - private final Map accountItems; + private final Map> accountItems; private final RskAddress address; private final int storageKeyOffset = ( TrieKeyMapper.domainPrefix().length + @@ -252,11 +253,11 @@ private static class StorageKeysIterator implements Iterator { * Byte.SIZE; private final TrieKeyMapper trieKeyMapper; private DataWord currentStorageKey; - private Iterator> accountIterator; + private Iterator>> accountIterator; StorageKeysIterator( Iterator keysIterator, - Map accountItems, + Map> accountItems, RskAddress addr, TrieKeyMapper trieKeyMapper) { this.keysIterator = keysIterator; @@ -275,8 +276,8 @@ public boolean hasNext() { DataWord item = keysIterator.next(); ByteArrayWrapper fullKey = getCompleteKey(item); if (accountItems.containsKey(fullKey)) { - byte[] value = accountItems.remove(fullKey); - if (value == null){ + Optional value = accountItems.remove(fullKey); + if (!value.isPresent()){ continue; } } @@ -289,9 +290,9 @@ public boolean hasNext() { } while (accountIterator.hasNext()) { - Map.Entry entry = accountIterator.next(); + Map.Entry> entry = accountIterator.next(); byte[] key = entry.getKey().getData(); - if (entry.getValue() != null && key.length * Byte.SIZE > storageKeyOffset) { + if (entry.getValue().isPresent() && key.length * Byte.SIZE > storageKeyOffset) { // cached account key currentStorageKey = getPartialKey(key); return true; diff --git a/rskj-core/src/main/java/co/rsk/mine/BlockToMineBuilder.java b/rskj-core/src/main/java/co/rsk/mine/BlockToMineBuilder.java index d887562334a..c549b3ccd48 100644 --- a/rskj-core/src/main/java/co/rsk/mine/BlockToMineBuilder.java +++ b/rskj-core/src/main/java/co/rsk/mine/BlockToMineBuilder.java @@ -245,6 +245,7 @@ private BlockHeader createHeader( .setMinimumGasPrice(minimumGasPrice) .setUncleCount(uncles.size()) .setUmmRoot(ummRoot) + .setCreateParallelCompliantHeader(activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)) .build(); newHeader.setDifficulty(difficultyCalculator.calcDifficulty(newHeader, newBlockParentHeader)); diff --git a/rskj-core/src/main/java/co/rsk/util/FormatUtils.java b/rskj-core/src/main/java/co/rsk/util/FormatUtils.java index 9defca95b79..fe1f7575ccd 100644 --- a/rskj-core/src/main/java/co/rsk/util/FormatUtils.java +++ b/rskj-core/src/main/java/co/rsk/util/FormatUtils.java @@ -19,12 +19,14 @@ package co.rsk.util; +import java.util.Locale; + public class FormatUtils { private FormatUtils() { } public static String formatNanosecondsToSeconds(long nanoseconds) { - return String.format("%.6f", nanoseconds / 1_000_000_000.0); + return String.format(Locale.ROOT, "%.6f", nanoseconds / 1_000_000_000.0); } } diff --git a/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java new file mode 100644 index 00000000000..9885dbdfeb1 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java @@ -0,0 +1,47 @@ +package co.rsk.validators; + +import org.ethereum.config.Constants; +import org.ethereum.core.Block; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/* + Validates that: + - There are no more defined transaction subsets than the max number of execution threads + - All the edges are within the range of the list of transactions + - The edges do not define any empty subset + - The edges are in ascending order + */ +public class ValidTxExecutionListsEdgesRule implements BlockValidationRule { + + private static final Logger logger = LoggerFactory.getLogger("blockvalidator"); + + @Override + public boolean isValid(Block block) { + short[] edges = block.getHeader().getTxExecutionListsEdges(); + int nTxs = block.getTransactionsList().size(); + + if (edges == null) { + return true; + } + if (edges.length > Constants.getMaxTransactionExecutionThreads()) { + logger.warn("Invalid block: number of execution lists edges is greater than number of execution threads ({} vs {})", + edges.length, Constants.getMaxTransactionExecutionThreads()); + return false; + } + short prev = 0; + + for (short edge : edges) { + if (edge <= prev) { + logger.warn("Invalid block: execution lists edges are not in ascending order"); + return false; + } + if (edge > nTxs) { + logger.warn("Invalid block: execution list edge is out of bounds: {}", edge); + return false; + } + prev = edge; + } + return true; + } +} diff --git a/rskj-core/src/main/java/org/ethereum/config/Constants.java b/rskj-core/src/main/java/org/ethereum/config/Constants.java index dd4fcab9bf3..63ad2f55705 100644 --- a/rskj-core/src/main/java/org/ethereum/config/Constants.java +++ b/rskj-core/src/main/java/org/ethereum/config/Constants.java @@ -225,6 +225,8 @@ public static int getMaxBitcoinMergedMiningMerkleProofLength() { return 960; } + public static int getMaxTransactionExecutionThreads() { return 4; } + public static Constants mainnet() { return new Constants( MAINNET_CHAIN_ID, diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java index 50b77cbcea0..1b7180ec72b 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ConsensusRule.java @@ -76,7 +76,8 @@ public enum ConsensusRule { RSKIP290("rskip290"), // Testnet difficulty should drop to a higher difficulty RSKIP293("rskip293"), // Flyover improvements RSKIP294("rskip294"), - RSKIP297("rskip297"); // Increase max timestamp difference between btc and rsk blocks for Testnet + RSKIP297("rskip297"), // Increase max timestamp difference between btc and rsk blocks for Testnet + RSKIP144("rskip144"); // Parallel tx execution private String configKey; diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java index f6e6ae3eb5e..e7b6aef4bbd 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java @@ -31,6 +31,8 @@ import org.ethereum.util.RLPList; import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -38,8 +40,8 @@ import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH; public class BlockFactory { - private static final int RLP_HEADER_SIZE = 17; - private static final int RLP_HEADER_SIZE_WITH_MERGED_MINING = 20; + private static final int RLP_HEADER_SIZE = 18; + private static final int RLP_HEADER_SIZE_WITH_MERGED_MINING = 21; private final ActivationConfig activationConfig; @@ -138,30 +140,28 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { if (!canBeDecoded(rlpHeader, blockNumber)) { throw new IllegalArgumentException(String.format( - "A block header must have 16/17 elements or 19/20 including merged-mining fields but it had %d", + "Invalid block header size: %d", rlpHeader.size() )); } int r = 15; - boolean isUmm = activationConfig.isActive(ConsensusRule.RSKIPUMM, blockNumber); - - boolean includeUncleCount = isUmm || - // sizes prior to UMM activation - rlpHeader.size() == (RLP_HEADER_SIZE-1) || rlpHeader.size() == (RLP_HEADER_SIZE_WITH_MERGED_MINING-1); - - int uncleCount = 0; - if (includeUncleCount) { - byte[] ucBytes = rlpHeader.get(r++).getRLPData(); - uncleCount = parseBigInteger(ucBytes).intValueExact(); - } + byte[] ucBytes = rlpHeader.get(r++).getRLPData(); + int uncleCount = parseBigInteger(ucBytes).intValueExact(); byte[] ummRoot = null; - if (isUmm) { + if (activationConfig.isActive(ConsensusRule.RSKIPUMM, blockNumber)) { ummRoot = rlpHeader.get(r++).getRLPRawData(); } + short[] txExecutionListsEdges = null; + if (activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)) { + byte[] edgesBytes = rlpHeader.get(r++).getRLPRawData(); + txExecutionListsEdges = new short[edgesBytes.length / 2]; + ByteBuffer.wrap(edgesBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(txExecutionListsEdges); + } + byte[] bitcoinMergedMiningHeader = null; byte[] bitcoinMergedMiningMerkleProof = null; byte[] bitcoinMergedMiningCoinbaseTransaction = null; @@ -202,15 +202,16 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, new byte[0], minimumGasPrice, uncleCount, sealed, useRskip92Encoding, includeForkDetectionData, - ummRoot + ummRoot, txExecutionListsEdges ); } private boolean canBeDecoded(RLPList rlpHeader, long blockNumber) { int preUmmHeaderSizeAdjustment = activationConfig.isActive(ConsensusRule.RSKIPUMM, blockNumber) ? 0 : 1; - - return rlpHeader.size() == (RLP_HEADER_SIZE - preUmmHeaderSizeAdjustment) || - rlpHeader.size() == (RLP_HEADER_SIZE_WITH_MERGED_MINING - preUmmHeaderSizeAdjustment); + int preParallelSizeAdjustment = activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber) ? 0 : 1; + int expectedSize = RLP_HEADER_SIZE - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment; + int expectedSizeMM = RLP_HEADER_SIZE_WITH_MERGED_MINING - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment; + return rlpHeader.size() == expectedSize || rlpHeader.size() == expectedSizeMM; } private static BigInteger parseBigInteger(byte[] bytes) { diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index 7bbf0c7434a..2886c666894 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -32,6 +32,8 @@ import javax.annotation.Nullable; import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.Arrays; import java.util.List; @@ -103,6 +105,9 @@ public class BlockHeader { private byte[] miningForkDetectionData; + /* Edges of the transaction execution lists */ + private short[] txExecutionListsEdges; + private final byte[] ummRoot; /** @@ -129,7 +134,8 @@ public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, by Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, Coin minimumGasPrice, int uncleCount, boolean sealed, - boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot) { + boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot, + short[] txExecutionListsEdges) { this.parentHash = parentHash; this.unclesHash = unclesHash; this.coinbase = coinbase; @@ -155,6 +161,7 @@ public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, by this.useRskip92Encoding = useRskip92Encoding; this.includeForkDetectionData = includeForkDetectionData; this.ummRoot = ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; + this.txExecutionListsEdges = txExecutionListsEdges; } @VisibleForTesting @@ -371,6 +378,12 @@ public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProof fieldToEncodeList.add(RLP.encodeElement(this.ummRoot)); } + if (this.txExecutionListsEdges != null) { + byte[] edgesBytes = new byte[this.txExecutionListsEdges.length * 2]; + ByteBuffer.wrap(edgesBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(this.txExecutionListsEdges); + fieldToEncodeList.add(RLP.encodeElement(edgesBytes)); + } + if (withMergedMiningFields && hasMiningFields()) { byte[] bitcoinMergedMiningHeader = RLP.encodeElement(this.bitcoinMergedMiningHeader); fieldToEncodeList.add(bitcoinMergedMiningHeader); @@ -381,7 +394,6 @@ public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProof fieldToEncodeList.add(bitcoinMergedMiningCoinbaseTransaction); } } - return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); } @@ -448,6 +460,7 @@ private String toStringWithSuffix(final String suffix) { toStringBuff.append(" timestamp=").append(timestamp).append(" (").append(Utils.longToDateTime(timestamp)).append(")").append(suffix); toStringBuff.append(" extraData=").append(toHexStringOrEmpty(extraData)).append(suffix); toStringBuff.append(" minGasPrice=").append(minimumGasPrice).append(suffix); + toStringBuff.append(" txExecutionListsEdges=").append(txExecutionListsEdges).append(suffix); return toStringBuff.toString(); } @@ -612,4 +625,6 @@ public boolean isParentOf(BlockHeader header) { public byte[] getUmmRoot() { return ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; } + + public short[] getTxExecutionListsEdges() { return this.txExecutionListsEdges; } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java index ac62a6a600f..79f8d1dde31 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java @@ -57,6 +57,7 @@ public class BlockHeaderBuilder { private byte[] bitcoinMergedMiningCoinbaseTransaction; private byte[] mergedMiningForkDetectionData; private byte[] ummRoot; + private short[] txExecutionListsEdges; private Coin minimumGasPrice; private int uncleCount; @@ -68,11 +69,13 @@ public class BlockHeaderBuilder { private boolean createConsensusCompliantHeader; private boolean createUmmCompliantHeader; + private boolean createParallelCompliantHeader; public BlockHeaderBuilder(ActivationConfig activationConfig) { this.activationConfig = activationConfig; createConsensusCompliantHeader = true; createUmmCompliantHeader = true; + createParallelCompliantHeader = true; } public BlockHeaderBuilder setCreateConsensusCompliantHeader(boolean createConsensusCompliantHeader) { @@ -85,6 +88,15 @@ public BlockHeaderBuilder setCreateUmmCompliantHeader(boolean createUmmCompliant return this; } + public BlockHeaderBuilder setCreateParallelCompliantHeader(boolean createParallelCompliantHeader) { + this.createParallelCompliantHeader = createParallelCompliantHeader; + + if (!createParallelCompliantHeader) { + this.txExecutionListsEdges = null; + } + return this; + } + public BlockHeaderBuilder setStateRoot(byte[] stateRoot) { this.stateRoot = copy(stateRoot); return this; @@ -252,6 +264,18 @@ public BlockHeaderBuilder setUmmRoot(byte[] ummRoot) { return this; } + public BlockHeaderBuilder setTxExecutionListsEdges(short[] edges) { + if (edges != null) { + this.txExecutionListsEdges = new short[edges.length]; + System.arraycopy(edges, 0, this.txExecutionListsEdges, 0, edges.length); + this.createParallelCompliantHeader = true; + } else { + this.txExecutionListsEdges = null; + this.createParallelCompliantHeader = false; + } + return this; + } + private void initializeWithDefaultValues() { extraData = normalizeValue(extraData, new byte[0]); bitcoinMergedMiningHeader = normalizeValue(bitcoinMergedMiningHeader, new byte[0]); @@ -309,6 +333,12 @@ public BlockHeader build() { } } + if (createParallelCompliantHeader) { + if (txExecutionListsEdges == null) { + txExecutionListsEdges = new short[0]; + } + } + return new BlockHeader( parentHash, unclesHash, coinbase, stateRoot, txTrieRoot, receiptTrieRoot, @@ -320,7 +350,7 @@ public BlockHeader build() { mergedMiningForkDetectionData, minimumGasPrice, uncleCount, false, useRskip92Encoding, - includeForkDetectionData, ummRoot + includeForkDetectionData, ummRoot, txExecutionListsEdges ); } } \ No newline at end of file diff --git a/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java b/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java index 1419ff2c978..d732030bc44 100644 --- a/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/GenesisHeader.java @@ -52,6 +52,7 @@ public GenesisHeader(byte[] parentHash, false, useRskip92Encoding, false, + null, null); this.difficulty = ByteUtils.clone(difficulty); } @@ -95,6 +96,7 @@ public GenesisHeader(byte[] parentHash, false, useRskip92Encoding, false, + null, null); this.difficulty = ByteUtils.clone(difficulty); } diff --git a/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java b/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java index 747b820de90..5b46c548162 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java +++ b/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java @@ -27,15 +27,18 @@ import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * Provides tracing and exporting to JSON */ public class ProgramTraceProcessor { - - private final Map traces = new HashMap<>(); + private final Map traces = new ConcurrentHashMap<>(); private final TraceOptions traceOptions; diff --git a/rskj-core/src/main/resources/expected.conf b/rskj-core/src/main/resources/expected.conf index c8a53424d34..eb2c4194be3 100644 --- a/rskj-core/src/main/resources/expected.conf +++ b/rskj-core/src/main/resources/expected.conf @@ -74,6 +74,7 @@ blockchain = { rskip293 = rskip294 = rskip297 = + rskip144 = } } gc = { diff --git a/rskj-core/src/main/resources/reference.conf b/rskj-core/src/main/resources/reference.conf index 89df2ad25bd..dbd94947f36 100644 --- a/rskj-core/src/main/resources/reference.conf +++ b/rskj-core/src/main/resources/reference.conf @@ -63,6 +63,7 @@ blockchain = { rskip293 = hop400 rskip294 = hop400 rskip297 = hop400 + rskip144 = hop400 } } gc = { diff --git a/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java b/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java index 01680b8a4c7..fc37e8f97e5 100644 --- a/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java +++ b/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java @@ -32,8 +32,8 @@ import org.bouncycastle.util.encoders.Hex; import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; -import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; import org.ethereum.crypto.HashUtil; import org.ethereum.util.ByteUtil; @@ -45,7 +45,6 @@ import java.util.*; import static org.ethereum.core.Genesis.getZeroHash; -import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH; /** * Created by ajlopez on 5/10/2016. @@ -171,6 +170,7 @@ public Block createChildBlock(Block parent, long fees, List uncles, .setEmptyMergedMiningForkDetectionData() .setUncleCount(uncles.size()) .setUmmRoot(ummRoot) + .setCreateParallelCompliantHeader(activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)) .build(); return blockFactory.newBlock( @@ -207,6 +207,7 @@ public Block createChildBlock(Block parent, List txs, byte[] stateR .setTimestamp(parent.getTimestamp() + ++count) .setEmptyMergedMiningForkDetectionData() .setUmmRoot(ummRoot) + .setCreateParallelCompliantHeader(activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)) .build(); return blockFactory.newBlock( @@ -242,9 +243,15 @@ public Block createChildBlock(Block parent, List txs, List txs, List uncles, + long difficulty, BigInteger minGasPrice, byte[] gasLimit, RskAddress coinbase) { + short[] edges = activationConfig.isActive(ConsensusRule.RSKIP144, parent.getNumber() + 1) ? new short[0] : null; + return createChildBlock(parent, txs, uncles, difficulty, minGasPrice, gasLimit, coinbase, edges); + } public Block createChildBlock(Block parent, List txs, List uncles, - long difficulty, BigInteger minGasPrice, byte[] gasLimit, RskAddress coinbase) { + long difficulty, BigInteger minGasPrice, byte[] gasLimit, RskAddress coinbase, short[] edges) { if (txs == null) { txs = new ArrayList<>(); } @@ -278,6 +285,7 @@ public Block createChildBlock(Block parent, List txs, List factory.decodeHeader(encodedHeader)); } @@ -244,16 +243,16 @@ void decodeBlockRskip110OffRskipUMMOnAndMergedMiningFieldsValidUMMRoot() { enableRulesAt(number, RSKIP92, RSKIPUMM); byte[] ummRoot = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; - BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], ummRoot); + BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], ummRoot, null); byte[] encodedHeader = header.getFullEncoded(); RLPList headerRLP = RLP.decodeList(encodedHeader); - MatcherAssert.assertThat(headerRLP.size(), is(20)); + assertThat(headerRLP.size(), is(20)); BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); - MatcherAssert.assertThat(header.getHash(), is(decodedHeader.getHash())); - MatcherAssert.assertThat(header.getUmmRoot(), is(decodedHeader.getUmmRoot())); + assertThat(header.getHash(), is(decodedHeader.getHash())); + assertThat(header.getUmmRoot(), is(decodedHeader.getUmmRoot())); } @Test @@ -261,16 +260,16 @@ void decodeBlockRskip110OffRskipUMMOnAndMergedMiningFieldsEmptyUmmRoot() { long number = 500L; enableRulesAt(number, RSKIP92, RSKIPUMM); - BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], new byte[0]); + BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], new byte[0], null); byte[] encodedHeader = header.getFullEncoded(); RLPList headerRLP = RLP.decodeList(encodedHeader); - MatcherAssert.assertThat(headerRLP.size(), is(20)); + assertThat(headerRLP.size(), is(20)); BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); - MatcherAssert.assertThat(header.getHash(), is(decodedHeader.getHash())); - MatcherAssert.assertThat(header.getUmmRoot(), is(decodedHeader.getUmmRoot())); + assertThat(header.getHash(), is(decodedHeader.getHash())); + assertThat(header.getUmmRoot(), is(decodedHeader.getUmmRoot())); } @Test @@ -280,15 +279,87 @@ void decodeBlockRskip110OffRskipUMMOnAndMergedMiningFieldsNullUmmRoot() { // this should not be possible after the activation of UMM // blocks are expected to have an empty byte array - BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], null); + BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], null, null); byte[] encodedHeader = header.getFullEncoded(); RLPList headerRLP = RLP.decodeList(encodedHeader); - MatcherAssert.assertThat(headerRLP.size(), is(19)); + assertThat(headerRLP.size(), is(19)); Assertions.assertThrows(IllegalArgumentException.class, () -> factory.decodeHeader(encodedHeader)); } + @Test + public void decodeBlockRskip144OnRskipUMMOnAndMergedMiningFields() { + long number = 500L; + enableRulesAt(number, RSKIPUMM, RSKIP144); + short[] edges = TestUtils.randomShortArray(4); + + BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], new byte[0], edges); + + byte[] encodedHeader = header.getEncoded(); + RLPList headerRLP = RLP.decodeList(encodedHeader); + assertThat(headerRLP.size(), is(21)); + + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + + assertThat(header.getHash(), is(decodedHeader.getHash())); + assertThat(header.getTxExecutionListsEdges(), is(edges)); + } + + @Test + public void decodeBlockRskip144OnRskipUMMOnAndNoMergedMiningFields() { + long number = 500L; + enableRulesAt(number, RSKIPUMM, RSKIP144); + short[] edges = TestUtils.randomShortArray(4); + + BlockHeader header = createBlockHeader(number, new byte[0], new byte[0], edges); + + byte[] encodedHeader = header.getEncoded(); + RLPList headerRLP = RLP.decodeList(encodedHeader); + assertThat(headerRLP.size(), is(18)); + + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + + assertThat(header.getHash(), is(decodedHeader.getHash())); + assertThat(header.getTxExecutionListsEdges(), is(edges)); + } + + @Test + public void decodeBlockRskip144OnRskipUMMOffAndMergedMiningFields() { + long number = 500L; + enableRulesAt(number, RSKIP144); + short[] edges = TestUtils.randomShortArray(4); + + BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], null, edges); + + byte[] encodedHeader = header.getEncoded(); + RLPList headerRLP = RLP.decodeList(encodedHeader); + assertThat(headerRLP.size(), is(20)); + + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + + assertThat(header.getHash(), is(decodedHeader.getHash())); + assertThat(header.getTxExecutionListsEdges(), is(edges)); + } + + @Test + public void decodeBlockRskip144OnRskipUMMOffAndNoMergedMiningFields() { + long number = 500L; + enableRulesAt(number, RSKIP144); + short[] edges = TestUtils.randomShortArray(4); + + BlockHeader header = createBlockHeader(number, new byte[0], null, edges); + + byte[] encodedHeader = header.getEncoded(); + RLPList headerRLP = RLP.decodeList(encodedHeader); + assertThat(headerRLP.size(), is(17)); + + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + + assertThat(header.getHash(), is(decodedHeader.getHash())); + assertThat(header.getTxExecutionListsEdges(), is(edges)); + } + private void enableRulesAt(long number, ConsensusRule... consensusRules) { for (ConsensusRule consensusRule : consensusRules) { when(activationConfig.isActive(eq(consensusRule), geq(number))).thenReturn(true); @@ -298,7 +369,8 @@ private void enableRulesAt(long number, ConsensusRule... consensusRules) { private BlockHeader createBlockHeaderWithMergedMiningFields( long number, byte[] forkDetectionData, - byte[] ummRoot) { + byte[] ummRoot, + short[] edges) { byte[] difficulty = BigInteger.ONE.toByteArray(); byte[] gasLimit = BigInteger.valueOf(6800000).toByteArray(); long timestamp = 7731067; // Friday, 10 May 2019 6:04:05 @@ -325,13 +397,15 @@ private BlockHeader createBlockHeaderWithMergedMiningFields( .setUncleCount(0) .setCreateUmmCompliantHeader(ummRoot != null) .setUmmRoot(ummRoot) + .setTxExecutionListsEdges(edges) .build(); } private BlockHeader createBlockHeader( long number, byte[] forkDetectionData, - byte[] ummRoot) { + byte[] ummRoot, + short[] edges) { byte[] difficulty = BigInteger.ONE.toByteArray(); byte[] gasLimit = BigInteger.valueOf(6800000).toByteArray(); long timestamp = 7731067; // Friday, 10 May 2019 6:04:05 @@ -355,6 +429,7 @@ private BlockHeader createBlockHeader( .setUncleCount(0) .setCreateUmmCompliantHeader(ummRoot != null) .setUmmRoot(ummRoot) + .setTxExecutionListsEdges(edges) .build(); } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java index c519d17172b..06e84a7de50 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java @@ -34,7 +34,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; import org.mockito.MockedStatic; -import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.jupiter.MockitoExtension; import java.math.BigInteger; @@ -52,7 +51,7 @@ class BlockHeaderTest { @Test void getHashForMergedMiningWithForkDetectionDataAndIncludedOnAndMergedMiningFields() { - BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], true, new byte[0]); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], true, new byte[0], new short[0]); byte[] encodedBlock = header.getEncoded(false, false); byte[] hashForMergedMiningPrefix = Arrays.copyOfRange(HashUtil.keccak256(encodedBlock), 0, 20); @@ -69,7 +68,7 @@ void getHashForMergedMiningWithForkDetectionDataAndIncludedOnAndMergedMiningFiel @Test void getHashForMergedMiningWithNoForkDetectionDataAndIncludedOffAndMergedMiningFields() { - BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0]); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0], new short[0]); Keccak256 hash = header.getHash(); byte[] hashForMergedMining = header.getHashForMergedMining(); @@ -79,8 +78,8 @@ void getHashForMergedMiningWithNoForkDetectionDataAndIncludedOffAndMergedMiningF @Test void getHashForMergedMiningWithForkDetectionDataAndIncludedOn() { - byte[] forkDetectionData = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - BlockHeader header = createBlockHeaderWithNoMergedMiningFields(forkDetectionData, true, new byte[0]); + byte[] forkDetectionData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + BlockHeader header = createBlockHeaderWithNoMergedMiningFields(forkDetectionData, true, new byte[0], new short[0]); byte[] hash = header.getHash().getBytes(); byte[] hashFirst20Elements = Arrays.copyOfRange(hash, 0, 20); @@ -93,8 +92,8 @@ void getHashForMergedMiningWithForkDetectionDataAndIncludedOn() { @Test void getHashForMergedMiningWithForkDetectionDataAndIncludedOff() { - byte[] forkDetectionData = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; - BlockHeader header = createBlockHeaderWithNoMergedMiningFields(forkDetectionData, false, new byte[0]); + byte[] forkDetectionData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; + BlockHeader header = createBlockHeaderWithNoMergedMiningFields(forkDetectionData, false, new byte[0], new short[0]); byte[] hash = header.getHash().getBytes(); byte[] hashForMergedMining = header.getHashForMergedMining(); @@ -106,58 +105,100 @@ void getHashForMergedMiningWithForkDetectionDataAndIncludedOff() { @Test void getEncodedWithUmmRootWithMergedMiningFields() { byte[] ummRoot = TestUtils.randomBytes(20); - BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, ummRoot); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, ummRoot, new short[0]); byte[] headerEncoded = header.getFullEncoded(); RLPList headerRLP = RLP.decodeList(headerEncoded); - MatcherAssert.assertThat(headerRLP.size(), is(20)); + MatcherAssert.assertThat(headerRLP.size(), is(21)); } @Test void getEncodedWithUmmRootWithoutMergedMiningFields() { byte[] ummRoot = TestUtils.randomBytes(20); - BlockHeader header = createBlockHeaderWithNoMergedMiningFields(new byte[0], false, ummRoot); + BlockHeader header = createBlockHeaderWithNoMergedMiningFields(new byte[0], false, ummRoot, new short[0]); byte[] headerEncoded = header.getFullEncoded(); RLPList headerRLP = RLP.decodeList(headerEncoded); - MatcherAssert.assertThat(headerRLP.size(), is(17)); + MatcherAssert.assertThat(headerRLP.size(), is(18)); } @Test void getEncodedNullUmmRootWithMergedMiningFields() { - BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, null); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, null, new short[0]); byte[] headerEncoded = header.getFullEncoded(); RLPList headerRLP = RLP.decodeList(headerEncoded); - MatcherAssert.assertThat(headerRLP.size(), is(19)); + MatcherAssert.assertThat(headerRLP.size(), is(20)); } @Test void getEncodedNullUmmRootWithoutMergedMiningFields() { - BlockHeader header = createBlockHeaderWithNoMergedMiningFields(new byte[0], false, null); + BlockHeader header = createBlockHeaderWithNoMergedMiningFields(new byte[0], false, null, new short[0]); byte[] headerEncoded = header.getFullEncoded(); RLPList headerRLP = RLP.decodeList(headerEncoded); - MatcherAssert.assertThat(headerRLP.size(), is(16)); + MatcherAssert.assertThat(headerRLP.size(), is(17)); } @Test void getEncodedEmptyUmmRootWithMergedMiningFields() { - BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0]); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0], new short[0]); byte[] headerEncoded = header.getFullEncoded(); RLPList headerRLP = RLP.decodeList(headerEncoded); - MatcherAssert.assertThat(headerRLP.size(), is(20)); + MatcherAssert.assertThat(headerRLP.size(), is(21)); } @Test void getEncodedEmptyUmmRootWithoutMergedMiningFields() { - BlockHeader header = createBlockHeaderWithNoMergedMiningFields(new byte[0], false, new byte[0]); + BlockHeader header = createBlockHeaderWithNoMergedMiningFields(new byte[0], false, new byte[0], new short[0]); + + byte[] headerEncoded = header.getFullEncoded(); + RLPList headerRLP = RLP.decodeList(headerEncoded); + + MatcherAssert.assertThat(headerRLP.size(), is(18)); + } + + @Test + void getEncodedWithEdgesWithMergedMiningFields() { + short[] edges = TestUtils.randomShortArray(4); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0], edges); + + byte[] headerEncoded = header.getFullEncoded(); + RLPList headerRLP = RLP.decodeList(headerEncoded); + + MatcherAssert.assertThat(headerRLP.size(), is(21)); + } + + @Test + void getEncodedWithEdgesWithoutMergedMiningFields() { + short[] edges = TestUtils.randomShortArray(4); + BlockHeader header = createBlockHeaderWithNoMergedMiningFields(new byte[0], false, new byte[0], edges); + + byte[] headerEncoded = header.getFullEncoded(); + RLPList headerRLP = RLP.decodeList(headerEncoded); + + MatcherAssert.assertThat(headerRLP.size(), is(18)); + } + + @Test + void getEncodedNullEdgesWithMergedMiningFields() { + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0], null); + + byte[] headerEncoded = header.getFullEncoded(); + RLPList headerRLP = RLP.decodeList(headerEncoded); + + MatcherAssert.assertThat(headerRLP.size(), is(20)); + } + + @Test + void getEncodedNullEdgesWithoutMergedMiningFields() { + BlockHeader header = createBlockHeaderWithNoMergedMiningFields(new byte[0], false, new byte[0], null); byte[] headerEncoded = header.getFullEncoded(); RLPList headerRLP = RLP.decodeList(headerEncoded); @@ -165,9 +206,34 @@ void getEncodedEmptyUmmRootWithoutMergedMiningFields() { MatcherAssert.assertThat(headerRLP.size(), is(17)); } + @Test + void getEncodedWithEdgesAndUmmRootWithMergedMiningFields() { + byte[] ummRoot = TestUtils.randomBytes(20); + short[] edges = TestUtils.randomShortArray(4); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, ummRoot, edges); + + byte[] headerEncoded = header.getFullEncoded(); + RLPList headerRLP = RLP.decodeList(headerEncoded); + + MatcherAssert.assertThat(headerRLP.size(), is(21)); + } + + @Test + void getEncodedWithEdgesAndUmmRootWithoutMergedMiningFields() { + byte[] ummRoot = TestUtils.randomBytes(20); + short[] edges = TestUtils.randomShortArray(4); + BlockHeader header = createBlockHeaderWithNoMergedMiningFields(new byte[0], false, ummRoot, edges); + + byte[] headerEncoded = header.getFullEncoded(); + RLPList headerRLP = RLP.decodeList(headerEncoded); + + MatcherAssert.assertThat(headerRLP.size(), is(18)); + } + + @Test void getMiningForkDetectionData() { - BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], true, new byte[0]); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], true, new byte[0], new short[0]); byte[] encodedBlock = header.getEncoded(false, false); byte[] hashForMergedMining = Arrays.copyOfRange(HashUtil.keccak256(encodedBlock), 0, 20); @@ -263,7 +329,7 @@ void getHashForMergedMiningWhenUmmRootWithLengthOver20() { */ @Test void getMiningForkDetectionDataNoDataCanBeFound() { - BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], true, new byte[0]); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], true, new byte[0], new short[0]); byte[] forkDetectionData = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; byte[] coinbase = concatenate(RskMiningConstants.RSK_TAG, forkDetectionData); @@ -275,14 +341,14 @@ void getMiningForkDetectionDataNoDataCanBeFound() { @Test void getMiningForkDetectionDataNoDataMustBeIncluded() { - BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0]); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0], new short[0]); MatcherAssert.assertThat(new byte[0], is(header.getMiningForkDetectionData())); } @Test void getHashShouldReuseCalculatedValue() { - BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0]); + BlockHeader header = createBlockHeaderWithMergedMiningFields(new byte[0], false, new byte[0], new short[0]); byte[] headerEncoded = header.getEncoded(); try (MockedStatic hashUtilMocked = mockStatic(HashUtil.class)) { @@ -299,7 +365,7 @@ void getHashShouldReuseCalculatedValue() { @Test void verifyRecalculatedHashForAmendedBlocks() { BlockHeader header = createBlockHeader(new byte[80], new byte[32], new byte[128], new byte[0], - false, new byte[0], false, false); + false, new byte[0], new short[0], false, false); assertArrayEquals(HashUtil.keccak256(header.getEncoded()), header.getHash().getBytes()); @@ -327,38 +393,39 @@ void verifyRecalculatedHashForAmendedBlocks() { private BlockHeader createBlockHeaderWithMergedMiningFields( byte[] forkDetectionData, - boolean includeForkDetectionData, byte[] ummRoot) { + boolean includeForkDetectionData, byte[] ummRoot, short[] edges){ return createBlockHeader(new byte[80], new byte[32], new byte[128], - forkDetectionData, includeForkDetectionData, ummRoot, false); + forkDetectionData, includeForkDetectionData, ummRoot, edges, false); } private BlockHeader createBlockHeaderWithNoMergedMiningFields( byte[] forkDetectionData, - boolean includeForkDetectionData, byte[] ummRoot) { + boolean includeForkDetectionData, byte[] ummRoot, short[] edges) { return createBlockHeader(null, null, null, - forkDetectionData, includeForkDetectionData, ummRoot, true); + forkDetectionData, includeForkDetectionData, ummRoot, edges, true); } private BlockHeader createBlockHeaderWithUmmRoot(byte[] ummRoot) { return createBlockHeader(null, null, null, - new byte[0], false, ummRoot, true); + new byte[0], false, ummRoot, new short[0], true); } private BlockHeader createBlockHeaderWithUmmRoot(byte[] ummRoot, byte[] forkDetectionData) { return createBlockHeader(null, null, null, - forkDetectionData, true, ummRoot, true); + forkDetectionData, true, ummRoot, new short[0], true); } private BlockHeader createBlockHeader(byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] forkDetectionData, - boolean includeForkDetectionData, byte[] ummRoot, boolean sealed) { + boolean includeForkDetectionData, byte[] ummRoot, short[] edges, boolean sealed) { return createBlockHeader(bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, - forkDetectionData, includeForkDetectionData, ummRoot, true, sealed); + forkDetectionData, includeForkDetectionData, ummRoot, edges, true, sealed); } private BlockHeader createBlockHeader(byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] forkDetectionData, - boolean includeForkDetectionData, byte[] ummRoot, boolean useRskip92Encoding, + boolean includeForkDetectionData, byte[] ummRoot, short[] edges, + boolean useRskip92Encoding, boolean sealed) { BlockDifficulty difficulty = new BlockDifficulty(BigInteger.ONE); long number = 1; @@ -389,7 +456,8 @@ private BlockHeader createBlockHeader(byte[] bitcoinMergedMiningHeader, byte[] b sealed, useRskip92Encoding, includeForkDetectionData, - ummRoot); + ummRoot, + edges); } private byte[] concatenate(byte[] left, byte[] right) { diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index 131d8d554ba..709ef36284b 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -32,6 +32,7 @@ import co.rsk.trie.TrieStore; import co.rsk.trie.TrieStoreImpl; import org.bouncycastle.util.BigIntegers; +import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; @@ -53,6 +54,8 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import java.math.BigInteger; import java.util.*; @@ -69,7 +72,8 @@ public class BlockExecutorTest { private static final byte[] EMPTY_TRIE_HASH = sha3(RLP.encodeElement(EMPTY_BYTE_ARRAY)); private static final TestSystemProperties CONFIG = new TestSystemProperties(); - private static final BlockFactory BLOCK_FACTORY = new BlockFactory(CONFIG.getActivationConfig()); + private static final ActivationConfig activationConfig = spy(CONFIG.getActivationConfig()); + private static final BlockFactory BLOCK_FACTORY = new BlockFactory(activationConfig); private Blockchain blockchain; private BlockExecutor executor; @@ -77,7 +81,7 @@ public class BlockExecutorTest { private RepositorySnapshot repository; @BeforeEach - void setUp() { + public void setUp() { RskTestFactory objects = new RskTestFactory(CONFIG); blockchain = objects.getBlockchain(); executor = objects.getBlockExecutor(); @@ -85,13 +89,18 @@ void setUp() { repository = objects.getRepositoryLocator().snapshotAt(blockchain.getBestBlock().getHeader()); } - @Test - void executeBlockWithoutTransaction() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeBlockWithoutTransaction(Boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); Block parent = blockchain.getBestBlock(); - Block block = new BlockGenerator().createChildBlock(parent); + Block block = new BlockGenerator(Constants.regtest(), activationConfig).createChildBlock(parent); BlockResult result = executor.execute(block, parent.getHeader(), false); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertNotNull(result); Assertions.assertNotNull(result.getTransactionReceipts()); Assertions.assertTrue(result.getTransactionReceipts().isEmpty()); @@ -99,8 +108,10 @@ void executeBlockWithoutTransaction() { Assertions.assertArrayEquals(repository.getRoot(), result.getFinalState().getHash().getBytes()); } - @Test - void executeBlockWithOneTransaction() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeBlockWithOneTransaction(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); executor.setRegisterProgramResults(false); Block block = getBlockWithOneTransaction(); // this changes the best block Block parent = blockchain.getBestBlock(); @@ -110,6 +121,9 @@ void executeBlockWithOneTransaction() { BlockResult result = executor.execute(block, parent.getHeader(), false); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertNotNull(result); Assertions.assertNotNull(result.getTransactionReceipts()); Assertions.assertFalse(result.getTransactionReceipts().isEmpty()); @@ -146,8 +160,10 @@ void executeBlockWithOneTransaction() { Assertions.assertEquals(BigInteger.valueOf(30000 - 21000 - 10), accountState.getBalance().asBigInteger()); } - @Test - void executeBlockWithOneTransactionAndCollectingProgramResults() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeBlockWithOneTransactionAndCollectingProgramResults(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); executor.setRegisterProgramResults(true); Block block = getBlockWithOneTransaction(); // this changes the best block Block parent = blockchain.getBestBlock(); @@ -157,6 +173,9 @@ void executeBlockWithOneTransactionAndCollectingProgramResults() { BlockResult result = executor.execute(block, parent.getHeader(), false); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertNotNull(result); Assertions.assertNotNull(result.getTransactionReceipts()); Assertions.assertFalse(result.getTransactionReceipts().isEmpty()); @@ -194,8 +213,10 @@ void executeBlockWithOneTransactionAndCollectingProgramResults() { Assertions.assertEquals(BigInteger.valueOf(30000 - 21000 - 10), accountState.getBalance().asBigInteger()); } - @Test - void executeBlockWithTwoTransactions() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeBlockWithTwoTransactions(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); Block block = getBlockWithTwoTransactions(); // this changes the best block Block parent = blockchain.getBestBlock(); @@ -205,6 +226,9 @@ void executeBlockWithTwoTransactions() { BlockResult result = executor.execute(block, parent.getHeader(), false); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertNotNull(result); Assertions.assertNotNull(result.getTransactionReceipts()); @@ -278,8 +302,10 @@ void executeBlockWithSavingToStore() { Assertions.assertEquals(trieStore.retrieve(block.getStateRoot()), Optional.of(result.getFinalState())); } - @Test - void executeAndFillBlockWithOneTransaction() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeAndFillBlockWithOneTransaction(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TestObjects objects = generateBlockWithOneTransaction(); Block parent = objects.getParent(); Block block = objects.getBlock(); @@ -289,6 +315,9 @@ void executeAndFillBlockWithOneTransaction() { executor.executeAndFill(block, parent.getHeader()); byte[] calculatedReceiptsRoot = BlockHashesHelper.calculateReceiptsTrieRoot(result.getTransactionReceipts(), true); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertArrayEquals(calculatedReceiptsRoot, block.getReceiptsRoot()); Assertions.assertArrayEquals(result.getFinalState().getHash().getBytes(), block.getStateRoot()); Assertions.assertEquals(result.getGasUsed(), block.getGasUsed()); @@ -298,8 +327,10 @@ void executeAndFillBlockWithOneTransaction() { Assertions.assertEquals(3000000, new BigInteger(1, block.getGasLimit()).longValue()); } - @Test - void executeAndFillBlockWithTxToExcludeBecauseSenderHasNoBalance() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeAndFillBlockWithTxToExcludeBecauseSenderHasNoBalance(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Repository repository = new MutableRepository(new MutableTrieImpl(trieStore, new Trie(trieStore))); @@ -343,13 +374,16 @@ void executeAndFillBlockWithTxToExcludeBecauseSenderHasNoBalance() { List uncles = new ArrayList<>(); - BlockGenerator blockGenerator = new BlockGenerator(); + BlockGenerator blockGenerator = new BlockGenerator(Constants.regtest(), activationConfig); Block genesis = blockGenerator.getGenesisBlock(); genesis.setStateRoot(repository.getRoot()); Block block = blockGenerator.createChildBlock(genesis, txs, uncles, 1, null); executor.executeAndFill(block, genesis.getHeader()); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); // Check tx2 was excluded Assertions.assertEquals(1, block.getTransactionsList().size()); Assertions.assertEquals(tx, block.getTransactionsList().get(0)); @@ -361,8 +395,10 @@ void executeAndFillBlockWithTxToExcludeBecauseSenderHasNoBalance() { Assertions.assertEquals(3141592, new BigInteger(1, block.getGasLimit()).longValue()); } - @Test - void executeBlockWithTxThatMakesBlockInvalidSenderHasNoBalance() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeBlockWithTxThatMakesBlockInvalidSenderHasNoBalance(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Repository repository = new MutableRepository(new MutableTrieImpl(trieStore, new Trie(trieStore))); @@ -406,22 +442,86 @@ void executeBlockWithTxThatMakesBlockInvalidSenderHasNoBalance() { List uncles = new ArrayList<>(); - BlockGenerator blockGenerator = new BlockGenerator(); + BlockGenerator blockGenerator = new BlockGenerator(Constants.regtest(), activationConfig); Block genesis = blockGenerator.getGenesisBlock(); genesis.setStateRoot(repository.getRoot()); Block block = blockGenerator.createChildBlock(genesis, txs, uncles, 1, null); BlockResult result = executor.execute(block, genesis.getHeader(), false); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertSame(BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT, result); } - @Test - void validateStateRootWithRskip126DisabledAndValidStateRoot() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeParallelBlocksWithDifferentSubsets(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + + doReturn(true).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + Block parent = blockchain.getBestBlock(); + Block block1 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); + BlockResult result1 = executor.execute(block1, parent.getHeader(), true); + + + Block block2 = getBlockWithTenTransactions(new short[]{5}); + BlockResult result2 = executor.execute(block2, parent.getHeader(), true); + + Assertions.assertArrayEquals(result2.getFinalState().getHash().getBytes(), result1.getFinalState().getHash().getBytes()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeParallelBlockAgainstSequentialBlock(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + + doReturn(true).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + Block parent = blockchain.getBestBlock(); + Block pBlock = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); + BlockResult parallelResult = executor.execute(pBlock, parent.getHeader(), true); + + + Block sBlock = getBlockWithTenTransactions(null); + BlockResult seqResult = executor.execute(sBlock, parent.getHeader(), true); + + Assertions.assertEquals(pBlock.getTransactionsList().size(), parallelResult.getExecutedTransactions().size()); + Assertions.assertArrayEquals(seqResult.getFinalState().getHash().getBytes(), parallelResult.getFinalState().getHash().getBytes()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeParallelBlockTwice(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + + doReturn(true).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + Block parent = blockchain.getBestBlock(); + Block block1 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); + BlockResult result1 = executor.executeAndFill(block1, parent.getHeader()); + + + Block block2 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); + BlockResult result2 = executor.executeAndFill(block2, parent.getHeader()); + + Assertions.assertArrayEquals(result2.getFinalState().getHash().getBytes(), result1.getFinalState().getHash().getBytes()); + Assertions.assertArrayEquals(block1.getHash().getBytes(), block2.getHash().getBytes()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void validateStateRootWithRskip126DisabledAndValidStateRoot(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Trie trie = new Trie(trieStore); - Block block = new BlockGenerator().getBlock(1); + Block block = new BlockGenerator(Constants.regtest(), activationConfig).getBlock(1); block.setStateRoot(trie.getHash().getBytes()); BlockResult blockResult = new BlockResult(block, Collections.emptyList(), Collections.emptyList(), 0, @@ -435,15 +535,20 @@ void validateStateRootWithRskip126DisabledAndValidStateRoot() { BlockExecutor executor = buildBlockExecutor(trieStore, cfg); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertTrue(executor.validateStateRoot(block.getHeader(), blockResult)); } - @Test - void validateStateRootWithRskip126DisabledAndInvalidStateRoot() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void validateStateRootWithRskip126DisabledAndInvalidStateRoot(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Trie trie = new Trie(trieStore); - Block block = new BlockGenerator().getBlock(1); + Block block = new BlockGenerator(Constants.regtest(), activationConfig).getBlock(1); block.setStateRoot(new byte[] { 1, 2, 3, 4 }); BlockResult blockResult = new BlockResult(block, Collections.emptyList(), Collections.emptyList(), 0, @@ -457,21 +562,31 @@ void validateStateRootWithRskip126DisabledAndInvalidStateRoot() { BlockExecutor executor = buildBlockExecutor(trieStore, cfg); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertTrue(executor.validateStateRoot(block.getHeader(), blockResult)); } - @Test - void validateBlock() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void validateBlock(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TestObjects objects = generateBlockWithOneTransaction(); Block parent = objects.getParent(); Block block = objects.getBlock(); BlockExecutor executor = buildBlockExecutor(objects.getTrieStore()); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertTrue(executor.executeAndValidate(block, parent.getHeader())); } - @Test - void invalidBlockBadStateRoot() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void invalidBlockBadStateRoot(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TestObjects objects = generateBlockWithOneTransaction(); Block parent = objects.getParent(); Block block = objects.getBlock(); @@ -479,12 +594,16 @@ void invalidBlockBadStateRoot() { byte[] stateRoot = block.getStateRoot(); stateRoot[0] = (byte) ((stateRoot[0] + 1) % 256); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); } - @Test - void invalidBlockBadReceiptsRoot() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void invalidBlockBadReceiptsRoot(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TestObjects objects = generateBlockWithOneTransaction(); Block parent = objects.getParent(); Block block = objects.getBlock(); @@ -492,36 +611,48 @@ void invalidBlockBadReceiptsRoot() { byte[] receiptsRoot = block.getReceiptsRoot(); receiptsRoot[0] = (byte) ((receiptsRoot[0] + 1) % 256); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); } - @Test - void invalidBlockBadGasUsed() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void invalidBlockBadGasUsed(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TestObjects objects = generateBlockWithOneTransaction(); Block parent = objects.getParent(); Block block = objects.getBlock(); BlockExecutor executor = buildBlockExecutor(objects.getTrieStore()); block.getHeader().setGasUsed(0); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); } - @Test - void invalidBlockBadPaidFees() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void invalidBlockBadPaidFees(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TestObjects objects = generateBlockWithOneTransaction(); Block parent = objects.getParent(); Block block = objects.getBlock(); BlockExecutor executor = buildBlockExecutor(objects.getTrieStore()); block.getHeader().setPaidFees(Coin.ZERO); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); } - @Test - void invalidBlockBadLogsBloom() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void invalidBlockBadLogsBloom(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); TestObjects objects = generateBlockWithOneTransaction(); Block parent = objects.getParent(); Block block = objects.getBlock(); @@ -529,7 +660,9 @@ void invalidBlockBadLogsBloom() { byte[] logBloom = block.getLogBloom(); logBloom[0] = (byte) ((logBloom[0] + 1) % 256); + short[] expectedEdges = activeRskip144 ? new short[0] : null; + Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); } @@ -574,7 +707,7 @@ private static TestObjects generateBlockWithOneTransaction() { // in genesis. byte[] rootPriorExecution = repository.getRoot(); - Block block = new BlockGenerator().createChildBlock(genesis, txs, uncles, 1, null); + Block block = new BlockGenerator(Constants.regtest(), activationConfig).createChildBlock(genesis, txs, uncles, 1, null); executor.executeAndFill(block, genesis.getHeader()); repository.save(); @@ -610,7 +743,7 @@ private Block getBlockWithOneTransaction() { ); List uncles = new ArrayList<>(); - return new BlockGenerator().createChildBlock(bestBlock, txs, uncles, 1, null); + return new BlockGenerator(Constants.regtest(), activationConfig).createChildBlock(bestBlock, txs, uncles, 1, null); } private Block getBlockWithTwoTransactions() { @@ -654,7 +787,49 @@ private Block getBlockWithTwoTransactions() { ); List uncles = new ArrayList<>(); - return new BlockGenerator().createChildBlock(bestBlock, txs, uncles, 1, null); + return new BlockGenerator(Constants.regtest(), activationConfig).createChildBlock(bestBlock, txs, uncles, 1, null); + } + + private Block getBlockWithTenTransactions(short[] edges) { + int nTxs = 10; + int nAccounts = nTxs * 2; + Repository track = repository.startTracking(); + List accounts = new LinkedList<>(); + + for (int i = 0; i < nAccounts; i++) { + accounts.add(createAccount("accounttest" + i, track, Coin.valueOf(60000))); + } + track.commit(); + Block bestBlock = blockchain.getBestBlock(); + bestBlock.setStateRoot(repository.getRoot()); + + List txs = new LinkedList<>(); + + for (int i = 0; i < nTxs; i++) { + Transaction tx = Transaction.builder() + .nonce(BigInteger.ZERO) + .gasPrice(BigInteger.ONE) + .gasLimit(BigInteger.valueOf(21000)) + .destination(accounts.get(i + nTxs).getAddress()) + .chainId(CONFIG.getNetworkConstants().getChainId()) + .value(BigInteger.TEN) + .build(); + tx.sign(accounts.get(i).getEcKey().getPrivKeyBytes()); + txs.add(tx); + } + List uncles = new ArrayList<>(); + + return new BlockGenerator(Constants.regtest(), activationConfig) + .createChildBlock( + bestBlock, + txs, + uncles, + 1, + null, + bestBlock.getGasLimit(), + bestBlock.getCoinbase(), + edges + ); } public static Account createAccount(String seed, Repository repository, Coin balance) { @@ -785,7 +960,7 @@ public TestObjects generateBlockWithOneStrangeTransaction(int strangeTransaction Block genesis = BlockChainImplTest.getGenesisBlock(trieStore); genesis.setStateRoot(repository.getRoot()); - Block block = new BlockGenerator().createChildBlock(genesis, txs, uncles, 1, null); + Block block = new BlockGenerator(Constants.regtest(), activationConfig).createChildBlock(genesis, txs, uncles, 1, null); executor.executeAndFillReal(block, genesis.getHeader()); // Forces all transactions included repository.save(); diff --git a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java index 99d523cf7d4..3aea2c022f2 100644 --- a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java @@ -214,7 +214,7 @@ private BlockHeader createBlockHeader() { EMPTY_BYTE_ARRAY, 0L, 0L, EMPTY_BYTE_ARRAY, Coin.ZERO, EMPTY_BYTE_ARRAY, EMPTY_BYTE_ARRAY, EMPTY_BYTE_ARRAY, EMPTY_BYTE_ARRAY, Coin.ZERO, 0, false, true, false, - new byte[0] + new byte[0], null ); } } diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java index 997ce1c4d35..763f62b069c 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java @@ -293,7 +293,7 @@ public HardcodedHashBlockHeader( HashUtil.EMPTY_TRIE_HASH, new Bloom().getData(), finalDifficulty, parentBlock.getNumber() + 1, parentBlock.getGasLimit(), parentBlock.getGasUsed(), parentBlock.getTimestamp(), new byte[0], paidFees, null, null, null, new byte[0], - Coin.valueOf(10), uncles.size(), false, true, false, new byte[0] + Coin.valueOf(10), uncles.size(), false, true, false, new byte[0], null ); this.blockHash = blockHash; } diff --git a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/subscribe/EthSubscriptionNotificationTest.java b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/subscribe/EthSubscriptionNotificationTest.java index dca4ae7a98f..1fd41fe4ea5 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/subscribe/EthSubscriptionNotificationTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/subscribe/EthSubscriptionNotificationTest.java @@ -30,9 +30,9 @@ class EthSubscriptionNotificationTest { private static final Block TEST_BLOCK = new BlockGenerator().createBlock(12, 0); - private static final String TEST_BLOCK_RESULT_JSON = "{\"difficulty\":\"0x20000\",\"extraData\":\"0x\",\"gasLimit\":\"0x2fefd8\",\"gasUsed\":\"0x0\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0xe94aef644e428941ee0a3741f28d80255fddba7f\",\"number\":\"0xc\",\"parentHash\":\"0xbe5de0c9c661653c979ec457f610444dcd0048007e683b2d04ce05729af56280\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x1\",\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"hash\":\"0x35b063d13f7d7b3c13ae508e2c2b3aa7e7ba110d4dda17f3d822ac24b1f952b7\"}"; + private static final String TEST_BLOCK_RESULT_JSON = "{\"difficulty\":\"0x20000\",\"extraData\":\"0x\",\"gasLimit\":\"0x2fefd8\",\"gasUsed\":\"0x0\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0xe94aef644e428941ee0a3741f28d80255fddba7f\",\"number\":\"0xc\",\"parentHash\":\"0xbe5de0c9c661653c979ec457f610444dcd0048007e683b2d04ce05729af56280\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x1\",\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"hash\":\"0xae2699897284d434f3139376fbc606aabf2eade4b2cd1e551d37395f0b095dca\"}"; private static final Block TEST_BLOCK_2 = new BlockGenerator().createBlock(12, 0, 1000000L); - private static final String TEST_BLOCK_RESULT_JSON_2 = "{\"difficulty\":\"0x20000\",\"extraData\":\"0x\",\"gasLimit\":\"0xf4240\",\"gasUsed\":\"0x0\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0xe94aef644e428941ee0a3741f28d80255fddba7f\",\"number\":\"0xc\",\"parentHash\":\"0x82d804adc43b6382427216a764963f77c612694065f19b3b97c804338c6ceeec\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x1\",\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"hash\":\"0xa31b89729ad3f84b988a9418e629adf1de918dfcff8286ed65e06837574e80d8\"}"; + private static final String TEST_BLOCK_RESULT_JSON_2 = "{\"difficulty\":\"0x20000\",\"extraData\":\"0x\",\"gasLimit\":\"0xf4240\",\"gasUsed\":\"0x0\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0xe94aef644e428941ee0a3741f28d80255fddba7f\",\"number\":\"0xc\",\"parentHash\":\"0x82d804adc43b6382427216a764963f77c612694065f19b3b97c804338c6ceeec\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x1\",\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"hash\":\"0x644f8371116ee86c675cc1eadaddfd97c49617b01dd0deb47bc978fcdef86dfc\"}"; private static final String TEST_SYNC_RESULT_JSON = "{\"syncing\":true,\"status\":{\"startingBlock\":0,\"currentBlock\":1,\"highestBlock\":1000}}"; diff --git a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionListsEdgesTest.java b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionListsEdgesTest.java new file mode 100644 index 00000000000..dc6441d6481 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionListsEdgesTest.java @@ -0,0 +1,93 @@ +package co.rsk.validators; + +import org.ethereum.core.Block; +import org.ethereum.core.BlockHeader; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.mockito.Mockito; + +import java.util.LinkedList; +import java.util.List; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class ValidTxExecutionListsEdgesTest { + + private BlockHeader blockHeader; + private Block block; + + @BeforeAll + public void setUp() { + blockHeader = Mockito.mock(org.ethereum.core.BlockHeader.class); + block = Mockito.mock(Block.class); + List txList = Mockito.mock(LinkedList.class); + Mockito.when(block.getHeader()).thenReturn(blockHeader); + Mockito.when(block.getTransactionsList()).thenReturn(txList); + Mockito.when(txList.size()).thenReturn(10); + } + + @Test + public void blockWithValidEdges() { + Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{2, 5, 6}); + + ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); + Assertions.assertTrue(rule.isValid(block)); + } + + @Test + public void blockWithNullEdges() { + Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(null); + + ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); + Assertions.assertTrue(rule.isValid(block)); + } + + @Test + public void blockWithEmptyEdges() { + Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[0]); + + ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); + Assertions.assertTrue(rule.isValid(block)); + } + + @Test + public void blockWithTooManyEdges() { + Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{2, 5, 6, 8, 10, 12, 14}); + + ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + public void blockWithOutOfBoundsEdges() { + Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{12}); + + ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + public void blockWithNegativeEdge() { + Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{-2}); + + ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + public void blockWithEmptyListDefined() { + Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{2, 2}); + + ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + public void blockWithEdgesNotInOrder() { + Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{2, 4, 3}); + + ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } +} diff --git a/rskj-core/src/test/java/org/ethereum/TestUtils.java b/rskj-core/src/test/java/org/ethereum/TestUtils.java index fd50a09954a..ead9edfb7ca 100644 --- a/rskj-core/src/test/java/org/ethereum/TestUtils.java +++ b/rskj-core/src/test/java/org/ethereum/TestUtils.java @@ -22,6 +22,17 @@ import co.rsk.core.Coin; import co.rsk.core.RskAddress; import co.rsk.crypto.Keccak256; + +import java.io.File; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.ethereum.core.Block; @@ -61,6 +72,14 @@ public static byte[] randomBytes(int length) { return result; } + public static short[] randomShortArray(int length) { + short[] result = new short[length]; + byte[] bytes = new byte[length * 2]; + getRandom().nextBytes(bytes); + ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(result); + return result; + } + public static BigInteger randomBigInteger(int maxSizeBytes) { return new BigInteger(maxSizeBytes * 8, getRandom()); } diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index 04d736339f9..94c1e2ac0ea 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -101,6 +101,7 @@ class ActivationConfigTest { " rskip290: hop400", " rskip293: hop400", " rskip294: hop400", + " rskip144: hop400", " rskip297: hop400", "}" )); diff --git a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java index e9881de3d1b..81ac3c91d9f 100644 --- a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java @@ -400,14 +400,108 @@ void createsHeaderWithNullUmmrootButUmmCompliantHeaderOffAndRskipUmmOff() { assertArrayEquals(null, header.getUmmRoot()); } + @Test + void createsHeaderWithParallelCompliant() { + BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); + + BlockHeader header = builder + .setCreateParallelCompliantHeader(true) + .build(); + + assertArrayEquals(new short[0], header.getTxExecutionListsEdges()); + } + + @Test + void createsHeaderWithoutParallelCompliant() { + BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); + + BlockHeader header = builder + .setCreateParallelCompliantHeader(false) + .build(); + + assertArrayEquals(null, header.getTxExecutionListsEdges()); + } + + @Test + void createsHeaderWithEdges() { + BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); + short[] edges = TestUtils.randomShortArray(4); + + BlockHeader header = builder + .setTxExecutionListsEdges(edges) + .build(); + + assertArrayEquals(edges, header.getTxExecutionListsEdges()); + } + + @Test + void createsHeaderWithNullEdges() { + BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); + + BlockHeader header = builder + .setTxExecutionListsEdges(null) + .build(); + + assertArrayEquals(null, header.getTxExecutionListsEdges()); + } + + @Test + void createsHeaderWithNullEdgesButParallelCompliant() { + BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); + + BlockHeader header = builder + .setTxExecutionListsEdges(null) + .setCreateParallelCompliantHeader(true) + .build(); + + assertArrayEquals(new short[0], header.getTxExecutionListsEdges()); + } + + @Test + void createsHeaderWithoutParallelCompliantButWithEdges() { + BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); + short[] edges = TestUtils.randomShortArray(4); + + BlockHeader header = builder + .setCreateParallelCompliantHeader(false) + .setTxExecutionListsEdges(edges) + .build(); + + assertArrayEquals(edges, header.getTxExecutionListsEdges()); + } + + @Test + void createsHeaderWithEdgesButWithoutParallelCompliant() { + BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); + short[] edges = TestUtils.randomShortArray(4); + + BlockHeader header = builder + .setTxExecutionListsEdges(edges) + .setCreateParallelCompliantHeader(false) + .build(); + + assertArrayEquals(null, header.getTxExecutionListsEdges()); + } + + @Test + void createsHeaderWithParallelCompliantButWithNullEdges() { + BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); + + BlockHeader header = builder + .setCreateParallelCompliantHeader(true) + .setTxExecutionListsEdges(null) + .build(); + + assertArrayEquals(null, header.getTxExecutionListsEdges()); + } private static class CreateHeaderArgumentsProvider implements ArgumentsProvider { @Override public Stream provideArguments(ExtensionContext context) { return Stream.of( - Arguments.of(false, false, 20), - Arguments.of(false, true, 18), - Arguments.of(true, false, 18) + Arguments.of(false, false, 21), + Arguments.of(false, true, 19), + Arguments.of(true, false, 19) ); } } diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java index 466abd6676f..040582ed148 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java @@ -106,7 +106,7 @@ class Web3ImplLogsTest { private static final String ONE_TOPIC = "0000000000000000000000000000000000000000000000000000000000000001"; private static final String INCREMENT_METHOD_SIGNATURE = "371303c0"; private static final String GET_VALUE_METHOD_SIGNATURE = "20965255"; - private static final String TRACKED_TEST_BLOCK_HASH = "0xafb368a4f74e51a3c6b6d72b049c4fc7bc7506251f13a3afa4fee4bece0e85eb"; + private static final String TRACKED_TEST_BLOCK_HASH = "0x5fcfdf1c5c83850e4e4094124a1d7a314b8684055ed4577a02abf5bc438096f7"; private static final String UNTRACKED_TEST_BLOCK_HASH = "0xdea168a4f74e51a3eeb6d72b049c4fc7bc750dd51f13a3afa4fee4bece0e85eb"; private final TestSystemProperties config = new TestSystemProperties(); private Blockchain blockChain; From 89f947164a37e2221ceb54f107e8dcce96983fe5 Mon Sep 17 00:00:00 2001 From: julianlen Date: Wed, 1 Jun 2022 13:49:23 -0300 Subject: [PATCH 09/89] Modified the remasc payment. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of paying to the REMASC contract…every transaction, the fees are accumulated into a variable and once all of the transactions are processed, the fees are added to the Remasc's balance in the MutableRepository. If the rREMASCFee is greater than 0 the payment is made. Some tests had to be changed since they replicate the behavior of the block executor and the REMASC payment is actually made in the block executor so the tests were modified as well --- .../rsk/core/TransactionExecutorFactory.java | 19 ++++++++++++++++--- .../co/rsk/core/TransactionListExecutor.java | 6 +++++- .../java/co/rsk/core/bc/BlockExecutor.java | 16 +++++++++++++++- .../ethereum/core/TransactionExecutor.java | 7 +++++-- .../src/test/java/co/rsk/vm/MinerHelper.java | 17 ++++++++++++++++- .../core/TransactionExecutorTest.java | 9 +++++---- .../runners/StateTestRunner.java | 11 +++++++++-- 7 files changed, 71 insertions(+), 14 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java b/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java index fb500c9afcd..d41e446fefb 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java @@ -29,6 +29,7 @@ import java.util.HashSet; import java.util.Set; +import java.util.concurrent.atomic.LongAccumulator; public class TransactionExecutorFactory { @@ -64,7 +65,17 @@ public TransactionExecutor newInstance( Repository track, Block block, long totalGasUsed) { - return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, false, 0, new HashSet<>()); + return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, new LongAccumulator(Long::sum, 0)); + } + + public TransactionExecutor newInstance( + Transaction tx, + int txindex, + RskAddress coinbase, + Repository track, + Block block, + long totalGasUsed, LongAccumulator remascFees) { + return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, false, 0, new HashSet<>(), remascFees); } public TransactionExecutor newInstance( @@ -76,7 +87,8 @@ public TransactionExecutor newInstance( long totalGasUsed, boolean vmTrace, int vmTraceOptions, - Set deletedAccounts) { + Set deletedAccounts, + LongAccumulator remascFees) { // Tracing configuration is scattered across different files (VM, DetailedProgramTrace, etc.) and // TransactionExecutor#extractTrace doesn't work when called independently. // It would be great to decouple from VmConfig#vmTrace, but sadly that's a major refactor we can't do now. @@ -109,7 +121,8 @@ public TransactionExecutor newInstance( config.isRemascEnabled(), precompiledContracts, deletedAccounts, - blockTxSignatureCache + blockTxSignatureCache, + remascFees ); } } diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 43048966c39..33e7b4f348b 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -32,6 +32,7 @@ public class TransactionListExecutor implements Callable { private final Map receipts; private final Map transactionResults; private final ProgramTraceProcessor programTraceProcessor; + private final LongAccumulator remascFees; private final LongAccumulator accumulatedFees; private final LongAccumulator accumulatedGas; @@ -53,6 +54,7 @@ public TransactionListExecutor( Map transactionResults, boolean registerProgramResults, @Nullable ProgramTraceProcessor programTraceProcessor, + LongAccumulator remascFees, LongAccumulator accumulatedFees, LongAccumulator accumulatedGas, int firstTxIndex) { @@ -73,6 +75,7 @@ public TransactionListExecutor( this.accumulatedFees = accumulatedFees; this.accumulatedGas = accumulatedGas; this.i = firstTxIndex; + this.remascFees = remascFees; } @Override @@ -90,7 +93,8 @@ public Boolean call() { totalGasUsed, vmTrace, vmTraceOptions, - deletedAccounts + deletedAccounts, + remascFees ); boolean transactionExecuted = txExecutor.executeTransaction(); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 265202d9c66..6228b70b746 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -332,6 +332,7 @@ private BlockResult executeParallel( Map receipts = new ConcurrentSkipListMap<>(); Map executedTransactions = new ConcurrentSkipListMap<>(); Set deletedAccounts = ConcurrentHashMap.newKeySet(); + LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); CompletionService completionService = new ExecutorCompletionService(executorService); @@ -356,6 +357,7 @@ private BlockResult executeParallel( transactionResults, registerProgramResults, programTraceProcessor, + remascFees, totalPaidFees, totalGasUsed, start @@ -404,6 +406,7 @@ private BlockResult executeParallel( transactionResults, registerProgramResults, programTraceProcessor, + remascFees, totalPaidFees, totalGasUsed, start @@ -413,6 +416,7 @@ private BlockResult executeParallel( return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } + addFeesToRemasc(remascFees, track); saveOrCommitTrackState(saveState, track); BlockResult result = new BlockResult( block, @@ -427,6 +431,13 @@ private BlockResult executeParallel( return result; } + private void addFeesToRemasc(LongAccumulator remascFees, Repository track) { + long fee = remascFees.get(); + if (fee > 0) { + track.addBalance(PrecompiledContracts.REMASC_ADDR, Coin.valueOf(fee)); + } + } + private BlockResult executeSequential( @Nullable ProgramTraceProcessor programTraceProcessor, int vmTraceOptions, @@ -462,6 +473,7 @@ private BlockResult executeSequential( List receipts = new ArrayList<>(); List executedTransactions = new ArrayList<>(); Set deletedAccounts = new HashSet<>(); + LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); int txindex = 0; @@ -477,7 +489,8 @@ private BlockResult executeSequential( totalGasUsed, vmTrace, vmTraceOptions, - deletedAccounts); + deletedAccounts, + remascFees); boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { @@ -537,6 +550,7 @@ private BlockResult executeSequential( logger.trace("tx done"); } + addFeesToRemasc(remascFees, track); saveOrCommitTrackState(saveState, track); BlockResult result = new BlockResult( block, diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index fc0e3426b6f..9746fa8c86d 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -47,6 +47,7 @@ import java.math.BigInteger; import java.util.*; +import java.util.concurrent.atomic.LongAccumulator; import static co.rsk.util.ListArrayUtil.getLength; import static co.rsk.util.ListArrayUtil.isEmpty; @@ -76,6 +77,7 @@ public class TransactionExecutor { private final VmConfig vmConfig; private final PrecompiledContracts precompiledContracts; private final boolean enableRemasc; + private LongAccumulator remascFee; private String executionError = ""; private final long gasUsedInTheBlock; private Coin paidFees; @@ -106,7 +108,7 @@ public TransactionExecutor( Repository track, BlockStore blockStore, ReceiptStore receiptStore, BlockFactory blockFactory, ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsedInTheBlock, VmConfig vmConfig, boolean remascEnabled, PrecompiledContracts precompiledContracts, Set deletedAccounts, - SignatureCache signatureCache) { + SignatureCache signatureCache, LongAccumulator remascFee) { this.constants = constants; this.signatureCache = signatureCache; this.activations = activationConfig.forBlock(executionBlock.getNumber()); @@ -125,6 +127,7 @@ public TransactionExecutor( this.precompiledContracts = precompiledContracts; this.enableRemasc = remascEnabled; this.deletedAccounts = new HashSet<>(deletedAccounts); + this.remascFee = remascFee; } /** @@ -529,7 +532,7 @@ private void finalization() { //TODO: REMOVE THIS WHEN THE LocalBLockTests starts working with REMASC if (enableRemasc) { logger.trace("Adding fee to remasc contract account"); - track.addBalance(PrecompiledContracts.REMASC_ADDR, summaryFee); + remascFee.accumulate(summaryFee.asBigInteger().longValue()); } else { track.addBalance(coinbase, summaryFee); } diff --git a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java index 4be8cc7ab86..c3cf7c7a155 100644 --- a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java +++ b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java @@ -38,6 +38,7 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.LongAccumulator; /** * Created by Sergio on 18/07/2016. @@ -71,6 +72,8 @@ public void processBlock( Block block, Block parent) { totalGasUsed = 0; totalPaidFees = Coin.ZERO; txReceipts = new ArrayList<>(); + LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); + Repository track = repositoryLocator.startTrackingAt(parent.getHeader()); @@ -103,7 +106,7 @@ public void processBlock( Block block, Block parent) { null, new PrecompiledContracts(config, bridgeSupportFactory), blockTxSignatureCache); TransactionExecutor executor = transactionExecutorFactory - .newInstance(tx, txindex++, block.getCoinbase(), track, block, totalGasUsed); + .newInstance(tx, txindex++, block.getCoinbase(), track, block, totalGasUsed, remascFees); executor.executeTransaction(); @@ -112,6 +115,18 @@ public void processBlock( Block block, Block parent) { totalGasUsed += gasUsed; totalPaidFees = totalPaidFees.add(paidFees); + /* + * This method is a helper of the test "testSEND_1". It is replicating the + * behavior of BlockExecutor but slightly different. In the BlockExecutor, the + * fees are sent to the Remasc address once all the transactions are executed. + * Since the only test using this helper processes one transaction, so the loop has no sense. + * */ + long fees = remascFees.get(); + if (fees > 0) { + track.addBalance(PrecompiledContracts.REMASC_ADDR, Coin.valueOf(fees)); + track.commit(); + } + track.commit(); TransactionReceipt receipt = new TransactionReceipt(); diff --git a/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java b/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java index e7e9a9c2aaf..96d354b4a9a 100644 --- a/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java @@ -21,6 +21,7 @@ import java.math.BigInteger; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.atomic.LongAccumulator; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; @@ -75,7 +76,7 @@ void testInitHandlesFreeTransactionsOK() { repository, blockStore, receiptStore, blockFactory, programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, true, precompiledContracts, deletedAccounts, - blockTxSignatureCache + blockTxSignatureCache, new LongAccumulator(Long::sum, 0) ); @@ -181,7 +182,7 @@ void InvalidTxsIsInBlockAndShouldntBeInCache(){ repository, blockStore, receiptStore, blockFactory, programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, true, precompiledContracts, deletedAccounts, - blockTxSignatureCache + blockTxSignatureCache, new LongAccumulator(Long::sum, 0) ); assertEquals(0, transaction.transactionCost(constants, activationConfig.forBlock(executionBlock.getNumber()))); @@ -214,7 +215,7 @@ void remascTxIsReceivedAndShouldntBeInCache(){ repository, blockStore, receiptStore, blockFactory, programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, true, precompiledContracts, deletedAccounts, - blockTxSignatureCache + blockTxSignatureCache, new LongAccumulator(Long::sum, 0) ); assertEquals(0, transaction.transactionCost(constants, activationConfig.forBlock(executionBlock.getNumber()))); @@ -290,7 +291,7 @@ private boolean executeValidTransaction(Transaction transaction, BlockTxSignatur repository, blockStore, receiptStore, blockFactory, programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, true, precompiledContracts, deletedAccounts, - blockTxSignatureCache + blockTxSignatureCache, new LongAccumulator(Long::sum, 0) ); return txExecutor.executeTransaction(); diff --git a/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java b/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java index 078b3b85d99..ead7a90f0b6 100644 --- a/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java +++ b/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java @@ -21,6 +21,7 @@ import co.rsk.config.BridgeRegTestConstants; import co.rsk.config.TestSystemProperties; +import co.rsk.core.Coin; import co.rsk.core.RskAddress; import co.rsk.core.TransactionExecutorFactory; import co.rsk.core.bc.BlockChainImpl; @@ -60,6 +61,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.atomic.LongAccumulator; import static org.ethereum.util.ByteUtil.byteArrayToLong; @@ -104,6 +106,7 @@ public StateTestRunner setstateTestUSeREMASC(boolean v) { protected ProgramResult executeTransaction(Transaction tx) { Repository track = repository.startTracking(); + LongAccumulator remascFee = new LongAccumulator(Long::sum, 0); TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory( config, @@ -114,15 +117,19 @@ protected ProgramResult executeTransaction(Transaction tx) { precompiledContracts, new BlockTxSignatureCache(new ReceivedTxSignatureCache())); TransactionExecutor executor = transactionExecutorFactory - .newInstance(transaction, 0, new RskAddress(env.getCurrentCoinbase()), track, blockchain.getBestBlock(), 0); + .newInstance(transaction, 0, new RskAddress(env.getCurrentCoinbase()), track, blockchain.getBestBlock(), 0, remascFee); try{ executor.executeTransaction(); - } catch (StackOverflowError soe){ + } catch (StackOverflowError soe) { logger.error(" !!! StackOverflowError: update your java run command with -Xss32M !!!"); System.exit(-1); } + if (remascFee.get() > 0) { + track.addBalance(PrecompiledContracts.REMASC_ADDR, Coin.valueOf(remascFee.get())); + } + track.commit(); return executor.getResult(); } From 5d00cce975bfbefb23643700c293a9b984af8364 Mon Sep 17 00:00:00 2001 From: julianlen Date: Wed, 15 Jun 2022 13:28:47 -0300 Subject: [PATCH 10/89] Research/parallel-tx/header generation from master (#1792) * Added the execution plan for the transaction to the block header as a transactionEdgeList. * Added ParallelizeTransactionHandler that builds the different buckets that are going to be executed by different threads. If the sequential bucket doesn't have enough gas for the new transaction, the transaction isn't added to the block. In addition, once all the transactions are processed, the order of the transactions in the buckets is added to the receipt. The REMASC transaction is always added to the SequentialBucket. When a transaction is properly added to a bucket, it returns the gas used in that bucket. * Added a tracker to the MutableRepository (MR). If the MR is used for any reason but builds the block, the tracker used is a DummyTracker one. * Review processed * Handler's been refactorized (#1760) * Added tests. * Research/parallel-tx/splitting executions (#1799) * changed method names, deleted executeInternal from blockExecutor, and changed execute for executeParallel in some places. fixed BlockExecutorTest since from now on when the block is executed with executeAndFill and its blockNumber is over the rskip144 then the transactionEdgeList is built by the miner * Three changes so test pass (i) Changed the block hash in Web3ImplLogsTest due to the new field in the header for txEdges, (ii) Many tests used a really high gas, then we decided not to split the gas limit by two for parallel and sequential bucket, and (iii) addRemascFee wasn't present in the oldSequentialExecute and now is added. --- .../java/co/rsk/cli/tools/ExecuteBlocks.java | 2 +- .../java/co/rsk/core/bc/BlockChainImpl.java | 2 +- .../java/co/rsk/core/bc/BlockExecutor.java | 254 ++++-- .../main/java/co/rsk/core/bc/BlockResult.java | 7 +- .../rsk/core/bc/IReadWrittenKeysTracker.java | 34 + .../bc/ParallelizeTransactionHandler.java | 248 ++++++ .../rsk/core/bc/ReadWrittenKeysTracker.java | 64 ++ .../java/co/rsk/db/RepositoryLocator.java | 8 + .../java/org/ethereum/core/BlockHeader.java | 4 + .../db/DummyReadWrittenKeysTracker.java | 42 + .../org/ethereum/db/MutableRepository.java | 65 +- .../rsk/blockchain/utils/BlockGenerator.java | 1 + .../co/rsk/core/bc/BlockExecutorTest.java | 392 ++++++++-- .../bc/ParallelizeTransactionHandlerTest.java | 728 ++++++++++++++++++ .../core/bc/ReadWrittenKeysTrackerTest.java | 138 ++++ .../org/ethereum/rpc/Web3ImplLogsTest.java | 2 +- 16 files changed, 1847 insertions(+), 144 deletions(-) create mode 100644 rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java create mode 100644 rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java create mode 100644 rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java create mode 100644 rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java create mode 100644 rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java create mode 100644 rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java diff --git a/rskj-core/src/main/java/co/rsk/cli/tools/ExecuteBlocks.java b/rskj-core/src/main/java/co/rsk/cli/tools/ExecuteBlocks.java index 48b5c856995..b5014618bae 100644 --- a/rskj-core/src/main/java/co/rsk/cli/tools/ExecuteBlocks.java +++ b/rskj-core/src/main/java/co/rsk/cli/tools/ExecuteBlocks.java @@ -64,7 +64,7 @@ private void executeBlocks(String[] args, BlockExecutor blockExecutor, BlockStor Block block = blockStore.getChainBlockByNumber(n); Block parent = blockStore.getBlockByHash(block.getParentHash().getBytes()); - BlockResult blockResult = blockExecutor.execute(block, parent.getHeader(), false, false, true); + BlockResult blockResult = blockExecutor.execute(null, 0, block, parent.getHeader(), false, false, true); Keccak256 stateRootHash = stateRootHandler.translate(block.getHeader()); if (!Arrays.equals(blockResult.getFinalState().getHash().getBytes(), stateRootHash.getBytes())) { diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockChainImpl.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockChainImpl.java index ee5e933898b..85ace573f5d 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockChainImpl.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockChainImpl.java @@ -249,7 +249,7 @@ private ImportResult internalTryToConnect(Block block) { long saveTime = System.nanoTime(); logger.trace("execute start"); - result = blockExecutor.execute(block, parent.getHeader(), false, noValidation, true); + result = blockExecutor.execute(null, 0, block, parent.getHeader(), false, noValidation, true); logger.trace("execute done"); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 6228b70b746..6fb07560714 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -32,6 +32,7 @@ import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; import org.ethereum.vm.DataWord; +import org.ethereum.vm.GasCost; import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.program.ProgramResult; import org.ethereum.vm.trace.ProgramTraceProcessor; @@ -119,20 +120,20 @@ public static byte[] calculateLogsBloom(List receipts) { * @param parent The parent of the block. */ public BlockResult executeAndFill(Block block, BlockHeader parent) { - BlockResult result = execute(block, parent, true, false, false); + BlockResult result = executeForMining(block, parent, true, false, false); fill(block, result); return result; } @VisibleForTesting public void executeAndFillAll(Block block, BlockHeader parent) { - BlockResult result = execute(block, parent, false, true, false); + BlockResult result = executeForMining(block, parent, false, true, false); fill(block, result); } @VisibleForTesting public void executeAndFillReal(Block block, BlockHeader parent) { - BlockResult result = execute(block, parent, false, false, false); + BlockResult result = executeForMining(block, parent, false, false, false); if (result != BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT) { fill(block, result); } @@ -149,6 +150,7 @@ private void fill(Block block, BlockResult result) { header.setGasUsed(result.getGasUsed()); header.setPaidFees(result.getPaidFees()); header.setLogsBloom(calculateLogsBloom(result.getTransactionReceipts())); + header.setTxExecutionListsEdges(result.getTxEdges()); block.flushRLP(); profiler.stop(metric); @@ -163,7 +165,7 @@ private void fill(Block block, BlockResult result) { */ @VisibleForTesting public boolean executeAndValidate(Block block, BlockHeader parent) { - BlockResult result = execute(block, parent, false, false, false); + BlockResult result = execute(null, 0, block, parent, false, false, false); return this.validate(block, result); } @@ -258,13 +260,12 @@ private boolean validateLogsBloom(BlockHeader header, BlockResult result) { return Arrays.equals(calculateLogsBloom(result.getTransactionReceipts()), header.getLogsBloom()); } - @VisibleForTesting - BlockResult execute(Block block, BlockHeader parent, boolean discardInvalidTxs) { - return execute(block, parent, discardInvalidTxs, false, true); - } - - public BlockResult execute(Block block, BlockHeader parent, boolean discardInvalidTxs, boolean ignoreReadyToExecute, boolean saveState) { - return executeInternal(null, 0, block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); + public BlockResult executeForMining(Block block, BlockHeader parent, boolean discardInvalidTxs, boolean ignoreReadyToExecute, boolean saveState) { + if (block.getHeader().getTxExecutionListsEdges() != null) { + return executeForMiningAfterRSKIP144(block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); + } else { + return executePreviousRSKIP144(null, 0, block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); + } } /** @@ -277,12 +278,30 @@ public void traceBlock( BlockHeader parent, boolean discardInvalidTxs, boolean ignoreReadyToExecute) { - executeInternal( + execute( Objects.requireNonNull(programTraceProcessor), vmTraceOptions, block, parent, discardInvalidTxs, ignoreReadyToExecute, false ); } - private BlockResult executeInternal( + public BlockResult execute( + @Nullable ProgramTraceProcessor programTraceProcessor, + int vmTraceOptions, + Block block, + BlockHeader parent, + boolean discardInvalidTxs, + boolean acceptInvalidTransactions, + boolean saveState + ) { + boolean rskip144Active = activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber()); + + if (rskip144Active && block.getHeader().getTxExecutionListsEdges() != null) { + return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); + } else { + return executePreviousRSKIP144(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); + } + } + + private BlockResult executePreviousRSKIP144( @Nullable ProgramTraceProcessor programTraceProcessor, int vmTraceOptions, Block block, @@ -290,12 +309,126 @@ private BlockResult executeInternal( boolean discardInvalidTxs, boolean acceptInvalidTransactions, boolean saveState) { + boolean vmTrace = programTraceProcessor != null; + logger.trace("Start executeInternal."); + logger.trace("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), block.getTransactionsList().size()); - if (block.getHeader().getTxExecutionListsEdges() != null) { - return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); - } else { - return executeSequential(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); + // Forks the repo, does not change "repository". It will have a completely different + // image of the repo, where the middle caches are immediately ignored. + // In fact, while cloning everything, it asserts that no cache elements remains. + // (see assertNoCache()) + // Which means that you must commit changes and save them to be able to recover + // in the next block processed. + // Note that creating a snapshot is important when the block is executed twice + // (e.g. once while building the block in tests/mining, and the other when trying + // to conect the block). This is because the first execution will change the state + // of the repository to the state post execution, so it's necessary to get it to + // the state prior execution again. + Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE); + + Repository track = repositoryLocator.startTrackingAt(parent); + + maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); + + int i = 1; + long totalGasUsed = 0; + Coin totalPaidFees = Coin.ZERO; + List receipts = new ArrayList<>(); + List executedTransactions = new ArrayList<>(); + Set deletedAccounts = new HashSet<>(); + LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); + + int txindex = 0; + + for (Transaction tx : block.getTransactionsList()) { + logger.trace("apply block: [{}] tx: [{}] ", block.getNumber(), i); + + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( + tx, + txindex++, + block.getCoinbase(), + track, + block, + totalGasUsed, + vmTrace, + vmTraceOptions, + deletedAccounts, + remascFees); + boolean transactionExecuted = txExecutor.executeTransaction(); + + if (!acceptInvalidTransactions && !transactionExecuted) { + if (discardInvalidTxs) { + logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); + continue; + } else { + logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", + block.getNumber(), tx.getHash()); + profiler.stop(metric); + return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + } + } + + executedTransactions.add(tx); + + if (this.registerProgramResults) { + this.transactionResults.put(tx.getHash(), txExecutor.getResult()); + } + + if (vmTrace) { + txExecutor.extractTrace(programTraceProcessor); + } + + logger.trace("tx executed"); + + // No need to commit the changes here. track.commit(); + + logger.trace("track commit"); + + long gasUsed = txExecutor.getGasUsed(); + totalGasUsed += gasUsed; + Coin paidFees = txExecutor.getPaidFees(); + if (paidFees != null) { + totalPaidFees = totalPaidFees.add(paidFees); + } + + deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); + + TransactionReceipt receipt = new TransactionReceipt(); + receipt.setGasUsed(gasUsed); + receipt.setCumulativeGas(totalGasUsed); + + receipt.setTxStatus(txExecutor.getReceipt().isSuccessful()); + receipt.setTransaction(tx); + receipt.setLogInfoList(txExecutor.getVMLogs()); + receipt.setStatus(txExecutor.getReceipt().getStatus()); + + logger.trace("block: [{}] executed tx: [{}]", block.getNumber(), tx.getHash()); + + logger.trace("tx[{}].receipt", i); + + i++; + + receipts.add(receipt); + + logger.trace("tx done"); } + + addFeesToRemasc(remascFees, track); + saveOrCommitTrackState(saveState, track); + + BlockResult result = new BlockResult( + block, + executedTransactions, + receipts, + null, + totalGasUsed, + totalPaidFees, + vmTrace ? null : track.getTrie() + + ); + profiler.stop(metric); + logger.trace("End executeInternal."); + return result; } private BlockResult executeParallel( @@ -420,8 +553,9 @@ private BlockResult executeParallel( saveOrCommitTrackState(saveState, track); BlockResult result = new BlockResult( block, - new LinkedList(executedTransactions.values()), - new LinkedList(receipts.values()), + new LinkedList<>(executedTransactions.values()), + new LinkedList<>(receipts.values()), + new short[0], totalGasUsed.longValue(), Coin.valueOf(totalPaidFees.longValue()), vmTrace ? null : track.getTrie() @@ -438,17 +572,16 @@ private void addFeesToRemasc(LongAccumulator remascFees, Repository track) { } } - private BlockResult executeSequential( - @Nullable ProgramTraceProcessor programTraceProcessor, - int vmTraceOptions, + private BlockResult executeForMiningAfterRSKIP144( Block block, BlockHeader parent, boolean discardInvalidTxs, boolean acceptInvalidTransactions, boolean saveState) { - boolean vmTrace = programTraceProcessor != null; + logger.trace("Start executeInternal."); - logger.trace("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), block.getTransactionsList().size()); + List transactionsList = block.getTransactionsList(); + logger.trace("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), transactionsList.size()); // Forks the repo, does not change "repository". It will have a completely different // image of the repo, where the middle caches are immediately ignored. @@ -463,21 +596,25 @@ private BlockResult executeSequential( // the state prior execution again. Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE); - Repository track = repositoryLocator.startTrackingAt(parent); + ReadWrittenKeysTracker readWrittenKeysTracker = new ReadWrittenKeysTracker(); + Repository track = repositoryLocator.startTrackingAt(parent, readWrittenKeysTracker); maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); int i = 1; - long totalGasUsed = 0; + long gasUsedInBlock = 0; Coin totalPaidFees = Coin.ZERO; - List receipts = new ArrayList<>(); - List executedTransactions = new ArrayList<>(); + Map receiptsByTx = new HashMap<>(); Set deletedAccounts = new HashSet<>(); LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); + short buckets = 2; + + //TODO(Juli): Is there a better way to calculate the bucket gas limit? + ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler(buckets, GasCost.toGas(block.getGasLimit())); int txindex = 0; - for (Transaction tx : block.getTransactionsList()) { + for (Transaction tx : transactionsList) { logger.trace("apply block: [{}] tx: [{}] ", block.getNumber(), i); TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( @@ -486,11 +623,11 @@ private BlockResult executeSequential( block.getCoinbase(), track, block, - totalGasUsed, - vmTrace, - vmTraceOptions, + parallelizeTransactionHandler.getGasUsedInSequential(), + false, + 0, deletedAccounts, - remascFees); + remascFees); //TODO(Juli): Check how to differ this behavior between RSKIPs boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { @@ -505,14 +642,29 @@ private BlockResult executeSequential( } } - executedTransactions.add(tx); + Optional bucketGasAccumulated; + if (tx.isRemascTransaction(txindex, transactionsList.size())) { + bucketGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, txExecutor.getGasUsed()); + } else { + bucketGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getTemporalReadKeys(), readWrittenKeysTracker.getTemporalWrittenKeys(), txExecutor.getGasUsed()); + } - if (this.registerProgramResults) { - this.transactionResults.put(tx.getHash(), txExecutor.getResult()); + if (!acceptInvalidTransactions && !bucketGasAccumulated.isPresent()) { + if (discardInvalidTxs) { + logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); + continue; + } else { + logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", + block.getNumber(), tx.getHash()); + profiler.stop(metric); + return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + } } - if (vmTrace) { - txExecutor.extractTrace(programTraceProcessor); + readWrittenKeysTracker.clear(); + + if (this.registerProgramResults) { + this.transactionResults.put(tx.getHash(), txExecutor.getResult()); } logger.trace("tx executed"); @@ -522,7 +674,8 @@ private BlockResult executeSequential( logger.trace("track commit"); long gasUsed = txExecutor.getGasUsed(); - totalGasUsed += gasUsed; + gasUsedInBlock += gasUsed; + Coin paidFees = txExecutor.getPaidFees(); if (paidFees != null) { totalPaidFees = totalPaidFees.add(paidFees); @@ -532,7 +685,13 @@ private BlockResult executeSequential( TransactionReceipt receipt = new TransactionReceipt(); receipt.setGasUsed(gasUsed); - receipt.setCumulativeGas(totalGasUsed); + + if (bucketGasAccumulated.isPresent()) { + receipt.setCumulativeGas(bucketGasAccumulated.get()); + } else { + //This line is used for testing only when acceptInvalidTransactions is set. + receipt.setCumulativeGas(parallelizeTransactionHandler.getGasUsedIn(buckets)); + } receipt.setTxStatus(txExecutor.getReceipt().isSuccessful()); receipt.setTransaction(tx); @@ -545,20 +704,29 @@ private BlockResult executeSequential( i++; - receipts.add(receipt); + receiptsByTx.put(tx, receipt); logger.trace("tx done"); } addFeesToRemasc(remascFees, track); saveOrCommitTrackState(saveState, track); + + List executedTransactions = parallelizeTransactionHandler.getTransactionsInOrder(); + short[] bucketOrder = parallelizeTransactionHandler.getTransactionsPerBucketInOrder(); + List receipts = new ArrayList<>(); + + for (Transaction tx : executedTransactions) { + receipts.add(receiptsByTx.get(tx)); + } BlockResult result = new BlockResult( block, executedTransactions, receipts, - totalGasUsed, + bucketOrder, + gasUsedInBlock, totalPaidFees, - vmTrace ? null : track.getTrie() + track.getTrie() ); profiler.stop(metric); logger.trace("End executeInternal."); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockResult.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockResult.java index bb52ec2e364..fd656b6b73b 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockResult.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockResult.java @@ -35,7 +35,7 @@ public class BlockResult { null, Collections.emptyList(), Collections.emptyList(), - 0, + new short[0], 0, Coin.ZERO, null ); @@ -49,11 +49,13 @@ public class BlockResult { // It is for optimizing switching between states. Instead of using the "stateRoot" field, // which requires regenerating the trie, using the finalState field does not. private final Trie finalState; + private final short[] txEdges; public BlockResult( Block block, List executedTransactions, List transactionReceipts, + short[] txEdges, long gasUsed, Coin paidFees, Trie finalState) { @@ -63,12 +65,15 @@ public BlockResult( this.gasUsed = gasUsed; this.paidFees = paidFees; this.finalState = finalState; + this.txEdges = txEdges; } public Block getBlock() { return block; } + public short[] getTxEdges() { return txEdges; } + public List getExecutedTransactions() { return executedTransactions; } public List getTransactionReceipts() { diff --git a/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java b/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java new file mode 100644 index 00000000000..3fb6f21d11d --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java @@ -0,0 +1,34 @@ +/* + * This file is part of RskJ + * Copyright (C) 2019 RSK Labs Ltd. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package co.rsk.core.bc; + +import org.ethereum.db.ByteArrayWrapper; +import java.util.Set; + +public interface IReadWrittenKeysTracker { + Set getTemporalReadKeys(); + + Set getTemporalWrittenKeys(); + + void addNewReadKey(ByteArrayWrapper key); + + void addNewWrittenKey(ByteArrayWrapper key); + + void clear(); +} diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java new file mode 100644 index 00000000000..d2526872bdc --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -0,0 +1,248 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package co.rsk.core.bc; + +import co.rsk.core.RskAddress; +import org.ethereum.core.Transaction; +import org.ethereum.db.ByteArrayWrapper; +import org.ethereum.vm.GasCost; + +import java.util.*; + +public class ParallelizeTransactionHandler { + private final HashMap bucketByWrittenKey; + private final HashMap> bucketByReadKey; + private final Map bucketBySender; + private final ArrayList buckets; + + public ParallelizeTransactionHandler(short buckets, long bucketGasLimit) { + this.bucketBySender = new HashMap<>(); + this.bucketByWrittenKey = new HashMap<>(); + this.bucketByReadKey = new HashMap<>(); + this.buckets = new ArrayList<>(); + for (short i = 0; i < buckets; i++){ + this.buckets.add(new TransactionBucket(i, bucketGasLimit, false)); + } + this.buckets.add(new TransactionBucket(buckets, bucketGasLimit, true)); + } + + public Optional addTransaction(Transaction tx, Set newReadKeys, Set newWrittenKeys, long gasUsedByTx) { + TransactionBucket bucketCandidate = getBucketCandidates(tx, newReadKeys, newWrittenKeys); + + if (!bucketHasAvailableGas(tx, bucketCandidate)) { + if (bucketCandidate.isSequential()) { + return Optional.empty(); + } + bucketCandidate = getSequentialBucket(); + + if (!bucketHasAvailableGas(tx, bucketCandidate)) { + return Optional.empty(); + } + } + + bucketCandidate.addTransaction(tx, gasUsedByTx); + addNewKeysToMaps(tx.getSender(), bucketCandidate, newReadKeys, newWrittenKeys); + return Optional.of(bucketCandidate.getGasUsed()); + } + + private boolean bucketHasAvailableGas(Transaction tx, TransactionBucket bucketCandidate) { + return bucketCandidate.hasGasAvailable(GasCost.toGas(tx.getGasLimit())); + } + + public Optional addRemascTransaction(Transaction tx, long gasUsedByTx) { + TransactionBucket sequentialBucket = getSequentialBucket(); + sequentialBucket.addTransaction(tx, gasUsedByTx); + return Optional.of(sequentialBucket.getGasUsed()); + } + + public long getGasUsedIn(Short bucketId) { + + if (bucketId < 0 || bucketId >= buckets.size()) { + throw new NoSuchElementException(); + } + + return this.buckets.get(bucketId).getGasUsed(); + } + + public List getTransactionsInOrder() { + List txs = new ArrayList<>(); + for (TransactionBucket bucket: this.buckets) { + txs.addAll(bucket.getTransactions()); + } + return txs; + } + + public short[] getTransactionsPerBucketInOrder() { + List bucketSizes = new ArrayList<>(); + short bucketEdges = 0; + + for (TransactionBucket bucket: this.buckets) { + if (bucket.getTransactions().isEmpty() || bucket.isSequential()) { + continue; + } + bucketEdges += bucket.getTransactions().size(); + bucketSizes.add(bucketEdges); + } + + short[] bucketOrder = new short[bucketSizes.size()]; + int i = 0; + for (Short size: bucketSizes) { + bucketOrder[i] = size; + i++; + } + + return bucketOrder; + } + + private void addNewKeysToMaps(RskAddress sender, TransactionBucket bucket, Set newReadKeys, Set newWrittenKeys) { + for (ByteArrayWrapper key : newReadKeys) { + Set bucketsAlreadyRead = bucketByReadKey.getOrDefault(key, new HashSet<>()); + bucketsAlreadyRead.add(bucket); + bucketByReadKey.put(key, bucketsAlreadyRead); + } + + if (bucket.isSequential()) { + bucketBySender.put(sender, bucket); + return; + } else { + bucketBySender.putIfAbsent(sender, bucket); + } + + for (ByteArrayWrapper key: newWrittenKeys) { + bucketByWrittenKey.putIfAbsent(key, bucket); + } + } + + private Optional getBucketBySender(Transaction tx) { + return Optional.ofNullable(bucketBySender.get(tx.getSender())); + } + + private Optional getAvailableBucketWithLessUsedGas(long txGasLimit) { + long gasUsed = Long.MAX_VALUE; + Optional bucketCandidate = Optional.empty(); + + for (TransactionBucket bucket : buckets) { + if (!bucket.isSequential() && bucket.hasGasAvailable(txGasLimit) && bucket.getGasUsed() < gasUsed) { + bucketCandidate = Optional.of(bucket); + gasUsed = bucket.getGasUsed(); + } + } + + return bucketCandidate; + } + + + private TransactionBucket getBucketCandidates(Transaction tx, Set newReadKeys, Set newWrittenKeys) { + Optional bucketCandidate = getBucketBySender(tx); + + if (bucketCandidate.isPresent() && bucketCandidate.get().isSequential()) { + return getSequentialBucket(); + } + + // read - written + for (ByteArrayWrapper newReadKey : newReadKeys) { + if (bucketByWrittenKey.containsKey(newReadKey)) { + TransactionBucket bucket = bucketByWrittenKey.get(newReadKey); + + if (bucketCandidate.isPresent() && !bucketCandidate.get().equals(bucket)) { + return getSequentialBucket(); + } else if (!bucketCandidate.isPresent()) { + bucketCandidate = Optional.of(bucket); + } + } + } + + for (ByteArrayWrapper newWrittenKey : newWrittenKeys) { + // written - written, + if (bucketByWrittenKey.containsKey(newWrittenKey)) { + TransactionBucket bucket = bucketByWrittenKey.get(newWrittenKey); + + if (bucketCandidate.isPresent() && !bucketCandidate.get().equals(bucket)) { + return getSequentialBucket(); + } else { + bucketCandidate = Optional.of(bucket); + } + } + // read - written + if (bucketByReadKey.containsKey(newWrittenKey)) { + Set readBuckets = bucketByReadKey.get(newWrittenKey); + + if (readBuckets.size() > 1) { + return getSequentialBucket(); + } + + if (bucketCandidate.isPresent() && !readBuckets.contains(bucketCandidate.get())) { + return getSequentialBucket(); + } else { + bucketCandidate = Optional.of(readBuckets.iterator().next()); + } + } + } + + return bucketCandidate.orElseGet(() -> getAvailableBucketWithLessUsedGas(GasCost.toGas(tx.getGasLimit())).orElseGet(this::getSequentialBucket)); + } + + private TransactionBucket getSequentialBucket() { + return this.buckets.get(this.buckets.size()-1); + } + + public long getGasUsedInSequential() { + return getSequentialBucket().getGasUsed(); + } + + private static class TransactionBucket { + + final Short id; + final long gasLimit; + final boolean isSequential; + final List transactions; + long gasUsedInBucket; + + public TransactionBucket(Short id, long bucketGasLimit, boolean isSequential) { + this.id = id; + this.gasLimit = bucketGasLimit; + this.isSequential = isSequential; + this.transactions = new ArrayList<>(); + this.gasUsedInBucket = 0; + } + + private void addTransaction(Transaction tx, long gasUsedByTx) { + transactions.add(tx); + gasUsedInBucket = gasUsedInBucket + gasUsedByTx; + } + + private boolean hasGasAvailable(long txGasLimit) { + //TODO(JULI): Re-check a thousand of times this line. + long cumulativeGas = GasCost.add(gasUsedInBucket, txGasLimit); + return cumulativeGas <= gasLimit; + } + + public long getGasUsed() { + return gasUsedInBucket; + } + + public List getTransactions() { + return this.transactions; + } + + public boolean isSequential() { + return isSequential; + } + } +} diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java b/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java new file mode 100644 index 00000000000..46e1b877816 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java @@ -0,0 +1,64 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package co.rsk.core.bc; + +import org.ethereum.db.ByteArrayWrapper; + +import java.util.HashSet; +import java.util.Set; + +//TODO(JULI): +// * Next step should be to check whether a key is written in the cache but also deleted in the same transaction. This key shouldn't be considered as a written key. + +public class ReadWrittenKeysTracker implements IReadWrittenKeysTracker { + private Set temporalReadKeys; + private Set temporalWrittenKeys; + + public ReadWrittenKeysTracker() { + this.temporalReadKeys = new HashSet<>(); + this.temporalWrittenKeys = new HashSet<>(); + } + + @Override + public Set getTemporalReadKeys(){ + return this.temporalReadKeys; + } + + @Override + public Set getTemporalWrittenKeys(){ + return this.temporalWrittenKeys; + } + + @Override + public void addNewReadKey(ByteArrayWrapper key) { + temporalReadKeys.add(key); + } + + @Override + public void addNewWrittenKey(ByteArrayWrapper key) { + temporalWrittenKeys.add(key); + } + + @Override + public void clear() { + this.temporalReadKeys = new HashSet<>(); + this.temporalWrittenKeys = new HashSet<>(); + + } +} diff --git a/rskj-core/src/main/java/co/rsk/db/RepositoryLocator.java b/rskj-core/src/main/java/co/rsk/db/RepositoryLocator.java index 3a3eef66fec..7d328039bc5 100644 --- a/rskj-core/src/main/java/co/rsk/db/RepositoryLocator.java +++ b/rskj-core/src/main/java/co/rsk/db/RepositoryLocator.java @@ -18,6 +18,7 @@ package co.rsk.db; +import co.rsk.core.bc.IReadWrittenKeysTracker; import co.rsk.crypto.Keccak256; import co.rsk.trie.MutableTrie; import co.rsk.trie.Trie; @@ -77,6 +78,13 @@ public Repository startTrackingAt(BlockHeader header) { .orElseThrow(() -> trieNotFoundException(header)); } + public Repository startTrackingAt(BlockHeader header, IReadWrittenKeysTracker tracker) { + return mutableTrieSnapshotAt(header) + .map(MutableTrieCache::new) + .map(mutableTrieCache -> new MutableRepository(mutableTrieCache, tracker)) + .orElseThrow(() -> trieNotFoundException(header)); + } + private IllegalArgumentException trieNotFoundException(BlockHeader header) { return new IllegalArgumentException(String.format( "The trie with root %s is missing in this store", header.getHash() diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index 2886c666894..aeb4b2657c8 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -627,4 +627,8 @@ public byte[] getUmmRoot() { } public short[] getTxExecutionListsEdges() { return this.txExecutionListsEdges; } + + public void setTxExecutionListsEdges(short[] txEdges) { + this.txExecutionListsEdges = txEdges; + } } diff --git a/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java b/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java new file mode 100644 index 00000000000..e928147c90e --- /dev/null +++ b/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java @@ -0,0 +1,42 @@ +package org.ethereum.db; + +import co.rsk.core.bc.IReadWrittenKeysTracker; + +import java.util.HashSet; +import java.util.Set; + +public class DummyReadWrittenKeysTracker implements IReadWrittenKeysTracker { + + private final HashSet temporalReadKeys; + private final HashSet temporalWrittenKeys; + + public DummyReadWrittenKeysTracker() { + this.temporalReadKeys = new HashSet<>(); + this.temporalWrittenKeys = new HashSet<>(); + } + + @Override + public Set getTemporalReadKeys() { + return temporalReadKeys; + } + + @Override + public Set getTemporalWrittenKeys() { + return temporalWrittenKeys; + } + + @Override + public void addNewReadKey(ByteArrayWrapper key) { + //Dummy tracker does not store added keys + } + + @Override + public void addNewWrittenKey(ByteArrayWrapper key) { + //Dummy tracker does not store added keys + } + + @Override + public void clear() { + //Dummy tracker does not store added keys + } +} diff --git a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java index e56c0df82c7..557740966f4 100644 --- a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java +++ b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java @@ -20,6 +20,7 @@ import co.rsk.core.Coin; import co.rsk.core.RskAddress; +import co.rsk.core.bc.IReadWrittenKeysTracker; import co.rsk.core.types.ints.Uint24; import co.rsk.crypto.Keccak256; import co.rsk.db.MutableTrieCache; @@ -46,6 +47,7 @@ public class MutableRepository implements Repository { private final TrieKeyMapper trieKeyMapper; private final MutableTrie mutableTrie; + private final IReadWrittenKeysTracker tracker; public MutableRepository(TrieStore trieStore, Trie trie) { this(new MutableTrieImpl(trieStore, trie)); @@ -54,6 +56,13 @@ public MutableRepository(TrieStore trieStore, Trie trie) { public MutableRepository(MutableTrie mutableTrie) { this.trieKeyMapper = new TrieKeyMapper(); this.mutableTrie = mutableTrie; + this.tracker = new DummyReadWrittenKeysTracker(); + } + + public MutableRepository(MutableTrie mutableTrie, IReadWrittenKeysTracker tracker) { + this.trieKeyMapper = new TrieKeyMapper(); + this.mutableTrie = mutableTrie; + this.tracker = tracker; } @Override @@ -71,13 +80,14 @@ public synchronized AccountState createAccount(RskAddress addr) { @Override public synchronized void setupContract(RskAddress addr) { byte[] prefix = trieKeyMapper.getAccountStoragePrefixKey(addr); - mutableTrie.put(prefix, ONE_BYTE_ARRAY); + internalPut(prefix, ONE_BYTE_ARRAY); } @Override public synchronized boolean isExist(RskAddress addr) { // Here we assume size != 0 means the account exists - return mutableTrie.getValueLength(trieKeyMapper.getAccountKey(addr)).compareTo(Uint24.ZERO) > 0; + byte[] accountKey = trieKeyMapper.getAccountKey(addr); + return internalGetValueLength(accountKey).compareTo(Uint24.ZERO) > 0; } @Override @@ -94,7 +104,9 @@ public synchronized AccountState getAccountState(RskAddress addr) { @Override public synchronized void delete(RskAddress addr) { - mutableTrie.deleteRecursive(trieKeyMapper.getAccountKey(addr)); + byte[] accountKey = trieKeyMapper.getAccountKey(addr); + tracker.addNewWrittenKey(new ByteArrayWrapper(accountKey)); + mutableTrie.deleteRecursive(accountKey); } @Override @@ -137,7 +149,7 @@ public synchronized BigInteger getNonce(RskAddress addr) { @Override public synchronized void saveCode(RskAddress addr, byte[] code) { byte[] key = trieKeyMapper.getCodeKey(addr); - mutableTrie.put(key, code); + internalPut(key, code); if (code != null && code.length != 0 && !isExist(addr)) { createAccount(addr); @@ -152,7 +164,7 @@ public synchronized int getCodeLength(RskAddress addr) { } byte[] key = trieKeyMapper.getCodeKey(addr); - return mutableTrie.getValueLength(key).intValue(); + return internalGetValueLength(key).intValue(); } @Override @@ -167,7 +179,7 @@ public synchronized Keccak256 getCodeHashNonStandard(RskAddress addr) { } byte[] key = trieKeyMapper.getCodeKey(addr); - Optional valueHash = mutableTrie.getValueHash(key); + Optional valueHash = internalGetValueHash(key); //Returning ZERO_HASH is the non standard implementation we had pre RSKIP169 implementation //and thus me must honor it. @@ -187,7 +199,7 @@ public synchronized Keccak256 getCodeHashStandard(RskAddress addr) { byte[] key = trieKeyMapper.getCodeKey(addr); - return mutableTrie.getValueHash(key).orElse(KECCAK_256_OF_EMPTY_ARRAY); + return internalGetValueHash(key).orElse(KECCAK_256_OF_EMPTY_ARRAY); } @Override @@ -202,13 +214,13 @@ public synchronized byte[] getCode(RskAddress addr) { } byte[] key = trieKeyMapper.getCodeKey(addr); - return mutableTrie.get(key); + return internalGet(key); } @Override public boolean isContract(RskAddress addr) { byte[] prefix = trieKeyMapper.getAccountStoragePrefixKey(addr); - return mutableTrie.get(prefix) != null; + return internalGet(prefix) != null; } @Override @@ -233,16 +245,16 @@ public synchronized void addStorageBytes(RskAddress addr, DataWord key, byte[] v // conversion here only applies if this is called directly. If suppose this only occurs in tests, but it can // also occur in precompiled contracts that store data directly using this method. if (value == null || value.length == 0) { - mutableTrie.put(triekey, null); + internalPut(triekey, null); } else { - mutableTrie.put(triekey, value); + internalPut(triekey, value); } } @Override public synchronized DataWord getStorageValue(RskAddress addr, DataWord key) { byte[] triekey = trieKeyMapper.getAccountStorageKey(addr, key); - byte[] value = mutableTrie.get(triekey); + byte[] value = internalGet(triekey); if (value == null) { return null; } @@ -253,7 +265,7 @@ public synchronized DataWord getStorageValue(RskAddress addr, DataWord key) { @Override public synchronized byte[] getStorageBytes(RskAddress addr, DataWord key) { byte[] triekey = trieKeyMapper.getAccountStorageKey(addr, key); - return mutableTrie.get(triekey); + return internalGet(triekey); } @Override @@ -311,7 +323,7 @@ public synchronized Set getAccountsKeys() { // To start tracking, a new repository is created, with a MutableTrieCache in the middle @Override public synchronized Repository startTracking() { - return new MutableRepository(new MutableTrieCache(mutableTrie)); + return new MutableRepository(new MutableTrieCache(mutableTrie), tracker); } @Override @@ -341,7 +353,7 @@ public synchronized byte[] getRoot() { @Override public synchronized void updateAccountState(RskAddress addr, final AccountState accountState) { byte[] accountKey = trieKeyMapper.getAccountKey(addr); - mutableTrie.put(accountKey, accountState.getEncoded()); + internalPut(accountKey, accountState.getEncoded()); } @VisibleForTesting @@ -367,6 +379,27 @@ private synchronized AccountState getAccountStateOrCreateNew(RskAddress addr) { } private byte[] getAccountData(RskAddress addr) { - return mutableTrie.get(trieKeyMapper.getAccountKey(addr)); + byte[] accountKey = trieKeyMapper.getAccountKey(addr); + return internalGet(accountKey); + } + + private void internalPut(byte[] key, byte[] value) { + tracker.addNewWrittenKey(new ByteArrayWrapper(key)); + mutableTrie.put(key, value); + } + + private byte[] internalGet(byte[] key) { + tracker.addNewReadKey(new ByteArrayWrapper(key)); + return mutableTrie.get(key); + } + + private Uint24 internalGetValueLength(byte[] key) { + tracker.addNewReadKey(new ByteArrayWrapper(key)); + return mutableTrie.getValueLength(key); + } + + private Optional internalGetValueHash(byte[] key) { + tracker.addNewReadKey(new ByteArrayWrapper(key)); + return mutableTrie.getValueHash(key); } } diff --git a/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java b/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java index fc37e8f97e5..372f539b81c 100644 --- a/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java +++ b/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java @@ -286,6 +286,7 @@ public Block createChildBlock(Block parent, List txs, List txs = block.getTransactionsList(); + BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); + + Assertions.assertEquals(txs, blockResult.getExecutedTransactions()); + Assertions.assertEquals(expectedAccumulatedGas, blockResult.getGasUsed()); + Assertions.assertArrayEquals(expectedEdges, blockResult.getTxEdges()); + + List transactionReceipts = blockResult.getTransactionReceipts(); + for (TransactionReceipt receipt: transactionReceipts) { + Assertions.assertEquals(expectedAccumulatedGas, GasCost.toGas(receipt.getCumulativeGas())); + } + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeSequentiallyTenIndependentTxsAndThemShouldGoInBothBuckets(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + long expectedGasUsed = 0L; + long expectedAccumulatedGas = 21000L; + short[] expectedEdges = new short[]{5, 10}; + Block parent = blockchain.getBestBlock(); + Block block = getBlockWithNIndependentTransactions(10, BigInteger.valueOf(expectedAccumulatedGas), false); + List txs = block.getTransactionsList(); + BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); + + Assertions.assertEquals(txs.size(), blockResult.getExecutedTransactions().size()); + Assertions.assertTrue(txs.containsAll(blockResult.getExecutedTransactions())); + Assertions.assertArrayEquals(expectedEdges, blockResult.getTxEdges()); + Assertions.assertEquals(expectedAccumulatedGas*10, blockResult.getGasUsed()); + + List transactionReceipts = blockResult.getTransactionReceipts(); + long accumulatedGasUsed = 0L; + short i = 0; + short edgeIndex = 0; + for (TransactionReceipt receipt: transactionReceipts) { + if ((edgeIndex < expectedEdges.length) && (i == expectedEdges[edgeIndex])) { + edgeIndex++; + accumulatedGasUsed = expectedGasUsed; + } + + accumulatedGasUsed += expectedAccumulatedGas; + Assertions.assertEquals(accumulatedGasUsed, GasCost.toGas(receipt.getCumulativeGas())); + i++; + } + + Assertions.assertEquals(i, transactionReceipts.size()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeBigIndependentTxsSequentiallyTheLastOneShouldGoToSequential(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + Block parent = blockchain.getBestBlock(); + long blockGasLimit = GasCost.toGas(parent.getGasLimit()); + int gasLimit = 21000; + int transactionNumber = (int) (blockGasLimit /gasLimit); + short[] expectedEdges = new short[]{(short) transactionNumber, (short) (transactionNumber*2)}; + int transactionsInSequential = 1; + + Block block = getBlockWithNIndependentTransactions(transactionNumber*2+transactionsInSequential, BigInteger.valueOf(gasLimit), false); + List transactionsList = block.getTransactionsList(); + BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); + + Assertions.assertArrayEquals(expectedEdges, blockResult.getTxEdges()); + Assertions.assertEquals(transactionsList.size(), blockResult.getExecutedTransactions().size()); + Assertions.assertTrue(transactionsList.containsAll(blockResult.getExecutedTransactions())); + + List transactionReceipts = blockResult.getTransactionReceipts(); + long accumulatedGasUsed = 0L; + short i = 0; + short edgeIndex = 0; + for (TransactionReceipt receipt: transactionReceipts) { + accumulatedGasUsed += gasLimit; + + if ((edgeIndex < expectedEdges.length) && (i == expectedEdges[edgeIndex])) { + edgeIndex++; + accumulatedGasUsed = gasLimit; + } + Assertions.assertEquals(accumulatedGasUsed, GasCost.toGas(receipt.getCumulativeGas())); + i++; + } + + Assertions.assertEquals(i, transactionReceipts.size()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeATxInSequentialAndBlockResultShouldTrackTheGasUsedInTheBlock(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + Block parent = blockchain.getBestBlock(); + long blockGasLimit = GasCost.toGas(parent.getGasLimit()); + int gasLimit = 21000; + int transactionNumberToFillParallelBucket = (int) (blockGasLimit / gasLimit); + int transactionsInSequential = 1; + int totalTxsNumber = transactionNumberToFillParallelBucket * 2 + transactionsInSequential; + Block block = getBlockWithNIndependentTransactions(totalTxsNumber, BigInteger.valueOf(gasLimit), false); + BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); + + Assertions.assertEquals(gasLimit*totalTxsNumber, blockResult.getGasUsed()); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void withTheBucketsFullTheLastTransactionShouldNotFit(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + Block parent = blockchain.getBestBlock(); + long blockGasLimit = GasCost.toGas(parent.getGasLimit()); + int gasLimit = 21000; + int transactionNumberToFillParallelBucket = (int) (blockGasLimit / gasLimit); + int totalTxs = (transactionNumberToFillParallelBucket) * 3 + 1; + Block block = getBlockWithNIndependentTransactions(totalTxs, BigInteger.valueOf(gasLimit), false); + BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); + Assertions.assertEquals(totalTxs, blockResult.getExecutedTransactions().size() + 1); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void withSequentialBucketFullRemascTxShouldFit(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + + doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + Block parent = blockchain.getBestBlock(); + long blockGasLimit = GasCost.toGas(parent.getGasLimit()); + int gasLimit = 21000; + int transactionNumberToFillABucket = (int) (blockGasLimit / gasLimit); + int expectedNumberOfTx = transactionNumberToFillABucket*3 + 1; + Block block = getBlockWithNIndependentTransactions(transactionNumberToFillABucket*3, BigInteger.valueOf(gasLimit), true); + BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); + Assertions.assertEquals(expectedNumberOfTx, blockResult.getExecutedTransactions().size()); + } + + @ParameterizedTest @ValueSource(booleans = {true, false}) void executeParallelBlocksWithDifferentSubsets(boolean activeRskip144) { @@ -463,13 +633,14 @@ void executeParallelBlocksWithDifferentSubsets(boolean activeRskip144) { } doReturn(true).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); Block block1 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); - BlockResult result1 = executor.execute(block1, parent.getHeader(), true); + BlockResult result1 = executor.execute(null, 0, block1, parent.getHeader(), true, false, true); Block block2 = getBlockWithTenTransactions(new short[]{5}); - BlockResult result2 = executor.execute(block2, parent.getHeader(), true); + BlockResult result2 = executor.execute(null, 0, block2, parent.getHeader(), true, false, true); Assertions.assertArrayEquals(result2.getFinalState().getHash().getBytes(), result1.getFinalState().getHash().getBytes()); } @@ -482,13 +653,14 @@ void executeParallelBlockAgainstSequentialBlock(boolean activeRskip144) { } doReturn(true).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); Block pBlock = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); - BlockResult parallelResult = executor.execute(pBlock, parent.getHeader(), true); + BlockResult parallelResult = executor.execute(null, 0, pBlock, parent.getHeader(), true, false, true); Block sBlock = getBlockWithTenTransactions(null); - BlockResult seqResult = executor.execute(sBlock, parent.getHeader(), true); + BlockResult seqResult = executor.executeForMining(sBlock, parent.getHeader(), true, false, true); Assertions.assertEquals(pBlock.getTransactionsList().size(), parallelResult.getExecutedTransactions().size()); Assertions.assertArrayEquals(seqResult.getFinalState().getHash().getBytes(), parallelResult.getFinalState().getHash().getBytes()); @@ -502,6 +674,7 @@ void executeParallelBlockTwice(boolean activeRskip144) { } doReturn(true).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); Block block1 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); BlockResult result1 = executor.executeAndFill(block1, parent.getHeader()); @@ -524,16 +697,16 @@ void validateStateRootWithRskip126DisabledAndValidStateRoot(boolean activeRskip1 Block block = new BlockGenerator(Constants.regtest(), activationConfig).getBlock(1); block.setStateRoot(trie.getHash().getBytes()); - BlockResult blockResult = new BlockResult(block, Collections.emptyList(), Collections.emptyList(), 0, + BlockResult blockResult = new BlockResult(block, Collections.emptyList(), Collections.emptyList(), new short[0], 0, Coin.ZERO, trie); - RskSystemProperties cfg = spy(CONFIG); +// RskSystemProperties cfg = spy(CONFIG); ActivationConfig activationConfig = spy(cfg.getActivationConfig()); doReturn(false).when(activationConfig).isActive(eq(RSKIP126), anyLong()); doReturn(activationConfig).when(cfg).getActivationConfig(); - BlockExecutor executor = buildBlockExecutor(trieStore, cfg); + BlockExecutor executor = buildBlockExecutor(trieStore, cfg, activeRskip144, false); short[] expectedEdges = activeRskip144 ? new short[0] : null; @@ -551,16 +724,16 @@ void validateStateRootWithRskip126DisabledAndInvalidStateRoot(boolean activeRski Block block = new BlockGenerator(Constants.regtest(), activationConfig).getBlock(1); block.setStateRoot(new byte[] { 1, 2, 3, 4 }); - BlockResult blockResult = new BlockResult(block, Collections.emptyList(), Collections.emptyList(), 0, + BlockResult blockResult = new BlockResult(block, Collections.emptyList(), Collections.emptyList(), new short[0], 0, Coin.ZERO, trie); - RskSystemProperties cfg = spy(CONFIG); +// RskSystemProperties cfg = spy(CONFIG); - ActivationConfig activationConfig = spy(cfg.getActivationConfig()); - doReturn(false).when(activationConfig).isActive(eq(RSKIP126), anyLong()); - doReturn(activationConfig).when(cfg).getActivationConfig(); +// ActivationConfig activationConfig = spy(cfg.getActivationConfig()); + boolean rskip126IsActive = false; +// doReturn(activationConfig).when(cfg).getActivationConfig(); - BlockExecutor executor = buildBlockExecutor(trieStore, cfg); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, rskip126IsActive); short[] expectedEdges = activeRskip144 ? new short[0] : null; @@ -572,12 +745,12 @@ void validateStateRootWithRskip126DisabledAndInvalidStateRoot(boolean activeRski @ValueSource(booleans = {true, false}) void validateBlock(boolean activeRskip144) { doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); - TestObjects objects = generateBlockWithOneTransaction(); + TestObjects objects = generateBlockWithOneTransaction(activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = objects.getParent(); Block block = objects.getBlock(); - BlockExecutor executor = buildBlockExecutor(objects.getTrieStore()); + BlockExecutor executor = buildBlockExecutor(objects.getTrieStore(), activeRskip144, RSKIP_126_IS_ACTIVE); - short[] expectedEdges = activeRskip144 ? new short[0] : null; + short[] expectedEdges = activeRskip144 ? new short[]{(short) block.getTransactionsList().size()} : null; Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertTrue(executor.executeAndValidate(block, parent.getHeader())); @@ -587,14 +760,14 @@ void validateBlock(boolean activeRskip144) { @ValueSource(booleans = {true, false}) void invalidBlockBadStateRoot(boolean activeRskip144) { doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); - TestObjects objects = generateBlockWithOneTransaction(); + TestObjects objects = generateBlockWithOneTransaction(activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = objects.getParent(); Block block = objects.getBlock(); - BlockExecutor executor = buildBlockExecutor(objects.getTrieStore()); + BlockExecutor executor = buildBlockExecutor(objects.getTrieStore(), activeRskip144, RSKIP_126_IS_ACTIVE); byte[] stateRoot = block.getStateRoot(); stateRoot[0] = (byte) ((stateRoot[0] + 1) % 256); - short[] expectedEdges = activeRskip144 ? new short[0] : null; + short[] expectedEdges = activeRskip144 ? new short[]{(short) block.getTransactionsList().size()} : null; Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); @@ -604,14 +777,14 @@ void invalidBlockBadStateRoot(boolean activeRskip144) { @ValueSource(booleans = {true, false}) void invalidBlockBadReceiptsRoot(boolean activeRskip144) { doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); - TestObjects objects = generateBlockWithOneTransaction(); + TestObjects objects = generateBlockWithOneTransaction(activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = objects.getParent(); Block block = objects.getBlock(); - BlockExecutor executor = buildBlockExecutor(objects.getTrieStore()); + BlockExecutor executor = buildBlockExecutor(objects.getTrieStore(), activeRskip144, RSKIP_126_IS_ACTIVE); byte[] receiptsRoot = block.getReceiptsRoot(); receiptsRoot[0] = (byte) ((receiptsRoot[0] + 1) % 256); - short[] expectedEdges = activeRskip144 ? new short[0] : null; + short[] expectedEdges = activeRskip144 ? new short[]{(short) block.getTransactionsList().size()} : null; Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); @@ -621,13 +794,13 @@ void invalidBlockBadReceiptsRoot(boolean activeRskip144) { @ValueSource(booleans = {true, false}) void invalidBlockBadGasUsed(boolean activeRskip144) { doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); - TestObjects objects = generateBlockWithOneTransaction(); + TestObjects objects = generateBlockWithOneTransaction(activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = objects.getParent(); Block block = objects.getBlock(); - BlockExecutor executor = buildBlockExecutor(objects.getTrieStore()); + BlockExecutor executor = buildBlockExecutor(objects.getTrieStore(), activeRskip144, RSKIP_126_IS_ACTIVE); block.getHeader().setGasUsed(0); - short[] expectedEdges = activeRskip144 ? new short[0] : null; + short[] expectedEdges = activeRskip144 ? new short[]{(short) block.getTransactionsList().size()} : null; Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); @@ -637,13 +810,13 @@ void invalidBlockBadGasUsed(boolean activeRskip144) { @ValueSource(booleans = {true, false}) void invalidBlockBadPaidFees(boolean activeRskip144) { doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); - TestObjects objects = generateBlockWithOneTransaction(); + TestObjects objects = generateBlockWithOneTransaction(activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = objects.getParent(); Block block = objects.getBlock(); - BlockExecutor executor = buildBlockExecutor(objects.getTrieStore()); + BlockExecutor executor = buildBlockExecutor(objects.getTrieStore(), activeRskip144, RSKIP_126_IS_ACTIVE); block.getHeader().setPaidFees(Coin.ZERO); - short[] expectedEdges = activeRskip144 ? new short[0] : null; + short[] expectedEdges = activeRskip144 ? new short[]{(short) block.getTransactionsList().size()} : null; Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); @@ -653,20 +826,20 @@ void invalidBlockBadPaidFees(boolean activeRskip144) { @ValueSource(booleans = {true, false}) void invalidBlockBadLogsBloom(boolean activeRskip144) { doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); - TestObjects objects = generateBlockWithOneTransaction(); + TestObjects objects = generateBlockWithOneTransaction(activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = objects.getParent(); Block block = objects.getBlock(); - BlockExecutor executor = buildBlockExecutor(objects.getTrieStore()); + BlockExecutor executor = buildBlockExecutor(objects.getTrieStore(), activeRskip144, RSKIP_126_IS_ACTIVE); byte[] logBloom = block.getLogBloom(); logBloom[0] = (byte) ((logBloom[0] + 1) % 256); - short[] expectedEdges = activeRskip144 ? new short[0] : null; + short[] expectedEdges = activeRskip144 ? new short[]{(short) block.getTransactionsList().size()} : null; Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionListsEdges()); Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); } - private static TestObjects generateBlockWithOneTransaction() { + private static TestObjects generateBlockWithOneTransaction(Boolean activeRskip144, boolean rskip126IsActive) { TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Repository repository = new MutableRepository(trieStore, new Trie(trieStore)); @@ -679,7 +852,7 @@ private static TestObjects generateBlockWithOneTransaction() { Assertions.assertFalse(Arrays.equals(EMPTY_TRIE_HASH, repository.getRoot())); - BlockExecutor executor = buildBlockExecutor(trieStore); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, rskip126IsActive); Transaction tx1 = Transaction .builder() @@ -832,6 +1005,53 @@ private Block getBlockWithTenTransactions(short[] edges) { ); } + private Block getBlockWithNIndependentTransactions(int number, BigInteger txGasLimit, boolean withRemasc) { + int nTxs = number; + int nAccounts = nTxs * 2; + Repository track = repository.startTracking(); + List accounts = new LinkedList<>(); + + for (int i = 0; i < nAccounts; i++) { + accounts.add(createAccount("accounttest" + i, track, Coin.valueOf(600000))); + } + track.commit(); + Block bestBlock = blockchain.getBestBlock(); + bestBlock.setStateRoot(repository.getRoot()); + + List txs = new LinkedList<>(); + + for (int i = 0; i < nTxs; i++) { + Transaction tx = Transaction.builder() + .nonce(BigInteger.ZERO) + .gasPrice(BigInteger.ONE) + .gasLimit(txGasLimit) + .destination(accounts.get(i + nTxs).getAddress()) + .chainId(CONFIG.getNetworkConstants().getChainId()) + .value(BigInteger.TEN) + .build(); + tx.sign(accounts.get(i).getEcKey().getPrivKeyBytes()); + txs.add(tx); + } + + if (withRemasc) { + txs.add(new RemascTransaction(1L)); + } + + List uncles = new ArrayList<>(); + + return new BlockGenerator(Constants.regtest(), activationConfig) + .createChildBlock( + bestBlock, + txs, + uncles, + 1, + null, + bestBlock.getGasLimit(), + bestBlock.getCoinbase(), + null + ); + } + public static Account createAccount(String seed, Repository repository, Coin balance) { Account account = createAccount(seed); repository.createAccount(account.getAddress()); @@ -849,32 +1069,36 @@ public static Account createAccount(String seed) { ////////////////////////////////////////////// // Testing strange Txs ///////////////////////////////////////////// - @Test - void executeBlocksWithOneStrangeTransactions1() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeBlocksWithOneStrangeTransactions1(Boolean activeRskip144) { // will fail to create an address that is not 20 bytes long - Assertions.assertThrows(RuntimeException.class, () -> generateBlockWithOneStrangeTransaction(0)); + Assertions.assertThrows(RuntimeException.class, () -> generateBlockWithOneStrangeTransaction(0, activeRskip144)); } - @Test - void executeBlocksWithOneStrangeTransactions2() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeBlocksWithOneStrangeTransactions2(Boolean activeRskip144) { // will fail to create an address that is not 20 bytes long - Assertions.assertThrows(RuntimeException.class, () -> generateBlockWithOneStrangeTransaction(1)); + Assertions.assertThrows(RuntimeException.class, () -> generateBlockWithOneStrangeTransaction(1, activeRskip144)); } - @Test - void executeBlocksWithOneStrangeTransactions3() { + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeBlocksWithOneStrangeTransactions3(Boolean activeRskip144) { // the wrongly-encoded value parameter will be re-encoded with the correct serialization and won't fail - executeBlockWithOneStrangeTransaction(false, false, generateBlockWithOneStrangeTransaction(2)); + executeBlockWithOneStrangeTransaction(false, false, generateBlockWithOneStrangeTransaction(2, activeRskip144), activeRskip144); } private void executeBlockWithOneStrangeTransaction( boolean mustFailValidation, boolean mustFailExecution, - TestObjects objects) { + TestObjects objects, + Boolean activeRskip144) { Block parent = objects.getParent(); Block block = objects.getBlock(); TrieStore trieStore = objects.getTrieStore(); - BlockExecutor executor = buildBlockExecutor(trieStore); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Repository repository = new MutableRepository(trieStore, trieStore.retrieve(objects.getParent().getStateRoot()).get()); Transaction tx = objects.getTransaction(); @@ -892,7 +1116,7 @@ private void executeBlockWithOneStrangeTransaction( return; } - BlockResult result = executor.execute(block, parent.getHeader(), false); + BlockResult result = executor.executeForMining(block, parent.getHeader(), false, false, true); Assertions.assertNotNull(result); if (mustFailExecution) { @@ -932,7 +1156,7 @@ private void executeBlockWithOneStrangeTransaction( Assertions.assertEquals(BigInteger.valueOf(30000 - 21000 - 10), accountState.getBalance().asBigInteger()); } - public TestObjects generateBlockWithOneStrangeTransaction(int strangeTransactionType) { + public TestObjects generateBlockWithOneStrangeTransaction(int strangeTransactionType, Boolean activeRskip144) { TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Repository repository = new MutableRepository(trieStore, new Trie(trieStore)); Repository track = repository.startTracking(); @@ -944,7 +1168,7 @@ public TestObjects generateBlockWithOneStrangeTransaction(int strangeTransaction Assertions.assertFalse(Arrays.equals(EMPTY_TRIE_HASH, repository.getRoot())); - BlockExecutor executor = buildBlockExecutor(trieStore); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); List txs = new ArrayList<>(); Transaction tx = createStrangeTransaction( @@ -1013,29 +1237,35 @@ private static byte[] sha3(byte[] input) { return digest.digest(); } - private static BlockExecutor buildBlockExecutor(TrieStore store) { - return buildBlockExecutor(store, CONFIG); + private static BlockExecutor buildBlockExecutor(TrieStore store, Boolean activeRskip144, boolean rskip126IsActive) { + return buildBlockExecutor(store, CONFIG, activeRskip144, rskip126IsActive); } - private static BlockExecutor buildBlockExecutor(TrieStore store, RskSystemProperties config) { - StateRootHandler stateRootHandler = new StateRootHandler(config.getActivationConfig(), new StateRootsStoreImpl(new HashMapDB())); + private static BlockExecutor buildBlockExecutor(TrieStore store, RskSystemProperties config, Boolean activeRskip144, Boolean activeRskip126) { + RskSystemProperties cfg = spy(config); + doReturn(activationConfig).when(cfg).getActivationConfig(); + doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); + doReturn(activeRskip126).when(activationConfig).isActive(eq(RSKIP126), anyLong()); + + + StateRootHandler stateRootHandler = new StateRootHandler(cfg.getActivationConfig(), new StateRootsStoreImpl(new HashMapDB())); Factory btcBlockStoreFactory = new RepositoryBtcBlockStoreWithCache.Factory( - config.getNetworkConstants().getBridgeConstants().getBtcParams()); + cfg.getNetworkConstants().getBridgeConstants().getBtcParams()); BridgeSupportFactory bridgeSupportFactory = new BridgeSupportFactory( - btcBlockStoreFactory, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig()); + btcBlockStoreFactory, cfg.getNetworkConstants().getBridgeConstants(), cfg.getActivationConfig()); return new BlockExecutor( - config.getActivationConfig(), + cfg.getActivationConfig(), new RepositoryLocator(store, stateRootHandler), new TransactionExecutorFactory( - config, + cfg, null, null, BLOCK_FACTORY, new ProgramInvokeFactoryImpl(), - new PrecompiledContracts(config, bridgeSupportFactory), + new PrecompiledContracts(cfg, bridgeSupportFactory), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) ) ); diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java new file mode 100644 index 00000000000..9f4bf89a356 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -0,0 +1,728 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package co.rsk.core.bc; + +import co.rsk.test.builders.AccountBuilder; +import co.rsk.test.builders.TransactionBuilder; +import org.ethereum.core.Account; +import org.ethereum.core.Transaction; +import org.ethereum.db.ByteArrayWrapper; +import org.ethereum.vm.GasCost; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.util.*; + + +public class ParallelizeTransactionHandlerTest { + + private short buckets; + private ParallelizeTransactionHandler handler; + private Transaction tx; + private Transaction tx2; + private Transaction tx3; + private ByteArrayWrapper aWrappedKey; + private ByteArrayWrapper aDifferentWrapperKey; + private Transaction bigTx; + private Transaction bigTx2; + private short sequentialBucketNumber; + + @BeforeEach + public void setup() { + Account sender = new AccountBuilder().name("sender").build(); + Account sender2 = new AccountBuilder().name("sender2").build(); + Account sender3 = new AccountBuilder().name("sender3").build(); + Account sender4 = new AccountBuilder().name("sender4").build(); + Account sender5 = new AccountBuilder().name("sender5").build(); + byte[] aKey = {1, 2, 3}; + byte[] aDifferentKey = {1, 2, 3, 4}; + int blockGasLimit = 6800000; + long gasUsedByTx = 16000; + long biggestGasLimitPossibleInBucket = blockGasLimit - 1; + + aWrappedKey = new ByteArrayWrapper(aKey); + buckets = 2; + sequentialBucketNumber = buckets; + handler = new ParallelizeTransactionHandler(buckets, blockGasLimit); + tx = new TransactionBuilder().nonce(1).sender(sender).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); + tx2 = new TransactionBuilder().nonce(1).sender(sender2).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); + tx3 = new TransactionBuilder().nonce(1).sender(sender3).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); + bigTx = new TransactionBuilder().nonce(1).sender(sender4).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInBucket)).value(BigInteger.valueOf(1)).build(); + bigTx2 = new TransactionBuilder().nonce(1).sender(sender5).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInBucket)).value(BigInteger.valueOf(1)).build(); + aDifferentWrapperKey = new ByteArrayWrapper(aDifferentKey); + } + + @Test + void createAHandlerShouldReturnAnEmptyTransactionList() { + int expectedNumberOfTxs = 0; + int expectedNumberOfTxsInBuckets = 0; + + Assertions.assertEquals(expectedNumberOfTxs, handler.getTransactionsInOrder().size()); + Assertions.assertEquals(expectedNumberOfTxsInBuckets, handler.getTransactionsPerBucketInOrder().length); + } + + @Test + void createAHandlerAndGasUsedInBucketShouldBeZero() { + int expectedGasUsed = 0; + for (short i = 0; i < buckets; i++) { + Assertions.assertEquals(expectedGasUsed, handler.getGasUsedIn(i)); + } + } + + @Test + void addTransactionIntoTheHandlerAndShouldBeAddedInTheFirstParallelBucket() { + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), 0); + short[] expectedTransactionEdgeList = new short[]{1}; + long expectedGasUsed = 0; + + Assertions.assertTrue(bucketGasUsed.isPresent()); + Assertions.assertEquals(expectedGasUsed, (long) bucketGasUsed.get()); + + List expectedListOfTxs = new ArrayList<>(); + expectedListOfTxs.add(tx); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void addTransactionIntoTheHandlerAndShouldBeSubtractedGasUsedInTheBucket() { + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + + Assertions.assertTrue(bucketGasUsed.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + } + + @Test + void addTwoTransactionsWithTheSameReadKeyAndShouldBeAddedInDifferentBuckets() { + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + short[] expectedTransactionEdgeList = new short[]{1, 2}; + + Set readKeys = createASetAndAddKeys(aWrappedKey); + + Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys, new HashSet<>(), gasUsedByTx); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + } + + @Test + void addTwoTransactionsWithDifferentReadKeysShouldBeAddedInDifferentBuckets() { + short[] expectedTransactionEdgeList = new short[]{1, 2}; + + HashSet readKeys = createASetAndAddKeys(aWrappedKey); + HashSet readKeys2 = createASetAndAddKeys(aDifferentWrapperKey); + + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx2); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + } + + @Test + void addTwoTransactionsWithSameWrittenKeysShouldBeAddedInTheSameBucket() { + short[] expectedTransactionEdgeList = new short[]{2}; + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx+gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + } + + @Test + void addTwoTransactionsWithDifferentWrittenKeysShouldBeAddedInDifferentBuckets() { + short[] expectedTransactionEdgeList = new short[]{1, 2}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrapperKey); + + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx2); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + } + + @Test + void addTwoTransactionsWithTheSameWrittenReadKeyShouldBeAddedInTheSameBucket() { + short[] expectedTransactionEdgeList = new short[]{2}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet readKeys = createASetAndAddKeys(aWrappedKey); + + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys, new HashSet<>(), gasUsedByTx2); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx+gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + } + + @Test + void addTwoTransactionsWithTheSameReadWrittenKeyShouldBeAddedInTheSameBucket() { + short[] expectedTransactionEdgeList = new short[]{2}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet readKeys = createASetAndAddKeys(aWrappedKey); + + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx+gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + } + + @Test + void addTwoTransactionsWithDifferentReadWrittenKeysShouldBeAddedInDifferentBuckets() { + short[] expectedTransactionEdgeList = new short[]{1,2}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet readKeys = createASetAndAddKeys(aDifferentWrapperKey); + + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + } + + @Test + void addTwoTransactionWithDifferentWrittenReadKeyShouldBeAddedInDifferentBuckets() { + short[] expectedTransactionEdgeList = new short[]{1, 2}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet readKeys = createASetAndAddKeys(aDifferentWrapperKey); + + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(tx, writtenKeys, new HashSet<>(), gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys, new HashSet<>(), gasUsedByTx2); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + } + + @Test + void addTwoIndependentTxsAndAThirdOneCollidingWithBothAndShouldBeAddedInTheSequential() { + short[] expectedTransactionEdgeList = new short[]{1, 2}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet differentWrittenKeys = createASetAndAddKeys(aDifferentWrapperKey); + + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), differentWrittenKeys, gasUsedByTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(tx3, differentWrittenKeys, writtenKeys, gasUsedByTx3); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx3, (long) bucketGasUsed3.get()); + Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialBucketNumber)); + + List expectedListOfTxs = Arrays.asList(tx, tx2, tx3); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void addTwoDependentTxsWithTheSecondInSequentialAndAThirdOneCollidingWithBothAndShouldBeAddedInTheSequential() { + short[] expectedTransactionEdgeList = new short[]{1}; + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + + long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); + long totalGasInSequential = gasUsedByTx2 + gasUsedByTx3; + + + Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + Optional bucketGasUsed3 = handler.addTransaction(tx3, new HashSet<>(), writtenKeys, gasUsedByTx3); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(totalGasInSequential, (long) bucketGasUsed3.get()); + Assertions.assertEquals(totalGasInSequential, handler.getGasUsedIn(sequentialBucketNumber)); + + List expectedListOfTxs = Arrays.asList(bigTx, tx2, tx3); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void addABigTransactionAndAnotherWithTheSameWrittenKeyAndTheLastOneShouldGoToSequential() { + short[] expectedTransactionEdgeList = new short[]{1}; + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + + long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed2 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(bigTx, tx, expectedTransactionEdgeList); + } + + @Test + void addABigTxAndAnotherWithTheSameReadWrittenKeyAndShouldGoToSequential() { + short[] expectedTransactionEdgeList = new short[]{1}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet readKeys = createASetAndAddKeys(aWrappedKey); + + long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(bigTx, readKeys, new HashSet<>(), gasUsedByBigTx); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed2 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(bigTx, tx, expectedTransactionEdgeList); + } + + @Test + void addABigTxAndAnotherWithTheSameWrittenReadKeyAndShouldGoToSequential() { + short[] expectedTransactionEdgeList = new short[]{1}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet readKeys = createASetAndAddKeys(aWrappedKey); + + long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed2 = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(bigTx, tx, expectedTransactionEdgeList); + } + + @Test + void addTwoTransactionsWithTheSameSenderToTheSequentialBucketAndTheSecondShouldBeAddedCorrectly() { + short[] expectedTransactionEdgeList = new short[]{1,2}; + List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, tx, tx); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + + handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), GasCost.toGas(bigTx.getGasLimit())); + handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), GasCost.toGas(bigTx2.getGasLimit())); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Assertions.assertTrue(bucketGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed3.get()); + + Optional bucketGasUsed4 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Assertions.assertTrue(bucketGasUsed4.isPresent()); + Assertions.assertEquals(2*gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(2*gasUsedByTx, (long) bucketGasUsed4.get()); + + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void twoTransactionWithTheSameSenderShouldBeInTheSameBucket() { + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + short[] expectedTransactionEdgeList = new short[]{2}; + + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(2*gasUsedByTx, (long) bucketGasUsed2.get()); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx, expectedTransactionEdgeList); + } + + @Test + void ifATxHasTheSameSenderThatAnotherAlreadyAddedIntoTheSequentialShouldGoToTheSequential() { + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); + short[] expectedTransactionEdgeList = new short[]{1,2}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrapperKey); + HashSet readKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); + + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx); + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(tx3, readKeys, new HashSet<>(), gasUsedByTx3); + Optional bucketGasUsed4 = handler.addTransaction(tx3, new HashSet<>(), new HashSet<>(), gasUsedByTx3); + Assertions.assertTrue(bucketGasUsed3.isPresent() && bucketGasUsed4.isPresent()); + Assertions.assertEquals(gasUsedByTx3*2, handler.getGasUsedIn(sequentialBucketNumber)); + + List expectedListOfTxs = Arrays.asList(tx, tx2, tx3, tx3); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void ifATxReadTwoDifferentWrittenKeysShouldGoToSequential() { + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); + short[] expectedTransactionEdgeList = new short[]{1,2}; + + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrapperKey); + HashSet readKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); + + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx); + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(tx3, readKeys, new HashSet<>(), gasUsedByTx3); + Assertions.assertTrue(bucketGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialBucketNumber)); + + List expectedListOfTxs = Arrays.asList(tx, tx2, tx3); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void ifATxWritesAKeyAlreadyReadByTwoTxsPlacedInDifferentBucketsShouldGoToTheSequential() { + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); + short[] expectedTransactionEdgeList = new short[]{1,2}; + + HashSet readKeys = createASetAndAddKeys(aWrappedKey); + HashSet readKeys2 = createASetAndAddKeys(aWrappedKey); + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + + Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx); + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(tx3, new HashSet<>(), writtenKeys, gasUsedByTx3); + Assertions.assertTrue(bucketGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialBucketNumber)); + + List expectedListOfTxs = Arrays.asList(tx, tx2, tx3); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void ifATxReadTwoKeysThatAreInDifferentBucketsShouldGoToTheSequential() { + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); + short[] expectedTransactionEdgeList = new short[]{1,2}; + + HashSet readKeys = createASetAndAddKeys(aWrappedKey); + HashSet readKeys2 = createASetAndAddKeys(aDifferentWrapperKey); + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); + + Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx); + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(tx3, new HashSet<>(), writtenKeys, gasUsedByTx3); + Assertions.assertTrue(bucketGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialBucketNumber)); + + List expectedListOfTxs = Arrays.asList(tx, tx2, tx3); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void ifATxCollidesWithAnotherOneThatAlsoHasTheSameSenderShouldGoIntoTheSameBucket() { + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + short[] expectedTransactionEdgeList = new short[]{2}; + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(2*gasUsedByTx, (long) bucketGasUsed2.get()); + assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx, expectedTransactionEdgeList); + } + + @Test + void ifATransactionHasAnAlreadyAddedSenderButCollidesWithAnotherTxShouldBeAddedIntoTheSequential() { + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); + short[] expectedTransactionEdgeList = new short[]{1,2}; + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + + Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + + List expectedListOfTxs = Arrays.asList(tx, tx2, tx); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void ifANewTxComesAndAllThePossibleBucketsAreFullTheTxShouldNotBeAdded() { + long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + short[] expectedTransactionEdgeList = new short[]{1,2}; + + List expectedListOfTxs = new ArrayList<>(); + expectedListOfTxs.add(bigTx); + expectedListOfTxs.add(bigTx2); + expectedListOfTxs.add(bigTx); + + Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional bucketGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional bucketGasUsed4 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + + Assertions.assertFalse(bucketGasUsed4.isPresent()); + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); + + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByBigTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed3.get()); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void ifBucketsAreFullAndAnIndependentTxComesShouldBeAddedInTheSequential() { + short[] expectedTransactionEdgeList = new short[]{1,2}; + + List expectedListOfTxs = new ArrayList<>(); + expectedListOfTxs.add(bigTx); + expectedListOfTxs.add(bigTx2); + expectedListOfTxs.add(tx); + + long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional bucketGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); + + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByBigTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed3.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void ifAllTheBucketsAreFullTheNewIndependentTxShouldNotBeIncluded() { + short[] expectedTransactionEdgeList = new short[]{1,2}; + List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, bigTx); + + long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); + + Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional bucketGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Assertions.assertTrue(bucketGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed3.get()); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional emptyBucket = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), GasCost.toGas(tx.getGasLimit())); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertFalse(emptyBucket.isPresent()); + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByBigTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void ifAllTheBucketsAreFullTheNewDependentTxShouldNotBeIncluded() { + short[] expectedTransactionEdgeList = new short[]{1,2}; + List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, bigTx); + + long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); + + Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); + Optional bucketGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional bucketGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Assertions.assertTrue(bucketGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed3.get()); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); + + Optional emptyBucket = handler.addTransaction(tx, new HashSet<>(), writtenKeys, GasCost.toGas(tx.getGasLimit())); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertFalse(emptyBucket.isPresent()); + Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); + Assertions.assertEquals(gasUsedByBigTx2, (long) bucketGasUsed2.get()); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } + + @Test + void aRemascTxAddedShouldBeInTheSequentialBucket() { + List expectedListOfTxs = Collections.singletonList(tx); + long gasUsedByTx = GasCost.toGas(bigTx.getGasLimit()); + + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sequentialBucketGasUsed = handler.addRemascTransaction(tx, gasUsedByTx); + + Assertions.assertTrue(sequentialBucketGasUsed.isPresent()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(gasUsedByTx, (long) sequentialBucketGasUsed.get()); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + } + + @Test + void ifItsSequentialTheEdgesListShouldHaveSizeZero() { + handler.addRemascTransaction(tx, GasCost.toGas(bigTx.getGasLimit())); + Assertions.assertEquals(0, handler.getTransactionsPerBucketInOrder().length); + } + + @Test + void callGetGasUsedInWithAnInvalidBucketShouldThrowAnError() { + short invalidBucketId = (short) (buckets+1); + try { + handler.getGasUsedIn(invalidBucketId); + Assertions.fail(); + } catch (NoSuchElementException e) { + Assertions.assertTrue(true); + } + } + + @Test + void callGetGasUsedInWithAnInvalidBucketShouldThrowAnError2() { + short invalidBucketId = -1; + try { + handler.getGasUsedIn(invalidBucketId); + Assertions.fail(); + } catch (NoSuchElementException e) { + Assertions.assertTrue(true); + } + } + private HashSet createASetAndAddKeys(ByteArrayWrapper... aKey) { + return new HashSet<>(Arrays.asList(aKey)); + } + + private void assertTwoTransactionsWereAddedProperlyIntoTheBuckets(Transaction tx, Transaction tx2, short[] expectedTransactionEdgeList) { + List expectedListOfTxs = Arrays.asList(tx, tx2); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + } +} diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java new file mode 100644 index 00000000000..c9103ded52d --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java @@ -0,0 +1,138 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package co.rsk.core.bc; + +import org.ethereum.db.ByteArrayWrapper; +import org.ethereum.db.DummyReadWrittenKeysTracker; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Set; + + +public class ReadWrittenKeysTrackerTest { + + private IReadWrittenKeysTracker tracker; + private IReadWrittenKeysTracker dummyTracker; + private ByteArrayWrapper key1; + private ByteArrayWrapper key2; + + + @BeforeEach + void setup() { + this.tracker = new ReadWrittenKeysTracker(); + this.dummyTracker = new DummyReadWrittenKeysTracker(); + this.key1 = new ByteArrayWrapper(new byte[]{1}); + this.key2 = new ByteArrayWrapper(new byte[]{2}); + } + + @Test + void createATrackerShouldHaveEmptyMaps() { + Assertions.assertEquals(0, tracker.getTemporalReadKeys().size()); + Assertions.assertEquals(0, tracker.getTemporalWrittenKeys().size()); + } + + @Test + void addReadKeyToTheTrackerAndShouldBeInReadMap() { + tracker.addNewReadKey(key1); + Set temporalReadKeys = tracker.getTemporalReadKeys(); + assertKeyWasAddedInMap(temporalReadKeys, key1); + } + + @Test + void addReadKeyToTheTrackerAndShouldntBeInWrittenMap() { + tracker.addNewReadKey(key1); + Assertions.assertEquals(0, tracker.getTemporalWrittenKeys().size()); + } + + @Test + void addWrittenKeyToTheTrackerAndShouldBeInWrittenMap() { + tracker.addNewWrittenKey(key1); + Set temporalWrittenKeys = tracker.getTemporalWrittenKeys(); + assertKeyWasAddedInMap(temporalWrittenKeys, key1); + } + + @Test + void addWrittenKeyToTheTrackerAndShouldntBeInReadMap() { + tracker.addNewWrittenKey(key1); + Assertions.assertEquals(0, tracker.getTemporalReadKeys().size()); + } + + @Test + void clearTrackerShouldEmptyTheMaps() { + tracker.addNewWrittenKey(key1); + tracker.addNewReadKey(key1); + tracker.addNewWrittenKey(key2); + tracker.addNewReadKey(key2); + + Assertions.assertEquals(2, tracker.getTemporalReadKeys().size()); + Assertions.assertEquals(2, tracker.getTemporalWrittenKeys().size()); + + tracker.clear(); + + Assertions.assertEquals(0, tracker.getTemporalReadKeys().size()); + Assertions.assertEquals(0, tracker.getTemporalWrittenKeys().size()); + } + + @Test + void createADummyTrackerShouldHaveEmptyMaps() { + Assertions.assertEquals(0, dummyTracker.getTemporalReadKeys().size()); + Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + } + + @Test + void addReadKeyToTheDummyTrackerShouldDoNothing() { + dummyTracker.addNewReadKey(key1); + Assertions.assertEquals(0, dummyTracker.getTemporalReadKeys().size()); + } + + @Test + void addReadKeyToTheTrackerShouldDoNothing() { + dummyTracker.addNewReadKey(key1); + Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + } + + @Test + void addWrittenKeyToTheDummyTrackerShouldDoNothing() { + dummyTracker.addNewWrittenKey(key1); + Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + } + + @Test + void clearDummyTrackerShouldDoNothing() { + dummyTracker.addNewWrittenKey(key1); + dummyTracker.addNewReadKey(key1); + dummyTracker.addNewWrittenKey(key2); + dummyTracker.addNewReadKey(key2); + + Assertions.assertEquals(0, dummyTracker.getTemporalReadKeys().size()); + Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + + dummyTracker.clear(); + + Assertions.assertEquals(0, dummyTracker.getTemporalReadKeys().size()); + Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + } + + private void assertKeyWasAddedInMap(Set map, ByteArrayWrapper key) { + Assertions.assertEquals(1, map.size()); + Assertions.assertTrue(map.contains(key)); + } +} diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java index 040582ed148..7d121ccab62 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java @@ -106,7 +106,7 @@ class Web3ImplLogsTest { private static final String ONE_TOPIC = "0000000000000000000000000000000000000000000000000000000000000001"; private static final String INCREMENT_METHOD_SIGNATURE = "371303c0"; private static final String GET_VALUE_METHOD_SIGNATURE = "20965255"; - private static final String TRACKED_TEST_BLOCK_HASH = "0x5fcfdf1c5c83850e4e4094124a1d7a314b8684055ed4577a02abf5bc438096f7"; + private static final String TRACKED_TEST_BLOCK_HASH = "0x1d3137d39f8467053020d1521019d23d33e3a03e92f296f4a0e8ed12b2891ae7"; private static final String UNTRACKED_TEST_BLOCK_HASH = "0xdea168a4f74e51a3eeb6d72b049c4fc7bc750dd51f13a3afa4fee4bece0e85eb"; private final TestSystemProperties config = new TestSystemProperties(); private Blockchain blockChain; From 9516448bd89c5a5146c3070602944bab06ec4613 Mon Sep 17 00:00:00 2001 From: julianlen Date: Wed, 15 Jun 2022 15:34:51 -0300 Subject: [PATCH 11/89] Research/parallel-tx/block validation (#1804) * Added thread logic in the Tracker so it controls whether there is a key collision between threads * Clear tracker between parallel and sequential execution within the parallel block execution * Tracker is now atomic * Added tests to test for race conditions * Tracker returns a copy of the maps. --- .../co/rsk/core/TransactionListExecutor.java | 8 + .../java/co/rsk/core/bc/BlockExecutor.java | 7 +- .../rsk/core/bc/IReadWrittenKeysTracker.java | 2 + .../rsk/core/bc/ReadWrittenKeysTracker.java | 54 +++++-- .../db/DummyReadWrittenKeysTracker.java | 5 + .../co/rsk/core/bc/BlockExecutorTest.java | 90 ++++++++++-- .../core/bc/ReadWrittenKeysTrackerTest.java | 138 +++++++++++++++++- 7 files changed, 278 insertions(+), 26 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 33e7b4f348b..0b22499e60d 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -1,5 +1,6 @@ package co.rsk.core; +import co.rsk.core.bc.IReadWrittenKeysTracker; import co.rsk.crypto.Keccak256; import org.ethereum.core.*; import org.ethereum.vm.DataWord; @@ -21,6 +22,7 @@ public class TransactionListExecutor implements Callable { private final TransactionExecutorFactory transactionExecutorFactory; private final List transactions; + private IReadWrittenKeysTracker readWrittenKeysTracker; private final Block block; private final Repository track; private final boolean vmTrace; @@ -41,6 +43,7 @@ public class TransactionListExecutor implements Callable { public TransactionListExecutor( List transactions, + IReadWrittenKeysTracker readWrittenKeysTracker, Block block, TransactionExecutorFactory transactionExecutorFactory, Repository track, @@ -58,6 +61,7 @@ public TransactionListExecutor( LongAccumulator accumulatedFees, LongAccumulator accumulatedGas, int firstTxIndex) { + this.readWrittenKeysTracker = readWrittenKeysTracker; this.block = block; this.transactionExecutorFactory = transactionExecutorFactory; this.track = track; @@ -98,6 +102,10 @@ public Boolean call() { ); boolean transactionExecuted = txExecutor.executeTransaction(); + if (readWrittenKeysTracker.hasCollided()) { + return false; + } + if (!acceptInvalidTransactions && !transactionExecuted) { if (discardInvalidTxs) { logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 6fb07560714..1cc4d2ca42a 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -455,8 +455,8 @@ private BlockResult executeParallel( // of the repository to the state post execution, so it's necessary to get it to // the state prior execution again. Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE); - - Repository track = repositoryLocator.startTrackingAt(parent); + IReadWrittenKeysTracker readWrittenKeysTracker = new ReadWrittenKeysTracker(); + Repository track = repositoryLocator.startTrackingAt(parent, readWrittenKeysTracker); maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); @@ -477,6 +477,7 @@ private BlockResult executeParallel( List sublist = block.getTransactionsList().subList(start, end); TransactionListExecutor txListExecutor = new TransactionListExecutor( sublist, + readWrittenKeysTracker, block, transactionExecutorFactory, track, @@ -522,10 +523,12 @@ private BlockResult executeParallel( } } + readWrittenKeysTracker.clear(); // execute remaining transactions after the parallel subsets List sublist = block.getTransactionsList().subList(start, block.getTransactionsList().size()); TransactionListExecutor txListExecutor = new TransactionListExecutor( sublist, + readWrittenKeysTracker, block, transactionExecutorFactory, track, diff --git a/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java b/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java index 3fb6f21d11d..c0aa9d11cc0 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java @@ -26,6 +26,8 @@ public interface IReadWrittenKeysTracker { Set getTemporalWrittenKeys(); + boolean hasCollided(); + void addNewReadKey(ByteArrayWrapper key); void addNewWrittenKey(ByteArrayWrapper key); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java b/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java index 46e1b877816..8d119d92a88 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java @@ -20,45 +20,71 @@ import org.ethereum.db.ByteArrayWrapper; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; //TODO(JULI): // * Next step should be to check whether a key is written in the cache but also deleted in the same transaction. This key shouldn't be considered as a written key. public class ReadWrittenKeysTracker implements IReadWrittenKeysTracker { - private Set temporalReadKeys; - private Set temporalWrittenKeys; + private Map> threadByReadKey; + private Map threadByWrittenKey; + private boolean collision; public ReadWrittenKeysTracker() { - this.temporalReadKeys = new HashSet<>(); - this.temporalWrittenKeys = new HashSet<>(); + this.threadByReadKey = new HashMap<>(); + this.threadByWrittenKey = new HashMap<>(); + this.collision = false; } @Override public Set getTemporalReadKeys(){ - return this.temporalReadKeys; + return new HashSet<>(this.threadByReadKey.keySet()); } @Override public Set getTemporalWrittenKeys(){ - return this.temporalWrittenKeys; + return new HashSet<>(this.threadByWrittenKey.keySet()); } + public boolean hasCollided() { return this.collision;} + @Override - public void addNewReadKey(ByteArrayWrapper key) { - temporalReadKeys.add(key); + public synchronized void addNewReadKey(ByteArrayWrapper key) { + long threadId = Thread.currentThread().getId(); + if (threadByWrittenKey.containsKey(key)) { + collision = collision || (threadId != threadByWrittenKey.get(key)); + } + Set threadSet; + if (threadByReadKey.containsKey(key)) { + threadSet = threadByReadKey.get(key); + } else { + threadSet = new HashSet<>(); + } + threadSet.add(threadId); + threadByReadKey.put(key, threadSet); } @Override - public void addNewWrittenKey(ByteArrayWrapper key) { - temporalWrittenKeys.add(key); + public synchronized void addNewWrittenKey(ByteArrayWrapper key) { + long threadId = Thread.currentThread().getId(); + if (threadByWrittenKey.containsKey(key)) { + collision = collision || (threadId != threadByWrittenKey.get(key)); + } + + if (threadByReadKey.containsKey(key)) { + Set threadSet = threadByReadKey.get(key); + collision = collision || !(threadSet.contains(threadId)) || (threadSet.size() > 1); + } + + threadByWrittenKey.put(key, threadId); } @Override - public void clear() { - this.temporalReadKeys = new HashSet<>(); - this.temporalWrittenKeys = new HashSet<>(); - + public synchronized void clear() { + this.threadByReadKey = new HashMap<>(); + this.threadByWrittenKey = new HashMap<>(); } } diff --git a/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java b/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java index e928147c90e..09cb33a4f33 100644 --- a/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java +++ b/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java @@ -25,6 +25,11 @@ public Set getTemporalWrittenKeys() { return temporalWrittenKeys; } + @Override + public boolean hasCollided() { + return false; + } + @Override public void addNewReadKey(ByteArrayWrapper key) { //Dummy tracker does not store added keys diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index caa127cf5e4..48f41d97a1a 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -281,7 +281,8 @@ void executeBlockWithTwoTransactions(boolean activeRskip144) { Assertions.assertEquals(BigInteger.valueOf(60000 - 42000 - 20), accountState.getBalance().asBigInteger()); } - @Test + @ParameterizedTest + @ValueSource(booleans = {true, false}) void executeAndFillBlockWithNoSavingToStore(boolean activeRskip144) { TestObjects objects = generateBlockWithOneTransaction(activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = objects.getParent(); @@ -294,7 +295,8 @@ void executeAndFillBlockWithNoSavingToStore(boolean activeRskip144) { Assertions.assertEquals(Optional.empty(), trieStore.retrieve(block.getStateRoot())); } - @Test + @ParameterizedTest + @ValueSource(booleans = {true, false}) void executeBlockWithSavingToStore(boolean activeRskip144) { TestObjects objects = generateBlockWithOneTransaction(activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = objects.getParent(); @@ -666,6 +668,35 @@ void executeParallelBlockAgainstSequentialBlock(boolean activeRskip144) { Assertions.assertArrayEquals(seqResult.getFinalState().getHash().getBytes(), parallelResult.getFinalState().getHash().getBytes()); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeInvalidParallelBlock(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + Block parent = blockchain.getBestBlock(); + Block pBlock = getBlockWithTwoDependentTransactions(new short[]{1, 2}); + BlockResult result = executor.execute(null, 0, pBlock, parent.getHeader(), true, false, true); + Assertions.assertEquals(BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT, result); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void ifThereIsACollisionBetweenParallelAndSequentialBucketItShouldNotBeConsidered(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + Block parent = blockchain.getBestBlock(); + Block pBlock = getBlockWithTwoDependentTransactions(new short[]{1}); + BlockResult result = executor.execute(null, 0, pBlock, parent.getHeader(), true, false, true); + Assertions.assertTrue(pBlock.getTransactionsList().containsAll(result.getExecutedTransactions())); + Assertions.assertEquals(pBlock.getTransactionsList().size(), result.getExecutedTransactions().size()); + } + @ParameterizedTest @ValueSource(booleans = {true, false}) void executeParallelBlockTwice(boolean activeRskip144) { @@ -677,11 +708,11 @@ void executeParallelBlockTwice(boolean activeRskip144) { BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); Block block1 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); - BlockResult result1 = executor.executeAndFill(block1, parent.getHeader()); + BlockResult result1 = executor.execute(null, 0, block1, parent.getHeader(), true, false, true); Block block2 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); - BlockResult result2 = executor.executeAndFill(block2, parent.getHeader()); + BlockResult result2 = executor.execute(null, 0, block2, parent.getHeader(), true, false, true); Assertions.assertArrayEquals(result2.getFinalState().getHash().getBytes(), result1.getFinalState().getHash().getBytes()); Assertions.assertArrayEquals(block1.getHash().getBytes(), block2.getHash().getBytes()); @@ -963,6 +994,48 @@ private Block getBlockWithTwoTransactions() { return new BlockGenerator(Constants.regtest(), activationConfig).createChildBlock(bestBlock, txs, uncles, 1, null); } + private Block getBlockWithTwoDependentTransactions(short[] edges) { + int nTxs = 2; + + Repository track = repository.startTracking(); + List accounts = new LinkedList<>(); + + for (int i = 0; i < nTxs; i++) { + accounts.add(createAccount("accounttest" + i, track, Coin.valueOf(60000))); + } + track.commit(); + Block bestBlock = blockchain.getBestBlock(); + bestBlock.setStateRoot(repository.getRoot()); + + List txs = new LinkedList<>(); + + for (int i = 0; i < nTxs; i++) { + Transaction tx = Transaction.builder() + .nonce(BigInteger.ZERO) + .gasPrice(BigInteger.ONE) + .gasLimit(BigInteger.valueOf(21000)) + .destination(accounts.get((i + 1) % 2).getAddress()) + .chainId(CONFIG.getNetworkConstants().getChainId()) + .value(BigInteger.TEN) + .build(); + tx.sign(accounts.get(i).getEcKey().getPrivKeyBytes()); + txs.add(tx); + } + List uncles = new ArrayList<>(); + + return new BlockGenerator(Constants.regtest(), activationConfig) + .createChildBlock( + bestBlock, + txs, + uncles, + 1, + null, + bestBlock.getGasLimit(), + bestBlock.getCoinbase(), + edges + ); + } + private Block getBlockWithTenTransactions(short[] edges) { int nTxs = 10; int nAccounts = nTxs * 2; @@ -1005,9 +1078,8 @@ private Block getBlockWithTenTransactions(short[] edges) { ); } - private Block getBlockWithNIndependentTransactions(int number, BigInteger txGasLimit, boolean withRemasc) { - int nTxs = number; - int nAccounts = nTxs * 2; + private Block getBlockWithNIndependentTransactions(int txNumber, BigInteger txGasLimit, boolean withRemasc) { + int nAccounts = txNumber * 2; Repository track = repository.startTracking(); List accounts = new LinkedList<>(); @@ -1020,12 +1092,12 @@ private Block getBlockWithNIndependentTransactions(int number, BigInteger txGasL List txs = new LinkedList<>(); - for (int i = 0; i < nTxs; i++) { + for (int i = 0; i < txNumber; i++) { Transaction tx = Transaction.builder() .nonce(BigInteger.ZERO) .gasPrice(BigInteger.ONE) .gasLimit(txGasLimit) - .destination(accounts.get(i + nTxs).getAddress()) + .destination(accounts.get(i + txNumber).getAddress()) .chainId(CONFIG.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java index c9103ded52d..e438e0e8630 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java @@ -24,8 +24,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Set; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.concurrent.*; public class ReadWrittenKeysTrackerTest { @@ -131,8 +134,141 @@ void clearDummyTrackerShouldDoNothing() { Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); } + @Test + public void ifTwoThreadsWriteTheSameKeyCollideShouldBeTrue() { + int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); + + for (int i = 0; i < nThreads; i++) { + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.singletonList(key1), Collections.emptyList()); + completionService.submit(rwKeys); + } + + assertThereWasACollision(nThreads, service, completionService); + } + + @Test + public void ifTwoThreadsReadAndWriteTheSameKeyShouldCollide() { + int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); + List writtenKeys; + List readKeys; + for (int i = 0; i < nThreads; i++) { + if (i == 0) { + writtenKeys = Collections.singletonList(key1); + readKeys = Collections.emptyList(); + } else { + writtenKeys = Collections.emptyList(); + readKeys = Collections.singletonList(key1); + } + + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, writtenKeys, readKeys); + completionService.submit(rwKeys); + } + + assertThereWasACollision(nThreads, service, completionService); + } + + @Test + public void ifTwoThreadsWriteDifferentKeyCollideShouldBeFalse() { + int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); + + for (int i = 0; i < nThreads; i++) { + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.singletonList(i == 0? key1 : key2), Collections.emptyList()); + completionService.submit(rwKeys); + } + assertThereWasNotACollision(nThreads, service, completionService); + } + + @Test + public void allThreadIdsShouldBeStoredInTheReadKeysMap() { + int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); + boolean hasCollided = false; + + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.emptyList(), Collections.singletonList(key1)); + completionService.submit(rwKeys); + + try { + Future hasCollidedFuture = completionService.take(); + hasCollided = hasCollidedFuture.get(); + } catch (Exception e) { + Assertions.fail(); + } + + Assertions.assertFalse(hasCollided); + ReadWrittenKeysHelper rwKeys2 = new ReadWrittenKeysHelper(this.tracker, Collections.singletonList(key1), Collections.singletonList(key1)); + completionService.submit(rwKeys2); + + try { + Future hasCollidedFuture = completionService.take(); + hasCollided = hasCollidedFuture.get(); + } catch (Exception e) { + Assertions.fail(); + } + + service.shutdown(); + Assertions.assertTrue(hasCollided); + } + + private void assertThereWasNotACollision(int nThreads, ExecutorService service, CompletionService completionService) { + boolean hasCollided = hasCollided(nThreads, completionService); + Assertions.assertFalse(hasCollided); + service.shutdown(); + } + + private void assertThereWasACollision(int nThreads, ExecutorService service, CompletionService completionService) { + boolean hasCollided = hasCollided(nThreads, completionService); + System.out.println(hasCollided); + Assertions.assertTrue(hasCollided); + service.shutdown(); + } + + private boolean hasCollided(int nThreads, CompletionService completionService) { + boolean hasCollided = false; + for (int i = 0; i < nThreads; i++) { + try { + Future hasCollidedFuture = completionService.take(); + hasCollided |= hasCollidedFuture.get(); + } catch (Exception e) { + Assertions.fail(); + } + } + return hasCollided; + } + private void assertKeyWasAddedInMap(Set map, ByteArrayWrapper key) { Assertions.assertEquals(1, map.size()); Assertions.assertTrue(map.contains(key)); } + private static class ReadWrittenKeysHelper implements Callable { + + private final List readKeys; + private final List writtenKeys; + private final IReadWrittenKeysTracker tracker; + + public ReadWrittenKeysHelper(IReadWrittenKeysTracker tracker, List writtenKeys, List readKeys) { + this.tracker = tracker; + this.readKeys = readKeys; + this.writtenKeys = writtenKeys; + } + //At first, it reads and then it writes. + public Boolean call() { + for (ByteArrayWrapper rk : readKeys) { + tracker.addNewReadKey(rk); + } + + for (ByteArrayWrapper wk : writtenKeys) { + tracker.addNewWrittenKey(wk); + } + + return tracker.hasCollided(); + } + } + } From 3a23caab17beeaf113a12254ed24b5c843c8c268 Mon Sep 17 00:00:00 2001 From: julianlen Date: Wed, 22 Jun 2022 11:24:33 -0300 Subject: [PATCH 12/89] Created a Constant with the number of threads (#1815) --- .../java/co/rsk/core/bc/BlockExecutor.java | 12 ++++------ .../ValidTxExecutionListsEdgesRule.java | 4 ++-- .../java/org/ethereum/config/Constants.java | 3 ++- .../co/rsk/core/bc/BlockExecutorTest.java | 24 ++++++++++--------- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 1cc4d2ca42a..74b0005b344 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -28,6 +28,7 @@ import co.rsk.metrics.profilers.Profiler; import co.rsk.metrics.profilers.ProfilerFactory; import com.google.common.annotations.VisibleForTesting; +import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; @@ -56,8 +57,6 @@ * Note that this class IS NOT guaranteed to be thread safe because its dependencies might hold state. */ public class BlockExecutor { - private static final int THREAD_COUNT = 4; - private static final Logger logger = LoggerFactory.getLogger("blockexecutor"); private static final Profiler profiler = ProfilerFactory.getInstance(); @@ -467,8 +466,8 @@ private BlockResult executeParallel( Set deletedAccounts = ConcurrentHashMap.newKeySet(); LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); - ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); - CompletionService completionService = new ExecutorCompletionService(executorService); + ExecutorService executorService = Executors.newFixedThreadPool(Constants.getTransactionExecutionThreads()); + ExecutorCompletionService completionService = new ExecutorCompletionService(executorService); int nTasks = 0; // execute parallel subsets of transactions @@ -610,10 +609,9 @@ private BlockResult executeForMiningAfterRSKIP144( Map receiptsByTx = new HashMap<>(); Set deletedAccounts = new HashSet<>(); LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); - short buckets = 2; //TODO(Juli): Is there a better way to calculate the bucket gas limit? - ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler(buckets, GasCost.toGas(block.getGasLimit())); + ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) Constants.getTransactionExecutionThreads(), GasCost.toGas(block.getGasLimit())); int txindex = 0; @@ -693,7 +691,7 @@ private BlockResult executeForMiningAfterRSKIP144( receipt.setCumulativeGas(bucketGasAccumulated.get()); } else { //This line is used for testing only when acceptInvalidTransactions is set. - receipt.setCumulativeGas(parallelizeTransactionHandler.getGasUsedIn(buckets)); + receipt.setCumulativeGas(parallelizeTransactionHandler.getGasUsedIn((short) Constants.getTransactionExecutionThreads())); } receipt.setTxStatus(txExecutor.getReceipt().isSuccessful()); diff --git a/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java index 9885dbdfeb1..0a89a03de2d 100644 --- a/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java +++ b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java @@ -24,9 +24,9 @@ public boolean isValid(Block block) { if (edges == null) { return true; } - if (edges.length > Constants.getMaxTransactionExecutionThreads()) { + if (edges.length > Constants.getTransactionExecutionThreads()) { logger.warn("Invalid block: number of execution lists edges is greater than number of execution threads ({} vs {})", - edges.length, Constants.getMaxTransactionExecutionThreads()); + edges.length, Constants.getTransactionExecutionThreads()); return false; } short prev = 0; diff --git a/rskj-core/src/main/java/org/ethereum/config/Constants.java b/rskj-core/src/main/java/org/ethereum/config/Constants.java index 63ad2f55705..d8efab75255 100644 --- a/rskj-core/src/main/java/org/ethereum/config/Constants.java +++ b/rskj-core/src/main/java/org/ethereum/config/Constants.java @@ -48,6 +48,7 @@ public class Constants { private static final long DEFAULT_MAX_TIMESTAMPS_DIFF_IN_SECS = 5L * 60; // 5 mins private static final long TESTNET_MAX_TIMESTAMPS_DIFF_IN_SECS = 120L * 60; // 120 mins + public static final int TX_EXECUTION_THREADS = 4; private final byte chainId; private final boolean seedCowAccounts; @@ -225,7 +226,7 @@ public static int getMaxBitcoinMergedMiningMerkleProofLength() { return 960; } - public static int getMaxTransactionExecutionThreads() { return 4; } + public static int getTransactionExecutionThreads() { return TX_EXECUTION_THREADS; } public static Constants mainnet() { return new Constants( diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index 48f41d97a1a..8d7dfe5fbba 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -499,16 +499,17 @@ void executeSequentiallyTenIndependentTxsAndThemShouldGoInBothBuckets(boolean ac BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); long expectedGasUsed = 0L; long expectedAccumulatedGas = 21000L; - short[] expectedEdges = new short[]{5, 10}; + int txNumber = 12; + short[] expectedEdges = new short[]{3, 6, 9, 12}; Block parent = blockchain.getBestBlock(); - Block block = getBlockWithNIndependentTransactions(10, BigInteger.valueOf(expectedAccumulatedGas), false); + Block block = getBlockWithNIndependentTransactions(txNumber, BigInteger.valueOf(expectedAccumulatedGas), false); List txs = block.getTransactionsList(); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); Assertions.assertEquals(txs.size(), blockResult.getExecutedTransactions().size()); Assertions.assertTrue(txs.containsAll(blockResult.getExecutedTransactions())); Assertions.assertArrayEquals(expectedEdges, blockResult.getTxEdges()); - Assertions.assertEquals(expectedAccumulatedGas*10, blockResult.getGasUsed()); + Assertions.assertEquals(expectedAccumulatedGas*txNumber, blockResult.getGasUsed()); List transactionReceipts = blockResult.getTransactionReceipts(); long accumulatedGasUsed = 0L; @@ -539,11 +540,10 @@ void executeBigIndependentTxsSequentiallyTheLastOneShouldGoToSequential(boolean Block parent = blockchain.getBestBlock(); long blockGasLimit = GasCost.toGas(parent.getGasLimit()); int gasLimit = 21000; - int transactionNumber = (int) (blockGasLimit /gasLimit); - short[] expectedEdges = new short[]{(short) transactionNumber, (short) (transactionNumber*2)}; + int transactionNumber = (int) (blockGasLimit/gasLimit); + short[] expectedEdges = new short[]{(short) transactionNumber, (short) (transactionNumber*2), (short) (transactionNumber*3), (short) (transactionNumber*4)}; int transactionsInSequential = 1; - - Block block = getBlockWithNIndependentTransactions(transactionNumber*2+transactionsInSequential, BigInteger.valueOf(gasLimit), false); + Block block = getBlockWithNIndependentTransactions(transactionNumber * Constants.getTransactionExecutionThreads() + transactionsInSequential, BigInteger.valueOf(gasLimit), false); List transactionsList = block.getTransactionsList(); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); @@ -582,7 +582,7 @@ void executeATxInSequentialAndBlockResultShouldTrackTheGasUsedInTheBlock(boolean int gasLimit = 21000; int transactionNumberToFillParallelBucket = (int) (blockGasLimit / gasLimit); int transactionsInSequential = 1; - int totalTxsNumber = transactionNumberToFillParallelBucket * 2 + transactionsInSequential; + int totalTxsNumber = transactionNumberToFillParallelBucket * Constants.getTransactionExecutionThreads() + transactionsInSequential; Block block = getBlockWithNIndependentTransactions(totalTxsNumber, BigInteger.valueOf(gasLimit), false); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); @@ -601,7 +601,8 @@ void withTheBucketsFullTheLastTransactionShouldNotFit(boolean activeRskip144) { long blockGasLimit = GasCost.toGas(parent.getGasLimit()); int gasLimit = 21000; int transactionNumberToFillParallelBucket = (int) (blockGasLimit / gasLimit); - int totalTxs = (transactionNumberToFillParallelBucket) * 3 + 1; + int totalNumberOfBuckets = Constants.getTransactionExecutionThreads() + 1; + int totalTxs = (transactionNumberToFillParallelBucket) * totalNumberOfBuckets + 1; Block block = getBlockWithNIndependentTransactions(totalTxs, BigInteger.valueOf(gasLimit), false); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); Assertions.assertEquals(totalTxs, blockResult.getExecutedTransactions().size() + 1); @@ -620,8 +621,9 @@ void withSequentialBucketFullRemascTxShouldFit(boolean activeRskip144) { long blockGasLimit = GasCost.toGas(parent.getGasLimit()); int gasLimit = 21000; int transactionNumberToFillABucket = (int) (blockGasLimit / gasLimit); - int expectedNumberOfTx = transactionNumberToFillABucket*3 + 1; - Block block = getBlockWithNIndependentTransactions(transactionNumberToFillABucket*3, BigInteger.valueOf(gasLimit), true); + int totalNumberOfBuckets = Constants.getTransactionExecutionThreads() + 1; + int expectedNumberOfTx = transactionNumberToFillABucket* totalNumberOfBuckets + 1; + Block block = getBlockWithNIndependentTransactions(transactionNumberToFillABucket * totalNumberOfBuckets, BigInteger.valueOf(gasLimit), true); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); Assertions.assertEquals(expectedNumberOfTx, blockResult.getExecutedTransactions().size()); } From 2398add1b3d188e5088ede365e7fc1e71ac48a9f Mon Sep 17 00:00:00 2001 From: julianlen Date: Mon, 27 Jun 2022 16:55:31 -0300 Subject: [PATCH 13/89] Research/refactoring code (#1817) * store and return a copy of txEdges * merged two ifs in the BlockHeaderBuilder * DummyTracker updated so it returns a copy of the maps * Unused hashmap import deleted from ProgramTraceProcessor * BlockResult returns and stores a copy of txEdges * big refactor in BlockExecutor * Refactor ParallelizeTransactionHandler * renaming Bucket to Sublist * renaming txExecutionListsEdges to txExecutionSublistEdges * simplification of Handler's code * InterruptedException solved properly in the BlockExecutor --- .../src/main/java/co/rsk/RskContext.java | 2 +- .../co/rsk/core/TransactionListExecutor.java | 25 +- .../java/co/rsk/core/bc/BlockExecutor.java | 195 +++---- .../main/java/co/rsk/core/bc/BlockResult.java | 6 +- .../bc/ParallelizeTransactionHandler.java | 223 +++---- .../rsk/core/bc/ReadWrittenKeysTracker.java | 3 - ...=> ValidTxExecutionSublistsEdgesRule.java} | 4 +- .../java/org/ethereum/core/BlockFactory.java | 8 +- .../java/org/ethereum/core/BlockHeader.java | 20 +- .../org/ethereum/core/BlockHeaderBuilder.java | 20 +- .../db/DummyReadWrittenKeysTracker.java | 14 +- .../vm/trace/ProgramTraceProcessor.java | 1 - .../rsk/blockchain/utils/BlockGenerator.java | 2 +- .../java/co/rsk/core/BlockFactoryTest.java | 12 +- .../co/rsk/core/bc/BlockExecutorTest.java | 56 +- .../bc/ParallelizeTransactionHandlerTest.java | 542 +++++++++--------- .../ValidTxExecutionListsEdgesTest.java | 93 --- .../ValidTxExecutionSublistsEdgesTest.java | 92 +++ .../ethereum/core/BlockHeaderBuilderTest.java | 28 +- 19 files changed, 673 insertions(+), 673 deletions(-) rename rskj-core/src/main/java/co/rsk/validators/{ValidTxExecutionListsEdgesRule.java => ValidTxExecutionSublistsEdgesRule.java} (90%) delete mode 100644 rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionListsEdgesTest.java create mode 100644 rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java diff --git a/rskj-core/src/main/java/co/rsk/RskContext.java b/rskj-core/src/main/java/co/rsk/RskContext.java index 0961e6fcb57..1aadb064b06 100644 --- a/rskj-core/src/main/java/co/rsk/RskContext.java +++ b/rskj-core/src/main/java/co/rsk/RskContext.java @@ -1089,7 +1089,7 @@ public synchronized BlockValidationRule getBlockValidationRule() { new GasLimitRule(commonConstants.getMinGasLimit()), new ExtraDataRule(commonConstants.getMaximumExtraDataSize()), getForkDetectionDataRule(), - new ValidTxExecutionListsEdgesRule() + new ValidTxExecutionSublistsEdgesRule() ); } diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 0b22499e60d..a5905f5e84b 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -10,19 +10,17 @@ import org.slf4j.LoggerFactory; import javax.annotation.Nullable; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.atomic.LongAccumulator; -public class TransactionListExecutor implements Callable { +public class TransactionListExecutor implements Callable { private static final Logger logger = LoggerFactory.getLogger("transactionlistexecutor"); private final TransactionExecutorFactory transactionExecutorFactory; private final List transactions; - private IReadWrittenKeysTracker readWrittenKeysTracker; + private final IReadWrittenKeysTracker readWrittenKeysTracker; private final Block block; private final Repository track; private final boolean vmTrace; @@ -39,7 +37,7 @@ public class TransactionListExecutor implements Callable { private final LongAccumulator accumulatedGas; private int i; - private boolean registerProgramResults; + private final boolean registerProgramResults; public TransactionListExecutor( List transactions, @@ -67,7 +65,7 @@ public TransactionListExecutor( this.track = track; this.vmTrace = vmTrace; this.vmTraceOptions = vmTraceOptions; - this.transactions = transactions; + this.transactions = new ArrayList<>(transactions); this.deletedAccounts = deletedAccounts; this.discardInvalidTxs = discardInvalidTxs; this.acceptInvalidTransactions = acceptInvalidTransactions; @@ -107,15 +105,15 @@ public Boolean call() { } if (!acceptInvalidTransactions && !transactionExecuted) { - if (discardInvalidTxs) { - logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); - continue; - } else { + if (!discardInvalidTxs) { logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", - block.getNumber(), tx.getHash() + block.getNumber(), tx.getHash() ); return false; } + + logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); + continue; } executedTransactions.put(i, tx); @@ -129,9 +127,6 @@ public Boolean call() { } logger.trace("tx[{}] executed", i + 1); - - // No need to commit the changes here. track.commit(); - logger.trace("track commit"); long txGasUsed = txExecutor.getGasUsed(); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 74b0005b344..b270c41e099 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -149,7 +149,7 @@ private void fill(Block block, BlockResult result) { header.setGasUsed(result.getGasUsed()); header.setPaidFees(result.getPaidFees()); header.setLogsBloom(calculateLogsBloom(result.getTransactionReceipts())); - header.setTxExecutionListsEdges(result.getTxEdges()); + header.setTxExecutionSublistsEdges(result.getTxEdges()); block.flushRLP(); profiler.stop(metric); @@ -260,7 +260,7 @@ private boolean validateLogsBloom(BlockHeader header, BlockResult result) { } public BlockResult executeForMining(Block block, BlockHeader parent, boolean discardInvalidTxs, boolean ignoreReadyToExecute, boolean saveState) { - if (block.getHeader().getTxExecutionListsEdges() != null) { + if (block.getHeader().getTxExecutionSublistsEdges() != null) { return executeForMiningAfterRSKIP144(block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); } else { return executePreviousRSKIP144(null, 0, block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); @@ -293,7 +293,7 @@ public BlockResult execute( ) { boolean rskip144Active = activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber()); - if (rskip144Active && block.getHeader().getTxExecutionListsEdges() != null) { + if (rskip144Active && block.getHeader().getTxExecutionSublistsEdges() != null) { return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); } else { return executePreviousRSKIP144(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); @@ -309,8 +309,8 @@ private BlockResult executePreviousRSKIP144( boolean acceptInvalidTransactions, boolean saveState) { boolean vmTrace = programTraceProcessor != null; - logger.trace("Start executeInternal."); - logger.trace("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), block.getTransactionsList().size()); + logger.trace("Start execute pre RSKIP144."); + loggingApplyBlock(block); // Forks the repo, does not change "repository". It will have a completely different // image of the repo, where the middle caches are immediately ignored. @@ -320,7 +320,7 @@ private BlockResult executePreviousRSKIP144( // in the next block processed. // Note that creating a snapshot is important when the block is executed twice // (e.g. once while building the block in tests/mining, and the other when trying - // to conect the block). This is because the first execution will change the state + // to connect the block). This is because the first execution will change the state // of the repository to the state post execution, so it's necessary to get it to // the state prior execution again. Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE); @@ -340,7 +340,7 @@ private BlockResult executePreviousRSKIP144( int txindex = 0; for (Transaction tx : block.getTransactionsList()) { - logger.trace("apply block: [{}] tx: [{}] ", block.getNumber(), i); + loggingApplyBlockToTx(block, i); TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( tx, @@ -356,15 +356,11 @@ private BlockResult executePreviousRSKIP144( boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { - if (discardInvalidTxs) { - logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); - continue; - } else { - logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", - block.getNumber(), tx.getHash()); - profiler.stop(metric); - return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + if (!discardInvalidTxs) { + return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } + loggingDiscardedBlock(block, tx); + continue; } executedTransactions.add(tx); @@ -377,12 +373,7 @@ private BlockResult executePreviousRSKIP144( txExecutor.extractTrace(programTraceProcessor); } - logger.trace("tx executed"); - - // No need to commit the changes here. track.commit(); - - logger.trace("track commit"); - + loggingTxExecuted(); long gasUsed = txExecutor.getGasUsed(); totalGasUsed += gasUsed; Coin paidFees = txExecutor.getPaidFees(); @@ -392,24 +383,15 @@ private BlockResult executePreviousRSKIP144( deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); - TransactionReceipt receipt = new TransactionReceipt(); - receipt.setGasUsed(gasUsed); - receipt.setCumulativeGas(totalGasUsed); + TransactionReceipt receipt = buildTransactionReceipt(tx, txExecutor, gasUsed, totalGasUsed); - receipt.setTxStatus(txExecutor.getReceipt().isSuccessful()); - receipt.setTransaction(tx); - receipt.setLogInfoList(txExecutor.getVMLogs()); - receipt.setStatus(txExecutor.getReceipt().getStatus()); - - logger.trace("block: [{}] executed tx: [{}]", block.getNumber(), tx.getHash()); - - logger.trace("tx[{}].receipt", i); + loggingExecuteTxAndReceipt(block, i, tx); i++; receipts.add(receipt); - logger.trace("tx done"); + loggingTxDone(); } addFeesToRemasc(remascFees, track); @@ -426,7 +408,7 @@ private BlockResult executePreviousRSKIP144( ); profiler.stop(metric); - logger.trace("End executeInternal."); + logger.trace("End execute pre RSKIP144."); return result; } @@ -439,8 +421,8 @@ private BlockResult executeParallel( boolean acceptInvalidTransactions, boolean saveState) { boolean vmTrace = programTraceProcessor != null; - logger.trace("Start executeInternal."); - logger.trace("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), block.getTransactionsList().size()); + logger.trace("Start executeParallel."); + loggingApplyBlock(block); // Forks the repo, does not change "repository". It will have a completely different // image of the repo, where the middle caches are immediately ignored. @@ -456,7 +438,6 @@ private BlockResult executeParallel( Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE); IReadWrittenKeysTracker readWrittenKeysTracker = new ReadWrittenKeysTracker(); Repository track = repositoryLocator.startTrackingAt(parent, readWrittenKeysTracker); - maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); LongAccumulator totalGasUsed = new LongAccumulator(Long::sum, 0); @@ -467,12 +448,12 @@ private BlockResult executeParallel( LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); ExecutorService executorService = Executors.newFixedThreadPool(Constants.getTransactionExecutionThreads()); - ExecutorCompletionService completionService = new ExecutorCompletionService(executorService); + ExecutorCompletionService completionService = new ExecutorCompletionService<>(executorService); int nTasks = 0; // execute parallel subsets of transactions short start = 0; - for (short end : block.getHeader().getTxExecutionListsEdges()) { + for (short end : block.getHeader().getTxExecutionSublistsEdges()) { List sublist = block.getTransactionsList().subList(start, end); TransactionListExecutor txListExecutor = new TransactionListExecutor( sublist, @@ -504,7 +485,7 @@ private BlockResult executeParallel( for (int i = 0; i < nTasks; i++) { try { Future success = completionService.take(); - if (!success.get()) { + if (!Boolean.TRUE.equals(success.get())) { executorService.shutdownNow(); profiler.stop(metric); return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; @@ -512,6 +493,7 @@ private BlockResult executeParallel( } catch (InterruptedException e) { logger.warn("block: [{}] execution was interrupted", block.getNumber()); logger.trace("", e); + Thread.currentThread().interrupt(); profiler.stop(metric); return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } catch (ExecutionException e) { @@ -547,12 +529,13 @@ private BlockResult executeParallel( start ); Boolean success = txListExecutor.call(); - if (!success) { + if (!Boolean.TRUE.equals(success)) { return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } addFeesToRemasc(remascFees, track); saveOrCommitTrackState(saveState, track); + BlockResult result = new BlockResult( block, new LinkedList<>(executedTransactions.values()), @@ -563,7 +546,7 @@ private BlockResult executeParallel( vmTrace ? null : track.getTrie() ); profiler.stop(metric); - logger.trace("End executeInternal."); + logger.trace("End executeParallel."); return result; } @@ -580,10 +563,9 @@ private BlockResult executeForMiningAfterRSKIP144( boolean discardInvalidTxs, boolean acceptInvalidTransactions, boolean saveState) { - - logger.trace("Start executeInternal."); + logger.trace("Start executeForMining."); List transactionsList = block.getTransactionsList(); - logger.trace("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), transactionsList.size()); + loggingApplyBlock(block); // Forks the repo, does not change "repository". It will have a completely different // image of the repo, where the middle caches are immediately ignored. @@ -598,9 +580,8 @@ private BlockResult executeForMiningAfterRSKIP144( // the state prior execution again. Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE); - ReadWrittenKeysTracker readWrittenKeysTracker = new ReadWrittenKeysTracker(); + IReadWrittenKeysTracker readWrittenKeysTracker = new ReadWrittenKeysTracker(); Repository track = repositoryLocator.startTrackingAt(parent, readWrittenKeysTracker); - maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); int i = 1; @@ -610,13 +591,12 @@ private BlockResult executeForMiningAfterRSKIP144( Set deletedAccounts = new HashSet<>(); LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); - //TODO(Juli): Is there a better way to calculate the bucket gas limit? ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) Constants.getTransactionExecutionThreads(), GasCost.toGas(block.getGasLimit())); int txindex = 0; for (Transaction tx : transactionsList) { - logger.trace("apply block: [{}] tx: [{}] ", block.getNumber(), i); + loggingApplyBlockToTx(block, i); TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( tx, @@ -628,38 +608,31 @@ private BlockResult executeForMiningAfterRSKIP144( false, 0, deletedAccounts, - remascFees); //TODO(Juli): Check how to differ this behavior between RSKIPs + remascFees); boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { - if (discardInvalidTxs) { - logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); - continue; - } else { - logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", - block.getNumber(), tx.getHash()); - profiler.stop(metric); - return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + if (!discardInvalidTxs) { + return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } + + loggingDiscardedBlock(block, tx); + continue; } - Optional bucketGasAccumulated; + Optional sublistGasAccumulated; if (tx.isRemascTransaction(txindex, transactionsList.size())) { - bucketGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, txExecutor.getGasUsed()); + sublistGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, txExecutor.getGasUsed()); } else { - bucketGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getTemporalReadKeys(), readWrittenKeysTracker.getTemporalWrittenKeys(), txExecutor.getGasUsed()); + sublistGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getTemporalReadKeys(), readWrittenKeysTracker.getTemporalWrittenKeys(), txExecutor.getGasUsed()); } - if (!acceptInvalidTransactions && !bucketGasAccumulated.isPresent()) { - if (discardInvalidTxs) { - logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); - continue; - } else { - logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", - block.getNumber(), tx.getHash()); - profiler.stop(metric); - return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + if (!acceptInvalidTransactions && !sublistGasAccumulated.isPresent()) { + if (!discardInvalidTxs) { + return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } + loggingDiscardedBlock(block, tx); + continue; } readWrittenKeysTracker.clear(); @@ -668,15 +641,9 @@ private BlockResult executeForMiningAfterRSKIP144( this.transactionResults.put(tx.getHash(), txExecutor.getResult()); } - logger.trace("tx executed"); - - // No need to commit the changes here. track.commit(); - - logger.trace("track commit"); - + loggingTxExecuted(); long gasUsed = txExecutor.getGasUsed(); gasUsedInBlock += gasUsed; - Coin paidFees = txExecutor.getPaidFees(); if (paidFees != null) { totalPaidFees = totalPaidFees.add(paidFees); @@ -684,53 +651,44 @@ private BlockResult executeForMiningAfterRSKIP144( deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); - TransactionReceipt receipt = new TransactionReceipt(); - receipt.setGasUsed(gasUsed); - if (bucketGasAccumulated.isPresent()) { - receipt.setCumulativeGas(bucketGasAccumulated.get()); - } else { - //This line is used for testing only when acceptInvalidTransactions is set. - receipt.setCumulativeGas(parallelizeTransactionHandler.getGasUsedIn((short) Constants.getTransactionExecutionThreads())); - } - - receipt.setTxStatus(txExecutor.getReceipt().isSuccessful()); - receipt.setTransaction(tx); - receipt.setLogInfoList(txExecutor.getVMLogs()); - receipt.setStatus(txExecutor.getReceipt().getStatus()); + //orElseGet is used for testing only when acceptInvalidTransactions is set. + long cumulativeGas = sublistGasAccumulated + .orElseGet(() -> parallelizeTransactionHandler.getGasUsedIn((short) Constants.getTransactionExecutionThreads())); + TransactionReceipt receipt = buildTransactionReceipt(tx, txExecutor, gasUsed, cumulativeGas); - logger.trace("block: [{}] executed tx: [{}]", block.getNumber(), tx.getHash()); - - logger.trace("tx[{}].receipt", i); + loggingExecuteTxAndReceipt(block, i, tx); i++; receiptsByTx.put(tx, receipt); - logger.trace("tx done"); + loggingTxDone(); } addFeesToRemasc(remascFees, track); saveOrCommitTrackState(saveState, track); + List executedTransactions = parallelizeTransactionHandler.getTransactionsInOrder(); - short[] bucketOrder = parallelizeTransactionHandler.getTransactionsPerBucketInOrder(); + short[] sublistOrder = parallelizeTransactionHandler.getTransactionsPerSublistInOrder(); List receipts = new ArrayList<>(); for (Transaction tx : executedTransactions) { receipts.add(receiptsByTx.get(tx)); } + BlockResult result = new BlockResult( block, executedTransactions, receipts, - bucketOrder, + sublistOrder, gasUsedInBlock, totalPaidFees, track.getTrie() ); profiler.stop(metric); - logger.trace("End executeInternal."); + logger.trace("End executeForMining."); return result; } @@ -749,6 +707,49 @@ private void saveOrCommitTrackState(boolean saveState, Repository track) { logger.trace("Building execution results."); } + private TransactionReceipt buildTransactionReceipt(Transaction tx, TransactionExecutor txExecutor, long gasUsed, long cumulativeGas) { + TransactionReceipt receipt = new TransactionReceipt(); + receipt.setGasUsed(gasUsed); + receipt.setTxStatus(txExecutor.getReceipt().isSuccessful()); + receipt.setTransaction(tx); + receipt.setLogInfoList(txExecutor.getVMLogs()); + receipt.setStatus(txExecutor.getReceipt().getStatus()); + receipt.setCumulativeGas(cumulativeGas); + return receipt; + } + + private BlockResult getBlockResultAndLogExecutionInterrupted(Block block, Metric metric, Transaction tx) { + logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", + block.getNumber(), tx.getHash()); + profiler.stop(metric); + return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + } + + private void loggingTxExecuted() { + logger.trace("tx executed"); + } + + private void loggingTxDone() { + logger.trace("tx done"); + } + + private void loggingDiscardedBlock(Block block, Transaction tx) { + logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); + } + + private void loggingApplyBlock(Block block) { + logger.trace("applyBlock: block: [{}] tx.list: [{}]", block.getNumber(), block.getTransactionsList().size()); + } + + private void loggingApplyBlockToTx(Block block, int i) { + logger.trace("apply block: [{}] tx: [{}] ", block.getNumber(), i); + } + + private void loggingExecuteTxAndReceipt(Block block, int i, Transaction tx) { + logger.trace("block: [{}] executed tx: [{}]", block.getNumber(), tx.getHash()); + logger.trace("tx[{}].receipt", i); + } + public ProgramResult getProgramResult(Keccak256 txhash) { return this.transactionResults.get(txhash); } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockResult.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockResult.java index fd656b6b73b..b8f3468b9d3 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockResult.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockResult.java @@ -24,6 +24,7 @@ import org.ethereum.core.Transaction; import org.ethereum.core.TransactionReceipt; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -65,14 +66,13 @@ public BlockResult( this.gasUsed = gasUsed; this.paidFees = paidFees; this.finalState = finalState; - this.txEdges = txEdges; + this.txEdges = txEdges != null? Arrays.copyOf(txEdges, txEdges.length) : null; } - public Block getBlock() { return block; } - public short[] getTxEdges() { return txEdges; } + public short[] getTxEdges() { return this.txEdges != null ? Arrays.copyOf(txEdges, txEdges.length) : null; } public List getExecutedTransactions() { return executedTransactions; } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index d2526872bdc..dce2549903c 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -26,219 +26,230 @@ import java.util.*; public class ParallelizeTransactionHandler { - private final HashMap bucketByWrittenKey; - private final HashMap> bucketByReadKey; - private final Map bucketBySender; - private final ArrayList buckets; - - public ParallelizeTransactionHandler(short buckets, long bucketGasLimit) { - this.bucketBySender = new HashMap<>(); - this.bucketByWrittenKey = new HashMap<>(); - this.bucketByReadKey = new HashMap<>(); - this.buckets = new ArrayList<>(); - for (short i = 0; i < buckets; i++){ - this.buckets.add(new TransactionBucket(i, bucketGasLimit, false)); + private final HashMap sublistByWrittenKey; + private final HashMap> sublistsByReadKey; + private final Map sublistBySender; + private final ArrayList sublists; + + public ParallelizeTransactionHandler(short numberOfSublists, long sublistGasLimit) { + this.sublistBySender = new HashMap<>(); + this.sublistByWrittenKey = new HashMap<>(); + this.sublistsByReadKey = new HashMap<>(); + this.sublists = new ArrayList<>(); + for (short i = 0; i < numberOfSublists; i++){ + this.sublists.add(new TransactionSublist(sublistGasLimit, false)); } - this.buckets.add(new TransactionBucket(buckets, bucketGasLimit, true)); + this.sublists.add(new TransactionSublist(sublistGasLimit, true)); } public Optional addTransaction(Transaction tx, Set newReadKeys, Set newWrittenKeys, long gasUsedByTx) { - TransactionBucket bucketCandidate = getBucketCandidates(tx, newReadKeys, newWrittenKeys); + TransactionSublist sublistCandidate = getSublistCandidates(tx, newReadKeys, newWrittenKeys); - if (!bucketHasAvailableGas(tx, bucketCandidate)) { - if (bucketCandidate.isSequential()) { + if (!sublistHasAvailableGas(tx, sublistCandidate)) { + if (sublistCandidate.isSequential()) { return Optional.empty(); } - bucketCandidate = getSequentialBucket(); + sublistCandidate = getSequentialSublist(); - if (!bucketHasAvailableGas(tx, bucketCandidate)) { + if (!sublistHasAvailableGas(tx, sublistCandidate)) { return Optional.empty(); } } - bucketCandidate.addTransaction(tx, gasUsedByTx); - addNewKeysToMaps(tx.getSender(), bucketCandidate, newReadKeys, newWrittenKeys); - return Optional.of(bucketCandidate.getGasUsed()); + sublistCandidate.addTransaction(tx, gasUsedByTx); + addNewKeysToMaps(tx.getSender(), sublistCandidate, newReadKeys, newWrittenKeys); + return Optional.of(sublistCandidate.getGasUsed()); } - private boolean bucketHasAvailableGas(Transaction tx, TransactionBucket bucketCandidate) { - return bucketCandidate.hasGasAvailable(GasCost.toGas(tx.getGasLimit())); + private boolean sublistHasAvailableGas(Transaction tx, TransactionSublist sublistCandidate) { + return sublistCandidate.hasGasAvailable(GasCost.toGas(tx.getGasLimit())); } public Optional addRemascTransaction(Transaction tx, long gasUsedByTx) { - TransactionBucket sequentialBucket = getSequentialBucket(); - sequentialBucket.addTransaction(tx, gasUsedByTx); - return Optional.of(sequentialBucket.getGasUsed()); + TransactionSublist sequentialSublist = getSequentialSublist(); + sequentialSublist.addTransaction(tx, gasUsedByTx); + return Optional.of(sequentialSublist.getGasUsed()); } - public long getGasUsedIn(Short bucketId) { + public long getGasUsedIn(Short sublistId) { - if (bucketId < 0 || bucketId >= buckets.size()) { + if (sublistId < 0 || sublistId >= sublists.size()) { throw new NoSuchElementException(); } - return this.buckets.get(bucketId).getGasUsed(); + return this.sublists.get(sublistId).getGasUsed(); } public List getTransactionsInOrder() { List txs = new ArrayList<>(); - for (TransactionBucket bucket: this.buckets) { - txs.addAll(bucket.getTransactions()); + for (TransactionSublist sublist: this.sublists) { + txs.addAll(sublist.getTransactions()); } return txs; } - public short[] getTransactionsPerBucketInOrder() { - List bucketSizes = new ArrayList<>(); - short bucketEdges = 0; + public short[] getTransactionsPerSublistInOrder() { + List sublistSizes = new ArrayList<>(); + short sublistEdges = 0; - for (TransactionBucket bucket: this.buckets) { - if (bucket.getTransactions().isEmpty() || bucket.isSequential()) { + for (TransactionSublist sublist: this.sublists) { + if (sublist.getTransactions().isEmpty() || sublist.isSequential()) { continue; } - bucketEdges += bucket.getTransactions().size(); - bucketSizes.add(bucketEdges); + sublistEdges += (short) sublist.getTransactions().size(); + sublistSizes.add(sublistEdges); } - short[] bucketOrder = new short[bucketSizes.size()]; + short[] sublistOrder = new short[sublistSizes.size()]; int i = 0; - for (Short size: bucketSizes) { - bucketOrder[i] = size; + for (Short size: sublistSizes) { + sublistOrder[i] = size; i++; } - return bucketOrder; + return sublistOrder; } - private void addNewKeysToMaps(RskAddress sender, TransactionBucket bucket, Set newReadKeys, Set newWrittenKeys) { + public long getGasUsedInSequential() { + return getSequentialSublist().getGasUsed(); + } + + private void addNewKeysToMaps(RskAddress sender, TransactionSublist sublist, Set newReadKeys, Set newWrittenKeys) { for (ByteArrayWrapper key : newReadKeys) { - Set bucketsAlreadyRead = bucketByReadKey.getOrDefault(key, new HashSet<>()); - bucketsAlreadyRead.add(bucket); - bucketByReadKey.put(key, bucketsAlreadyRead); + Set sublistsAlreadyRead = sublistsByReadKey.getOrDefault(key, new HashSet<>()); + sublistsAlreadyRead.add(sublist); + sublistsByReadKey.put(key, sublistsAlreadyRead); } - if (bucket.isSequential()) { - bucketBySender.put(sender, bucket); + if (sublist.isSequential()) { + sublistBySender.put(sender, sublist); return; } else { - bucketBySender.putIfAbsent(sender, bucket); + sublistBySender.putIfAbsent(sender, sublist); } for (ByteArrayWrapper key: newWrittenKeys) { - bucketByWrittenKey.putIfAbsent(key, bucket); + sublistByWrittenKey.putIfAbsent(key, sublist); } } - private Optional getBucketBySender(Transaction tx) { - return Optional.ofNullable(bucketBySender.get(tx.getSender())); + private Optional getSublistBySender(Transaction tx) { + return Optional.ofNullable(sublistBySender.get(tx.getSender())); } - private Optional getAvailableBucketWithLessUsedGas(long txGasLimit) { + private Optional getAvailableSublistWithLessUsedGas(long txGasLimit) { long gasUsed = Long.MAX_VALUE; - Optional bucketCandidate = Optional.empty(); + Optional sublistCandidate = Optional.empty(); - for (TransactionBucket bucket : buckets) { - if (!bucket.isSequential() && bucket.hasGasAvailable(txGasLimit) && bucket.getGasUsed() < gasUsed) { - bucketCandidate = Optional.of(bucket); - gasUsed = bucket.getGasUsed(); + for (TransactionSublist sublist : sublists) { + if (!sublist.isSequential() && sublist.hasGasAvailable(txGasLimit) && sublist.getGasUsed() < gasUsed) { + sublistCandidate = Optional.of(sublist); + gasUsed = sublist.getGasUsed(); } } - return bucketCandidate; + return sublistCandidate; } - private TransactionBucket getBucketCandidates(Transaction tx, Set newReadKeys, Set newWrittenKeys) { - Optional bucketCandidate = getBucketBySender(tx); + private TransactionSublist getSublistCandidates(Transaction tx, Set newReadKeys, Set newWrittenKeys) { + Optional sublistCandidate = getSublistBySender(tx); - if (bucketCandidate.isPresent() && bucketCandidate.get().isSequential()) { - return getSequentialBucket(); + if (sublistCandidate.isPresent() && sublistCandidate.get().isSequential()) { + return getSequentialSublist(); } // read - written for (ByteArrayWrapper newReadKey : newReadKeys) { - if (bucketByWrittenKey.containsKey(newReadKey)) { - TransactionBucket bucket = bucketByWrittenKey.get(newReadKey); - - if (bucketCandidate.isPresent() && !bucketCandidate.get().equals(bucket)) { - return getSequentialBucket(); - } else if (!bucketCandidate.isPresent()) { - bucketCandidate = Optional.of(bucket); - } + if (sublistByWrittenKey.containsKey(newReadKey)) { + TransactionSublist sublist = sublistByWrittenKey.get(newReadKey); + sublistCandidate = Optional.of(sublistCandidate.map(sc -> returnsSequentialIfBothAreDifferent(sc, sublist)).orElse(sublist)); } } + if (sublistCandidate.isPresent() && sublistCandidate.get().isSequential()) { + return sublistCandidate.get(); + } + for (ByteArrayWrapper newWrittenKey : newWrittenKeys) { // written - written, - if (bucketByWrittenKey.containsKey(newWrittenKey)) { - TransactionBucket bucket = bucketByWrittenKey.get(newWrittenKey); + if (sublistByWrittenKey.containsKey(newWrittenKey)) { + TransactionSublist sublist = sublistByWrittenKey.get(newWrittenKey); + sublistCandidate = Optional.of(sublistCandidate.map(sc -> returnsSequentialIfBothAreDifferent(sc, sublist)).orElse(sublist)); + } - if (bucketCandidate.isPresent() && !bucketCandidate.get().equals(bucket)) { - return getSequentialBucket(); - } else { - bucketCandidate = Optional.of(bucket); - } + if (sublistCandidate.isPresent() && sublistCandidate.get().isSequential()) { + return sublistCandidate.get(); } // read - written - if (bucketByReadKey.containsKey(newWrittenKey)) { - Set readBuckets = bucketByReadKey.get(newWrittenKey); + if (sublistsByReadKey.containsKey(newWrittenKey)) { + Set sublist = sublistsByReadKey.get(newWrittenKey); - if (readBuckets.size() > 1) { - return getSequentialBucket(); + if (sublist.size() > 1) { + return getSequentialSublist(); } - if (bucketCandidate.isPresent() && !readBuckets.contains(bucketCandidate.get())) { - return getSequentialBucket(); - } else { - bucketCandidate = Optional.of(readBuckets.iterator().next()); - } + sublistCandidate = Optional.of(sublistCandidate.map(sc -> getTransactionSublistForReadKeys(sublist, sc)).orElse(getNextSublist(sublist))); } } - return bucketCandidate.orElseGet(() -> getAvailableBucketWithLessUsedGas(GasCost.toGas(tx.getGasLimit())).orElseGet(this::getSequentialBucket)); + return sublistCandidate.orElseGet(() -> getAvailableSublistWithLessUsedGas(GasCost.toGas(tx.getGasLimit())).orElseGet(this::getSequentialSublist)); } - private TransactionBucket getSequentialBucket() { - return this.buckets.get(this.buckets.size()-1); + private TransactionSublist getTransactionSublistForReadKeys(Set sublist, TransactionSublist sc) { + if (!sublist.contains(sc)) { + return getSequentialSublist(); + } + + return getNextSublist(sublist); } - public long getGasUsedInSequential() { - return getSequentialBucket().getGasUsed(); + private TransactionSublist returnsSequentialIfBothAreDifferent(TransactionSublist sublist1, TransactionSublist sublist2) { + if (!sublist1.equals(sublist2)) { + return getSequentialSublist(); + } + return sublist1; + } + + private TransactionSublist getNextSublist(Set sublist) { + return sublist.iterator().next(); + } + + private TransactionSublist getSequentialSublist() { + return this.sublists.get(this.sublists.size()-1); } - private static class TransactionBucket { + private static class TransactionSublist { - final Short id; - final long gasLimit; - final boolean isSequential; - final List transactions; - long gasUsedInBucket; + private final long gasLimit; + private final boolean isSequential; + private final List transactions; + private long gasUsedInSublist; - public TransactionBucket(Short id, long bucketGasLimit, boolean isSequential) { - this.id = id; - this.gasLimit = bucketGasLimit; + public TransactionSublist(long sublistGasLimit, boolean isSequential) { + this.gasLimit = sublistGasLimit; this.isSequential = isSequential; this.transactions = new ArrayList<>(); - this.gasUsedInBucket = 0; + this.gasUsedInSublist = 0; } private void addTransaction(Transaction tx, long gasUsedByTx) { transactions.add(tx); - gasUsedInBucket = gasUsedInBucket + gasUsedByTx; + gasUsedInSublist = gasUsedInSublist + gasUsedByTx; } private boolean hasGasAvailable(long txGasLimit) { //TODO(JULI): Re-check a thousand of times this line. - long cumulativeGas = GasCost.add(gasUsedInBucket, txGasLimit); + long cumulativeGas = GasCost.add(gasUsedInSublist, txGasLimit); return cumulativeGas <= gasLimit; } public long getGasUsed() { - return gasUsedInBucket; + return gasUsedInSublist; } public List getTransactions() { - return this.transactions; + return new ArrayList<>(this.transactions); } public boolean isSequential() { diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java b/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java index 8d119d92a88..5aea1f0d32a 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java @@ -25,9 +25,6 @@ import java.util.Map; import java.util.Set; -//TODO(JULI): -// * Next step should be to check whether a key is written in the cache but also deleted in the same transaction. This key shouldn't be considered as a written key. - public class ReadWrittenKeysTracker implements IReadWrittenKeysTracker { private Map> threadByReadKey; private Map threadByWrittenKey; diff --git a/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java similarity index 90% rename from rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java rename to rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java index 0a89a03de2d..ebd9cb7d08c 100644 --- a/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionListsEdgesRule.java +++ b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java @@ -12,13 +12,13 @@ - The edges do not define any empty subset - The edges are in ascending order */ -public class ValidTxExecutionListsEdgesRule implements BlockValidationRule { +public class ValidTxExecutionSublistsEdgesRule implements BlockValidationRule { private static final Logger logger = LoggerFactory.getLogger("blockvalidator"); @Override public boolean isValid(Block block) { - short[] edges = block.getHeader().getTxExecutionListsEdges(); + short[] edges = block.getHeader().getTxExecutionSublistsEdges(); int nTxs = block.getTransactionsList().size(); if (edges == null) { diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java index e7b6aef4bbd..92c21f918c9 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java @@ -155,11 +155,11 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { ummRoot = rlpHeader.get(r++).getRLPRawData(); } - short[] txExecutionListsEdges = null; + short[] txExecutionSublistsEdges = null; if (activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)) { byte[] edgesBytes = rlpHeader.get(r++).getRLPRawData(); - txExecutionListsEdges = new short[edgesBytes.length / 2]; - ByteBuffer.wrap(edgesBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(txExecutionListsEdges); + txExecutionSublistsEdges = new short[edgesBytes.length / 2]; + ByteBuffer.wrap(edgesBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(txExecutionSublistsEdges); } byte[] bitcoinMergedMiningHeader = null; @@ -202,7 +202,7 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, new byte[0], minimumGasPrice, uncleCount, sealed, useRskip92Encoding, includeForkDetectionData, - ummRoot, txExecutionListsEdges + ummRoot, txExecutionSublistsEdges ); } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index aeb4b2657c8..b5bceacbe8a 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -106,7 +106,7 @@ public class BlockHeader { private byte[] miningForkDetectionData; /* Edges of the transaction execution lists */ - private short[] txExecutionListsEdges; + private short[] txExecutionSublistsEdges; private final byte[] ummRoot; @@ -135,7 +135,7 @@ public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, by byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, Coin minimumGasPrice, int uncleCount, boolean sealed, boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot, - short[] txExecutionListsEdges) { + short[] txExecutionSublistsEdges) { this.parentHash = parentHash; this.unclesHash = unclesHash; this.coinbase = coinbase; @@ -161,7 +161,7 @@ public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, by this.useRskip92Encoding = useRskip92Encoding; this.includeForkDetectionData = includeForkDetectionData; this.ummRoot = ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; - this.txExecutionListsEdges = txExecutionListsEdges; + this.txExecutionSublistsEdges = txExecutionSublistsEdges != null ? Arrays.copyOf(txExecutionSublistsEdges, txExecutionSublistsEdges.length) : null; } @VisibleForTesting @@ -378,9 +378,9 @@ public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProof fieldToEncodeList.add(RLP.encodeElement(this.ummRoot)); } - if (this.txExecutionListsEdges != null) { - byte[] edgesBytes = new byte[this.txExecutionListsEdges.length * 2]; - ByteBuffer.wrap(edgesBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(this.txExecutionListsEdges); + if (this.txExecutionSublistsEdges != null) { + byte[] edgesBytes = new byte[this.txExecutionSublistsEdges.length * 2]; + ByteBuffer.wrap(edgesBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(this.txExecutionSublistsEdges); fieldToEncodeList.add(RLP.encodeElement(edgesBytes)); } @@ -460,7 +460,7 @@ private String toStringWithSuffix(final String suffix) { toStringBuff.append(" timestamp=").append(timestamp).append(" (").append(Utils.longToDateTime(timestamp)).append(")").append(suffix); toStringBuff.append(" extraData=").append(toHexStringOrEmpty(extraData)).append(suffix); toStringBuff.append(" minGasPrice=").append(minimumGasPrice).append(suffix); - toStringBuff.append(" txExecutionListsEdges=").append(txExecutionListsEdges).append(suffix); + toStringBuff.append(" txExecutionSublistsEdges=").append(Arrays.toString(txExecutionSublistsEdges)).append(suffix); return toStringBuff.toString(); } @@ -626,9 +626,9 @@ public byte[] getUmmRoot() { return ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; } - public short[] getTxExecutionListsEdges() { return this.txExecutionListsEdges; } + public short[] getTxExecutionSublistsEdges() { return this.txExecutionSublistsEdges != null ? Arrays.copyOf(this.txExecutionSublistsEdges, this.txExecutionSublistsEdges.length) : null; } - public void setTxExecutionListsEdges(short[] txEdges) { - this.txExecutionListsEdges = txEdges; + public void setTxExecutionSublistsEdges(short[] edges) { + this.txExecutionSublistsEdges = edges != null? Arrays.copyOf(edges, edges.length) : null; } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java index 79f8d1dde31..57ac4d64be1 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java @@ -57,7 +57,7 @@ public class BlockHeaderBuilder { private byte[] bitcoinMergedMiningCoinbaseTransaction; private byte[] mergedMiningForkDetectionData; private byte[] ummRoot; - private short[] txExecutionListsEdges; + private short[] txExecutionSublistsEdges; private Coin minimumGasPrice; private int uncleCount; @@ -92,7 +92,7 @@ public BlockHeaderBuilder setCreateParallelCompliantHeader(boolean createParalle this.createParallelCompliantHeader = createParallelCompliantHeader; if (!createParallelCompliantHeader) { - this.txExecutionListsEdges = null; + this.txExecutionSublistsEdges = null; } return this; } @@ -264,13 +264,13 @@ public BlockHeaderBuilder setUmmRoot(byte[] ummRoot) { return this; } - public BlockHeaderBuilder setTxExecutionListsEdges(short[] edges) { + public BlockHeaderBuilder setTxExecutionSublistsEdges(short[] edges) { if (edges != null) { - this.txExecutionListsEdges = new short[edges.length]; - System.arraycopy(edges, 0, this.txExecutionListsEdges, 0, edges.length); + this.txExecutionSublistsEdges = new short[edges.length]; + System.arraycopy(edges, 0, this.txExecutionSublistsEdges, 0, edges.length); this.createParallelCompliantHeader = true; } else { - this.txExecutionListsEdges = null; + this.txExecutionSublistsEdges = null; this.createParallelCompliantHeader = false; } return this; @@ -333,10 +333,8 @@ public BlockHeader build() { } } - if (createParallelCompliantHeader) { - if (txExecutionListsEdges == null) { - txExecutionListsEdges = new short[0]; - } + if (createParallelCompliantHeader && txExecutionSublistsEdges == null) { + txExecutionSublistsEdges = new short[0]; } return new BlockHeader( @@ -350,7 +348,7 @@ public BlockHeader build() { mergedMiningForkDetectionData, minimumGasPrice, uncleCount, false, useRskip92Encoding, - includeForkDetectionData, ummRoot, txExecutionListsEdges + includeForkDetectionData, ummRoot, txExecutionSublistsEdges ); } } \ No newline at end of file diff --git a/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java b/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java index 09cb33a4f33..8716f1549d5 100644 --- a/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java +++ b/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java @@ -2,27 +2,29 @@ import co.rsk.core.bc.IReadWrittenKeysTracker; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; public class DummyReadWrittenKeysTracker implements IReadWrittenKeysTracker { - private final HashSet temporalReadKeys; - private final HashSet temporalWrittenKeys; + private final Map> temporalReadKeys; + private final Map temporalWrittenKeys; public DummyReadWrittenKeysTracker() { - this.temporalReadKeys = new HashSet<>(); - this.temporalWrittenKeys = new HashSet<>(); + this.temporalReadKeys = new HashMap<>(); + this.temporalWrittenKeys = new HashMap<>(); } @Override public Set getTemporalReadKeys() { - return temporalReadKeys; + return new HashSet<>(this.temporalReadKeys.keySet()); } @Override public Set getTemporalWrittenKeys() { - return temporalWrittenKeys; + return new HashSet<>(this.temporalWrittenKeys.keySet()); } @Override diff --git a/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java b/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java index 5b46c548162..a5c9e5fb866 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java +++ b/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java @@ -27,7 +27,6 @@ import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; diff --git a/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java b/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java index 372f539b81c..f6806a29b9e 100644 --- a/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java +++ b/rskj-core/src/test/java/co/rsk/blockchain/utils/BlockGenerator.java @@ -285,7 +285,7 @@ public Block createChildBlock(Block parent, List txs, List bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), 0); + void addTransactionIntoTheHandlerAndShouldBeAddedInTheFirstParallelSublist() { + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), 0); short[] expectedTransactionEdgeList = new short[]{1}; long expectedGasUsed = 0; - Assertions.assertTrue(bucketGasUsed.isPresent()); - Assertions.assertEquals(expectedGasUsed, (long) bucketGasUsed.get()); + Assertions.assertTrue(sublistGasUsed.isPresent()); + Assertions.assertEquals(expectedGasUsed, (long) sublistGasUsed.get()); List expectedListOfTxs = new ArrayList<>(); expectedListOfTxs.add(tx); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void addTransactionIntoTheHandlerAndShouldBeSubtractedGasUsedInTheBucket() { + void addTransactionIntoTheHandlerAndShouldBeSubtractedGasUsedInTheSublist() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); - - Assertions.assertTrue(bucketGasUsed.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Assertions.assertTrue(sublistGasUsed.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); } @Test - void addTwoTransactionsWithTheSameReadKeyAndShouldBeAddedInDifferentBuckets() { + void addTwoTransactionsWithTheSameReadKeyAndShouldBeAddedInADifferentSublist() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{1, 2}; Set readKeys = createASetAndAddKeys(aWrappedKey); - Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys, new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, readKeys, new HashSet<>(), gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed2.get()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx2, expectedTransactionEdgeList); } @Test - void addTwoTransactionsWithDifferentReadKeysShouldBeAddedInDifferentBuckets() { + void addTwoTransactionsWithDifferentReadKeysShouldBeAddedInADifferentSublist() { short[] expectedTransactionEdgeList = new short[]{1, 2}; HashSet readKeys = createASetAndAddKeys(aWrappedKey); @@ -138,14 +136,14 @@ void addTwoTransactionsWithDifferentReadKeysShouldBeAddedInDifferentBuckets() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx2); + Optional sublistGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx2); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx2, expectedTransactionEdgeList); } @Test @@ -156,14 +154,14 @@ void addTwoTransactionsWithSameWrittenKeysShouldBeAddedInTheSameBucket() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx+gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx+gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx2, expectedTransactionEdgeList); } @Test @@ -176,18 +174,18 @@ void addTwoTransactionsWithDifferentWrittenKeysShouldBeAddedInDifferentBuckets() long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx2); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx2); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx2, expectedTransactionEdgeList); } @Test - void addTwoTransactionsWithTheSameWrittenReadKeyShouldBeAddedInTheSameBucket() { + void addTwoTransactionsWithTheSameWrittenReadKeyShouldBeAddedInTheSameSublist() { short[] expectedTransactionEdgeList = new short[]{2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); @@ -196,18 +194,18 @@ void addTwoTransactionsWithTheSameWrittenReadKeyShouldBeAddedInTheSameBucket() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys, new HashSet<>(), gasUsedByTx2); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, readKeys, new HashSet<>(), gasUsedByTx2); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx+gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx+gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx2, expectedTransactionEdgeList); } @Test - void addTwoTransactionsWithTheSameReadWrittenKeyShouldBeAddedInTheSameBucket() { + void addTwoTransactionsWithTheSameReadWrittenKeyShouldBeAddedInTheSameSublist() { short[] expectedTransactionEdgeList = new short[]{2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); @@ -216,14 +214,14 @@ void addTwoTransactionsWithTheSameReadWrittenKeyShouldBeAddedInTheSameBucket() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + Optional sublistGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx+gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx+gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx2, expectedTransactionEdgeList); } @Test @@ -236,18 +234,18 @@ void addTwoTransactionsWithDifferentReadWrittenKeysShouldBeAddedInDifferentBucke long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + Optional sublistGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx2, expectedTransactionEdgeList); } @Test - void addTwoTransactionWithDifferentWrittenReadKeyShouldBeAddedInDifferentBuckets() { + void addTwoTransactionWithDifferentWrittenReadKeyShouldBeAddedInDifferentSublists() { short[] expectedTransactionEdgeList = new short[]{1, 2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); @@ -256,14 +254,14 @@ void addTwoTransactionWithDifferentWrittenReadKeyShouldBeAddedInDifferentBuckets long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(tx, writtenKeys, new HashSet<>(), gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys, new HashSet<>(), gasUsedByTx2); + Optional sublistGasUsed = handler.addTransaction(tx, writtenKeys, new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, readKeys, new HashSet<>(), gasUsedByTx2); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx2, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx2, expectedTransactionEdgeList); } @Test @@ -277,21 +275,21 @@ void addTwoIndependentTxsAndAThirdOneCollidingWithBothAndShouldBeAddedInTheSeque long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), differentWrittenKeys, gasUsedByTx2); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), differentWrittenKeys, gasUsedByTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed3 = handler.addTransaction(tx3, differentWrittenKeys, writtenKeys, gasUsedByTx3); + Optional sublistGasUsed3 = handler.addTransaction(tx3, differentWrittenKeys, writtenKeys, gasUsedByTx3); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(gasUsedByTx3, (long) bucketGasUsed3.get()); - Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent() && sublistGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx3, (long) sublistGasUsed3.get()); + Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialSublistNumber)); List expectedListOfTxs = Arrays.asList(tx, tx2, tx3); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test @@ -305,21 +303,21 @@ void addTwoDependentTxsWithTheSecondInSequentialAndAThirdOneCollidingWithBothAnd long totalGasInSequential = gasUsedByTx2 + gasUsedByTx3; - Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); - Optional bucketGasUsed3 = handler.addTransaction(tx3, new HashSet<>(), writtenKeys, gasUsedByTx3); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + Optional sublistGasUsed3 = handler.addTransaction(tx3, new HashSet<>(), writtenKeys, gasUsedByTx3); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(totalGasInSequential, (long) bucketGasUsed3.get()); - Assertions.assertEquals(totalGasInSequential, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent() && sublistGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(totalGasInSequential, (long) sublistGasUsed3.get()); + Assertions.assertEquals(totalGasInSequential, handler.getGasUsedIn(sequentialSublistNumber)); List expectedListOfTxs = Arrays.asList(bigTx, tx2, tx3); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test @@ -330,16 +328,16 @@ void addABigTransactionAndAnotherWithTheSameWrittenKeyAndTheLastOneShouldGoToSeq long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed2 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed2.get()); - Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(bigTx, tx, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(bigTx, tx, expectedTransactionEdgeList); } @Test @@ -352,16 +350,16 @@ void addABigTxAndAnotherWithTheSameReadWrittenKeyAndShouldGoToSequential() { long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(bigTx, readKeys, new HashSet<>(), gasUsedByBigTx); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(bigTx, readKeys, new HashSet<>(), gasUsedByBigTx); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed2 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed2.get()); - Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(bigTx, tx, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(bigTx, tx, expectedTransactionEdgeList); } @Test @@ -374,55 +372,55 @@ void addABigTxAndAnotherWithTheSameWrittenReadKeyAndShouldGoToSequential() { long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed2 = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed2.get()); - Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(bigTx, tx, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(bigTx, tx, expectedTransactionEdgeList); } @Test - void addTwoTransactionsWithTheSameSenderToTheSequentialBucketAndTheSecondShouldBeAddedCorrectly() { + void addTwoTransactionsWithTheSameSenderToTheSequentialSublistAndTheSecondShouldBeAddedCorrectly() { short[] expectedTransactionEdgeList = new short[]{1,2}; List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, tx, tx); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), GasCost.toGas(bigTx.getGasLimit())); handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), GasCost.toGas(bigTx2.getGasLimit())); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed3 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); - Assertions.assertTrue(bucketGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed3.get()); + Optional sublistGasUsed3 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Assertions.assertTrue(sublistGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed3.get()); - Optional bucketGasUsed4 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); - Assertions.assertTrue(bucketGasUsed4.isPresent()); - Assertions.assertEquals(2*gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); - Assertions.assertEquals(2*gasUsedByTx, (long) bucketGasUsed4.get()); + Optional sublistGasUsed4 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Assertions.assertTrue(sublistGasUsed4.isPresent()); + Assertions.assertEquals(2*gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(2*gasUsedByTx, (long) sublistGasUsed4.get()); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void twoTransactionWithTheSameSenderShouldBeInTheSameBucket() { + void twoTransactionWithTheSameSenderShouldBeInTheSameSublist() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{2}; - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(2*gasUsedByTx, (long) bucketGasUsed2.get()); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(2*gasUsedByTx, (long) sublistGasUsed2.get()); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx, expectedTransactionEdgeList); } @Test @@ -435,19 +433,20 @@ void ifATxHasTheSameSenderThatAnotherAlreadyAddedIntoTheSequentialShouldGoToTheS HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrapperKey); HashSet readKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - Optional bucketGasUsed3 = handler.addTransaction(tx3, readKeys, new HashSet<>(), gasUsedByTx3); - Optional bucketGasUsed4 = handler.addTransaction(tx3, new HashSet<>(), new HashSet<>(), gasUsedByTx3); - Assertions.assertTrue(bucketGasUsed3.isPresent() && bucketGasUsed4.isPresent()); - Assertions.assertEquals(gasUsedByTx3*2, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + + Optional sublistGasUsed3 = handler.addTransaction(tx3, readKeys, new HashSet<>(), gasUsedByTx3); + Optional sublistGasUsed4 = handler.addTransaction(tx3, new HashSet<>(), new HashSet<>(), gasUsedByTx3); + Assertions.assertTrue(sublistGasUsed3.isPresent() && sublistGasUsed4.isPresent()); + Assertions.assertEquals(gasUsedByTx3*2, handler.getGasUsedIn(sequentialSublistNumber)); List expectedListOfTxs = Arrays.asList(tx, tx2, tx3, tx3); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test @@ -460,22 +459,22 @@ void ifATxReadTwoDifferentWrittenKeysShouldGoToSequential() { HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrapperKey); HashSet readKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed3 = handler.addTransaction(tx3, readKeys, new HashSet<>(), gasUsedByTx3); - Assertions.assertTrue(bucketGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed3 = handler.addTransaction(tx3, readKeys, new HashSet<>(), gasUsedByTx3); + Assertions.assertTrue(sublistGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialSublistNumber)); List expectedListOfTxs = Arrays.asList(tx, tx2, tx3); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void ifATxWritesAKeyAlreadyReadByTwoTxsPlacedInDifferentBucketsShouldGoToTheSequential() { + void ifATxWritesAKeyAlreadyReadByTwoTxsPlacedInDifferentSublistsShouldGoToTheSequential() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{1,2}; @@ -484,22 +483,21 @@ void ifATxWritesAKeyAlreadyReadByTwoTxsPlacedInDifferentBucketsShouldGoToTheSequ HashSet readKeys2 = createASetAndAddKeys(aWrappedKey); HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed3 = handler.addTransaction(tx3, new HashSet<>(), writtenKeys, gasUsedByTx3); - Assertions.assertTrue(bucketGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed3 = handler.addTransaction(tx3, new HashSet<>(), writtenKeys, gasUsedByTx3); + Assertions.assertTrue(sublistGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialSublistNumber)); List expectedListOfTxs = Arrays.asList(tx, tx2, tx3); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } - @Test - void ifATxReadTwoKeysThatAreInDifferentBucketsShouldGoToTheSequential() { + void ifATxReadTwoKeysThatAreInDifferentSublistsShouldGoToTheSequential() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{1,2}; @@ -508,34 +506,34 @@ void ifATxReadTwoKeysThatAreInDifferentBucketsShouldGoToTheSequential() { HashSet readKeys2 = createASetAndAddKeys(aDifferentWrapperKey); HashSet writtenKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); - Optional bucketGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed3 = handler.addTransaction(tx3, new HashSet<>(), writtenKeys, gasUsedByTx3); - Assertions.assertTrue(bucketGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed3 = handler.addTransaction(tx3, new HashSet<>(), writtenKeys, gasUsedByTx3); + Assertions.assertTrue(sublistGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx3, handler.getGasUsedIn(sequentialSublistNumber)); List expectedListOfTxs = Arrays.asList(tx, tx2, tx3); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void ifATxCollidesWithAnotherOneThatAlsoHasTheSameSenderShouldGoIntoTheSameBucket() { + void ifATxCollidesWithAnotherOneThatAlsoHasTheSameSenderShouldGoIntoTheSameSublist() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(2*gasUsedByTx, (long) bucketGasUsed2.get()); - assertTwoTransactionsWereAddedProperlyIntoTheBuckets(tx, tx, expectedTransactionEdgeList); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(2*gasUsedByTx, (long) sublistGasUsed2.get()); + assertTwoTransactionsWereAddedProperlyIntoTheSublist(tx, tx, expectedTransactionEdgeList); } @Test @@ -545,24 +543,24 @@ void ifATransactionHasAnAlreadyAddedSenderButCollidesWithAnotherTxShouldBeAddedI short[] expectedTransactionEdgeList = new short[]{1,2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - Optional bucketGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); - Optional bucketGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys, gasUsedByTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed3 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); + Optional sublistGasUsed3 = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent() && sublistGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); List expectedListOfTxs = Arrays.asList(tx, tx2, tx); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void ifANewTxComesAndAllThePossibleBucketsAreFullTheTxShouldNotBeAdded() { + void ifANewTxComesAndAllThePossibleSublistAreFullTheTxShouldNotBeAdded() { long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); @@ -573,26 +571,26 @@ void ifANewTxComesAndAllThePossibleBucketsAreFullTheTxShouldNotBeAdded() { expectedListOfTxs.add(bigTx2); expectedListOfTxs.add(bigTx); - Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); - Optional bucketGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional sublistGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); - Optional bucketGasUsed4 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional sublistGasUsed4 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); - Assertions.assertFalse(bucketGasUsed4.isPresent()); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); + Assertions.assertFalse(sublistGasUsed4.isPresent()); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent() && sublistGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByBigTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed3.get()); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByBigTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed3.get()); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void ifBucketsAreFullAndAnIndependentTxComesShouldBeAddedInTheSequential() { + void ifSublistsAreFullAndAnIndependentTxComesShouldBeAddedInTheSequential() { short[] expectedTransactionEdgeList = new short[]{1,2}; List expectedListOfTxs = new ArrayList<>(); @@ -604,51 +602,51 @@ void ifBucketsAreFullAndAnIndependentTxComesShouldBeAddedInTheSequential() { long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); - Optional bucketGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); + Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional sublistGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional bucketGasUsed3 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + Optional sublistGasUsed3 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent() && bucketGasUsed3.isPresent()); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent() && sublistGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByBigTx2, (long) bucketGasUsed2.get()); - Assertions.assertEquals(gasUsedByTx, (long) bucketGasUsed3.get()); - Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByBigTx2, (long) sublistGasUsed2.get()); + Assertions.assertEquals(gasUsedByTx, (long) sublistGasUsed3.get()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void ifAllTheBucketsAreFullTheNewIndependentTxShouldNotBeIncluded() { + void ifAllTheSublistsAreFullTheNewIndependentTxShouldNotBeIncluded() { short[] expectedTransactionEdgeList = new short[]{1,2}; List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, bigTx); long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); - Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); - Optional bucketGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - - Optional bucketGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); - Assertions.assertTrue(bucketGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed3.get()); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); - - Optional emptyBucket = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), GasCost.toGas(tx.getGasLimit())); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); - Assertions.assertFalse(emptyBucket.isPresent()); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByBigTx2, (long) bucketGasUsed2.get()); + Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional sublistGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + + Optional sublistGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Assertions.assertTrue(sublistGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed3.get()); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + + Optional emptySublist = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), GasCost.toGas(tx.getGasLimit())); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertFalse(emptySublist.isPresent()); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByBigTx2, (long) sublistGasUsed2.get()); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void ifAllTheBucketsAreFullTheNewDependentTxShouldNotBeIncluded() { + void ifAllTheSublistsAreFullTheNewDependentTxShouldNotBeIncluded() { short[] expectedTransactionEdgeList = new short[]{1,2}; List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, bigTx); @@ -656,50 +654,50 @@ void ifAllTheBucketsAreFullTheNewDependentTxShouldNotBeIncluded() { long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - Optional bucketGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); - Optional bucketGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - - Optional bucketGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); - Assertions.assertTrue(bucketGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed3.get()); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); - - Optional emptyBucket = handler.addTransaction(tx, new HashSet<>(), writtenKeys, GasCost.toGas(tx.getGasLimit())); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialBucketNumber)); - Assertions.assertFalse(emptyBucket.isPresent()); - Assertions.assertTrue(bucketGasUsed.isPresent() && bucketGasUsed2.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) bucketGasUsed.get()); - Assertions.assertEquals(gasUsedByBigTx2, (long) bucketGasUsed2.get()); + Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); + Optional sublistGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + + Optional sublistGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Assertions.assertTrue(sublistGasUsed3.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed3.get()); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + + Optional emptySublist = handler.addTransaction(tx, new HashSet<>(), writtenKeys, GasCost.toGas(tx.getGasLimit())); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertFalse(emptySublist.isPresent()); + Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); + Assertions.assertEquals(gasUsedByBigTx2, (long) sublistGasUsed2.get()); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void aRemascTxAddedShouldBeInTheSequentialBucket() { + void aRemascTxAddedShouldBeInTheSequentialSublist() { List expectedListOfTxs = Collections.singletonList(tx); long gasUsedByTx = GasCost.toGas(bigTx.getGasLimit()); - Assertions.assertEquals(0, handler.getGasUsedIn(sequentialBucketNumber)); - Optional sequentialBucketGasUsed = handler.addRemascTransaction(tx, gasUsedByTx); + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + Optional sequentialSublistGasUsed = handler.addRemascTransaction(tx, gasUsedByTx); - Assertions.assertTrue(sequentialBucketGasUsed.isPresent()); - Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialBucketNumber)); - Assertions.assertEquals(gasUsedByTx, (long) sequentialBucketGasUsed.get()); + Assertions.assertTrue(sequentialSublistGasUsed.isPresent()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByTx, (long) sequentialSublistGasUsed.get()); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); } @Test void ifItsSequentialTheEdgesListShouldHaveSizeZero() { handler.addRemascTransaction(tx, GasCost.toGas(bigTx.getGasLimit())); - Assertions.assertEquals(0, handler.getTransactionsPerBucketInOrder().length); + Assertions.assertEquals(0, handler.getTransactionsPerSublistInOrder().length); } @Test - void callGetGasUsedInWithAnInvalidBucketShouldThrowAnError() { - short invalidBucketId = (short) (buckets+1); + void callGetGasUsedInWithAnInvalidSublistShouldThrowAnError() { + short invalidSublistId = (short) (sublists +1); try { - handler.getGasUsedIn(invalidBucketId); + handler.getGasUsedIn(invalidSublistId); Assertions.fail(); } catch (NoSuchElementException e) { Assertions.assertTrue(true); @@ -707,10 +705,10 @@ void callGetGasUsedInWithAnInvalidBucketShouldThrowAnError() { } @Test - void callGetGasUsedInWithAnInvalidBucketShouldThrowAnError2() { - short invalidBucketId = -1; + void callGetGasUsedInWithAnInvalidSublistShouldThrowAnError2() { + short invalidSublistId = -1; try { - handler.getGasUsedIn(invalidBucketId); + handler.getGasUsedIn(invalidSublistId); Assertions.fail(); } catch (NoSuchElementException e) { Assertions.assertTrue(true); @@ -720,9 +718,9 @@ private HashSet createASetAndAddKeys(ByteArrayWrapper... aKey) return new HashSet<>(Arrays.asList(aKey)); } - private void assertTwoTransactionsWereAddedProperlyIntoTheBuckets(Transaction tx, Transaction tx2, short[] expectedTransactionEdgeList) { + private void assertTwoTransactionsWereAddedProperlyIntoTheSublist(Transaction tx, Transaction tx2, short[] expectedTransactionEdgeList) { List expectedListOfTxs = Arrays.asList(tx, tx2); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); - Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerBucketInOrder()); + Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } } diff --git a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionListsEdgesTest.java b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionListsEdgesTest.java deleted file mode 100644 index dc6441d6481..00000000000 --- a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionListsEdgesTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package co.rsk.validators; - -import org.ethereum.core.Block; -import org.ethereum.core.BlockHeader; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.mockito.Mockito; - -import java.util.LinkedList; -import java.util.List; - -@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class ValidTxExecutionListsEdgesTest { - - private BlockHeader blockHeader; - private Block block; - - @BeforeAll - public void setUp() { - blockHeader = Mockito.mock(org.ethereum.core.BlockHeader.class); - block = Mockito.mock(Block.class); - List txList = Mockito.mock(LinkedList.class); - Mockito.when(block.getHeader()).thenReturn(blockHeader); - Mockito.when(block.getTransactionsList()).thenReturn(txList); - Mockito.when(txList.size()).thenReturn(10); - } - - @Test - public void blockWithValidEdges() { - Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{2, 5, 6}); - - ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); - Assertions.assertTrue(rule.isValid(block)); - } - - @Test - public void blockWithNullEdges() { - Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(null); - - ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); - Assertions.assertTrue(rule.isValid(block)); - } - - @Test - public void blockWithEmptyEdges() { - Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[0]); - - ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); - Assertions.assertTrue(rule.isValid(block)); - } - - @Test - public void blockWithTooManyEdges() { - Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{2, 5, 6, 8, 10, 12, 14}); - - ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); - Assertions.assertFalse(rule.isValid(block)); - } - - @Test - public void blockWithOutOfBoundsEdges() { - Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{12}); - - ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); - Assertions.assertFalse(rule.isValid(block)); - } - - @Test - public void blockWithNegativeEdge() { - Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{-2}); - - ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); - Assertions.assertFalse(rule.isValid(block)); - } - - @Test - public void blockWithEmptyListDefined() { - Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{2, 2}); - - ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); - Assertions.assertFalse(rule.isValid(block)); - } - - @Test - public void blockWithEdgesNotInOrder() { - Mockito.when(blockHeader.getTxExecutionListsEdges()).thenReturn(new short[]{2, 4, 3}); - - ValidTxExecutionListsEdgesRule rule = new ValidTxExecutionListsEdgesRule(); - Assertions.assertFalse(rule.isValid(block)); - } -} diff --git a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java new file mode 100644 index 00000000000..ddc776d5ecb --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java @@ -0,0 +1,92 @@ +package co.rsk.validators; + +import org.ethereum.core.Block; +import org.ethereum.core.BlockHeader; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.LinkedList; +import java.util.List; + +public class ValidTxExecutionSublistsEdgesTest { + + private BlockHeader blockHeader; + private Block block; + private List txSublist; + + @BeforeEach + public void setUp() { + blockHeader = Mockito.mock(org.ethereum.core.BlockHeader.class); + block = Mockito.mock(Block.class); + txSublist = Mockito.mock(LinkedList.class); + Mockito.when(block.getHeader()).thenReturn(blockHeader); + Mockito.when(block.getTransactionsList()).thenReturn(txSublist); + Mockito.when(txSublist.size()).thenReturn(10); + } + + @Test + void blockWithValidEdges() { + Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{2, 5, 6}); + + ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); + Assertions.assertTrue(rule.isValid(block)); + } + + @Test + void blockWithNullEdges() { + Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(null); + + ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); + Assertions.assertTrue(rule.isValid(block)); + } + + @Test + void blockWithEmptyEdges() { + Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[0]); + + ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); + Assertions.assertTrue(rule.isValid(block)); + } + + @Test + void blockWithTooManyEdges() { + Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{2, 5, 6, 8, 10, 12, 14}); + + ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + void blockWithOutOfBoundsEdges() { + Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{12}); + + ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + void blockWithNegativeEdge() { + Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{-2}); + + ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + void blockWithEmptyListDefined() { + Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{2, 2}); + + ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + void blockWithEdgesNotInOrder() { + Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{2, 4, 3}); + + ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); + Assertions.assertFalse(rule.isValid(block)); + } +} diff --git a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java index 81ac3c91d9f..aa9a87cf835 100644 --- a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java @@ -408,7 +408,7 @@ void createsHeaderWithParallelCompliant() { .setCreateParallelCompliantHeader(true) .build(); - assertArrayEquals(new short[0], header.getTxExecutionListsEdges()); + assertArrayEquals(new short[0], header.getTxExecutionSublistsEdges()); } @Test @@ -419,7 +419,7 @@ void createsHeaderWithoutParallelCompliant() { .setCreateParallelCompliantHeader(false) .build(); - assertArrayEquals(null, header.getTxExecutionListsEdges()); + assertArrayEquals(null, header.getTxExecutionSublistsEdges()); } @Test @@ -428,10 +428,10 @@ void createsHeaderWithEdges() { short[] edges = TestUtils.randomShortArray(4); BlockHeader header = builder - .setTxExecutionListsEdges(edges) + .setTxExecutionSublistsEdges(edges) .build(); - assertArrayEquals(edges, header.getTxExecutionListsEdges()); + assertArrayEquals(edges, header.getTxExecutionSublistsEdges()); } @Test @@ -439,10 +439,10 @@ void createsHeaderWithNullEdges() { BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); BlockHeader header = builder - .setTxExecutionListsEdges(null) + .setTxExecutionSublistsEdges(null) .build(); - assertArrayEquals(null, header.getTxExecutionListsEdges()); + assertArrayEquals(null, header.getTxExecutionSublistsEdges()); } @Test @@ -450,11 +450,11 @@ void createsHeaderWithNullEdgesButParallelCompliant() { BlockHeaderBuilder builder = new BlockHeaderBuilder(ActivationConfigsForTest.all()); BlockHeader header = builder - .setTxExecutionListsEdges(null) + .setTxExecutionSublistsEdges(null) .setCreateParallelCompliantHeader(true) .build(); - assertArrayEquals(new short[0], header.getTxExecutionListsEdges()); + assertArrayEquals(new short[0], header.getTxExecutionSublistsEdges()); } @Test @@ -464,10 +464,10 @@ void createsHeaderWithoutParallelCompliantButWithEdges() { BlockHeader header = builder .setCreateParallelCompliantHeader(false) - .setTxExecutionListsEdges(edges) + .setTxExecutionSublistsEdges(edges) .build(); - assertArrayEquals(edges, header.getTxExecutionListsEdges()); + assertArrayEquals(edges, header.getTxExecutionSublistsEdges()); } @Test @@ -476,11 +476,11 @@ void createsHeaderWithEdgesButWithoutParallelCompliant() { short[] edges = TestUtils.randomShortArray(4); BlockHeader header = builder - .setTxExecutionListsEdges(edges) + .setTxExecutionSublistsEdges(edges) .setCreateParallelCompliantHeader(false) .build(); - assertArrayEquals(null, header.getTxExecutionListsEdges()); + assertArrayEquals(null, header.getTxExecutionSublistsEdges()); } @Test @@ -489,10 +489,10 @@ void createsHeaderWithParallelCompliantButWithNullEdges() { BlockHeader header = builder .setCreateParallelCompliantHeader(true) - .setTxExecutionListsEdges(null) + .setTxExecutionSublistsEdges(null) .build(); - assertArrayEquals(null, header.getTxExecutionListsEdges()); + assertArrayEquals(null, header.getTxExecutionSublistsEdges()); } private static class CreateHeaderArgumentsProvider implements ArgumentsProvider { From 74f9c8cfaeecffceb14670b59860a35a5d96f480 Mon Sep 17 00:00:00 2001 From: Ilan <36084092+ilanolkies@users.noreply.github.com> Date: Mon, 27 Jun 2022 17:41:35 -0300 Subject: [PATCH 14/89] Research/parallel-tx/optimize writes (#1811) * Avoid tracking write for add balance 0 * Avoid tracking when writing the same value * Rename assertion * Remove condition for add balance zero It is considered in "write the same value" * Revert breaks --- .../org/ethereum/db/MutableRepository.java | 5 + .../co/rsk/db/RepositoryTrackingTest.java | 159 ++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java diff --git a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java index 557740966f4..ba591ddbfd0 100644 --- a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java +++ b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java @@ -384,6 +384,11 @@ private byte[] getAccountData(RskAddress addr) { } private void internalPut(byte[] key, byte[] value) { + // writes the same value + if (Arrays.equals(value, mutableTrie.get(key))) { + return; + } + tracker.addNewWrittenKey(new ByteArrayWrapper(key)); mutableTrie.put(key, value); } diff --git a/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java new file mode 100644 index 00000000000..19c9c4eda6b --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java @@ -0,0 +1,159 @@ +package co.rsk.db; +import co.rsk.core.Coin; +import co.rsk.core.RskAddress; +import co.rsk.core.bc.IReadWrittenKeysTracker; +import co.rsk.core.bc.ReadWrittenKeysTracker; +import co.rsk.trie.Trie; +import co.rsk.trie.TrieStore; +import co.rsk.trie.TrieStoreImpl; +import org.bouncycastle.util.encoders.Hex; +import org.ethereum.datasource.HashMapDB; +import org.ethereum.db.MutableRepository; +import org.ethereum.vm.DataWord; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + + +import java.math.BigInteger; + + +public class RepositoryTrackingTest { + public static final RskAddress COW = new RskAddress("CD2A3D9F938E13CD947EC05ABC7FE734DF8DD826"); + + private MutableRepository repository; + private MutableTrieImpl mutableTrie; + private TrieStore trieStore; + private IReadWrittenKeysTracker tracker; + + @BeforeEach + public void setUp() { + trieStore = new TrieStoreImpl(new HashMapDB()); + mutableTrie = new MutableTrieImpl(trieStore, new Trie(trieStore)); + tracker = new ReadWrittenKeysTracker(); + repository = new MutableRepository(mutableTrie, tracker); + } + + void assertRepositoryHasSize(int readRepoSize, int writtenRepoSize) { + Assertions.assertEquals(readRepoSize, tracker.getTemporalReadKeys().size()); + Assertions.assertEquals(writtenRepoSize, tracker.getTemporalWrittenKeys().size()); + } + + @Test + void tracksWriteInCreatedAccount() { + repository.createAccount(COW); + + assertRepositoryHasSize(0, 1); + } + + @Test + void tracksWriteInSetupContract() { + repository.createAccount(COW); + tracker.clear(); + + repository.setupContract(COW); + + assertRepositoryHasSize(0, 1); + } + + @Test + void tracksReadInIsExists() { + repository.isExist(COW); + + assertRepositoryHasSize(1, 0); + } + + @Test + void tracksReadInGetAccountState() { + repository.getAccountState(COW); + + assertRepositoryHasSize(1, 0); + } + + @Test + void tracksWriteInDelete() { + repository.createAccount(COW); + tracker.clear(); + + repository.delete(COW); + + assertRepositoryHasSize(0, 1); + } + + @Test + void tracksWriteInAddBalance() { + repository.createAccount(COW); + tracker.clear(); + + repository.addBalance(COW, new Coin(BigInteger.ONE)); + + assertRepositoryHasSize(1, 1); + } + + @Test + void doesntTrackWriteInAddBalanceZero() { + repository.createAccount(COW); + tracker.clear(); + + repository.addBalance(COW, Coin.ZERO); + + assertRepositoryHasSize(1, 0); + } + + @Test + void tracksReadOnGetStorageBytes() { + repository.createAccount(COW); + tracker.clear(); + + byte[] cowKey = Hex.decode("A1A2A3"); + + repository.getStorageBytes(COW, DataWord.valueOf(cowKey)); + + assertRepositoryHasSize(1, 0); + } + + @Test + void tracksWriteOnAddStorageBytes() { + repository.createAccount(COW); + tracker.clear(); + + byte[] cowValue = Hex.decode("A4A5A6"); + byte[] cowKey = Hex.decode("A1A2A3"); + + repository.addStorageBytes(COW, DataWord.valueOf(cowKey), cowValue); + + assertRepositoryHasSize(1, 1); + } + + @Test + void tracksWriteOnAddStorageDifferentBytes() { + repository.createAccount(COW); + tracker.clear(); + + byte[] cowValue = Hex.decode("A4A5A6"); + byte[] cowKey = Hex.decode("A1A2A3"); + byte[] cowKey2 = "key-c-2".getBytes(); + byte[] cowValue2 = "val-c-2".getBytes(); + + repository.addStorageBytes(COW, DataWord.valueOf(cowKey), cowValue); + repository.addStorageBytes(COW, DataWord.valueOf(cowKey2), cowValue2); + + assertRepositoryHasSize(1, 2); + } + + @Test + void doesntTrackWriteOnAddStorageSameBytes() { + repository.createAccount(COW); + + byte[] cowValue = Hex.decode("A4A5A6"); + byte[] cowKey = Hex.decode("A1A2A3"); + + repository.addStorageBytes(COW, DataWord.valueOf(cowKey), cowValue); + + tracker.clear(); + + repository.addStorageBytes(COW, DataWord.valueOf(cowKey), cowValue); + + assertRepositoryHasSize(1, 0); + } +} From fba1e7a2a2ad9a01b4a44baf77f4f358e27cbed2 Mon Sep 17 00:00:00 2001 From: julianlen Date: Wed, 6 Jul 2022 12:11:36 -0300 Subject: [PATCH 15/89] added validation for the rskip144 (#1824) --- rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index b270c41e099..b9662f52cea 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -260,7 +260,8 @@ private boolean validateLogsBloom(BlockHeader header, BlockResult result) { } public BlockResult executeForMining(Block block, BlockHeader parent, boolean discardInvalidTxs, boolean ignoreReadyToExecute, boolean saveState) { - if (block.getHeader().getTxExecutionSublistsEdges() != null) { + boolean rskip144Active = activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber()); + if (rskip144Active || (block.getHeader().getTxExecutionSublistsEdges() != null)) { return executeForMiningAfterRSKIP144(block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); } else { return executePreviousRSKIP144(null, 0, block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); From 29b92e2ca3a77ed0b26f5bd8ad6a75ac8eba0aeb Mon Sep 17 00:00:00 2001 From: Ilan <36084092+ilanolkies@users.noreply.github.com> Date: Tue, 19 Jul 2022 11:59:04 -0300 Subject: [PATCH 16/89] Fix write the same value - avoid write on add balance 0 (#1833) * Remove same written value check Ignored repository tracking tests * Add tests for addBalance * Avoid writing on addBalance(0) --- .../org/ethereum/db/MutableRepository.java | 9 ++-- .../co/rsk/db/RepositoryTrackingTest.java | 46 ++++++++++++++----- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java index ba591ddbfd0..b623d72d49c 100644 --- a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java +++ b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java @@ -296,6 +296,10 @@ public synchronized Coin getBalance(RskAddress addr) { public synchronized Coin addBalance(RskAddress addr, Coin value) { AccountState account = getAccountStateOrCreateNew(addr); + if (value == Coin.ZERO) { + return account.getBalance(); + } + Coin result = account.addToBalance(value); updateAccountState(addr, account); @@ -384,11 +388,6 @@ private byte[] getAccountData(RskAddress addr) { } private void internalPut(byte[] key, byte[] value) { - // writes the same value - if (Arrays.equals(value, mutableTrie.get(key))) { - return; - } - tracker.addNewWrittenKey(new ByteArrayWrapper(key)); mutableTrie.put(key, value); } diff --git a/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java index 19c9c4eda6b..61958fb7070 100644 --- a/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java +++ b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java @@ -91,17 +91,7 @@ void tracksWriteInAddBalance() { } @Test - void doesntTrackWriteInAddBalanceZero() { - repository.createAccount(COW); - tracker.clear(); - - repository.addBalance(COW, Coin.ZERO); - - assertRepositoryHasSize(1, 0); - } - - @Test - void tracksReadOnGetStorageBytes() { + void tracksReadOnGetStorageBytes () { repository.createAccount(COW); tracker.clear(); @@ -142,7 +132,7 @@ void tracksWriteOnAddStorageDifferentBytes() { } @Test - void doesntTrackWriteOnAddStorageSameBytes() { + void tracksWriteOnAddStorageSameBytes() { repository.createAccount(COW); byte[] cowValue = Hex.decode("A4A5A6"); @@ -154,6 +144,38 @@ void doesntTrackWriteOnAddStorageSameBytes() { repository.addStorageBytes(COW, DataWord.valueOf(cowKey), cowValue); + assertRepositoryHasSize(1, 1); + } + + public void tracksReadAndWriteOnAddBalanceOfNonExistent () { + repository.addBalance(COW, Coin.valueOf(1)); + + assertRepositoryHasSize(1, 1); + } + + public void tracksReadAndWriteOnAddBalanceZeroOfNonExistent () { + repository.addBalance(COW, Coin.valueOf(0)); + + assertRepositoryHasSize(1, 1); + } + + public void tracksReadAndWriteOnAddBalanceOfExistent () { + repository.addBalance(COW, Coin.valueOf(1)); + + tracker.clear(); + + repository.addBalance(COW, Coin.valueOf(1)); + + assertRepositoryHasSize(1, 1); + } + + public void doesntTrackWriteOnAddBalanceZeroOfExistent () { + repository.addBalance(COW, Coin.valueOf(1)); + + tracker.clear(); + + repository.addBalance(COW, Coin.valueOf(0)); + assertRepositoryHasSize(1, 0); } } From ecb2a65587001b007aa28e490421ecf6adf4ebc4 Mon Sep 17 00:00:00 2001 From: Ilan <36084092+ilanolkies@users.noreply.github.com> Date: Tue, 19 Jul 2022 13:42:07 -0300 Subject: [PATCH 17/89] Research/parallel-tx/Refactor validblock (#1794) * Refactor repeated code * Refactor repeated edges mocking * Modify test for too many edges - it was also hitting out of bounds * Refactor out of bounds validation If edges are in order, last edge decides if any was out of bounds * Rename test * Add case where first edge is zero First changed to prev = -1, then added test that failed, then set back prev = 0 * Refactor order verification * Research/refactoring code (#1817) * store and return a copy of txEdges * merged two ifs in the BlockHeaderBuilder * DummyTracker updated so it returns a copy of the maps * Unused hashmap import deleted from ProgramTraceProcessor * BlockResult returns and stores a copy of txEdges * big refactor in BlockExecutor * Refactor ParallelizeTransactionHandler * renaming Bucket to Sublist * renaming txExecutionListsEdges to txExecutionSublistEdges * simplification of Handler's code * InterruptedException solved properly in the BlockExecutor * Research/parallel-tx/optimize writes (#1811) * Avoid tracking write for add balance 0 * Avoid tracking when writing the same value * Rename assertion * Remove condition for add balance zero It is considered in "write the same value" * Revert breaks * Add test refactor back * Make remasc transaction not be in a parallelized sublist * Research/parallel-tx/Fix isRemascTransaction validation and pass correct index (#1822) * Fix isRemascTransaction validation and pass correct index * Remove -1 * Rename local data * Add tests for isRemascTransaction * Add tests for remasc transaction * Fix txindex Added test for block with rejected tx * Add missing txindex++ * Fix merge error Co-authored-by: julianlen --- .../java/co/rsk/core/bc/BlockExecutor.java | 5 +- .../ValidTxExecutionSublistsEdgesRule.java | 25 +-- .../java/org/ethereum/core/Transaction.java | 4 +- .../co/rsk/core/TransactionIsRemascTest.java | 154 ++++++++++++++++++ .../co/rsk/core/bc/BlockExecutorTest.java | 92 +++++++++-- .../ValidTxExecutionSublistsEdgesTest.java | 60 ++++--- 6 files changed, 297 insertions(+), 43 deletions(-) create mode 100644 rskj-core/src/test/java/co/rsk/core/TransactionIsRemascTest.java diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index b9662f52cea..62f0ef6de6a 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -601,7 +601,7 @@ private BlockResult executeForMiningAfterRSKIP144( TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( tx, - txindex++, + txindex, block.getCoinbase(), track, block, @@ -618,6 +618,7 @@ private BlockResult executeForMiningAfterRSKIP144( } loggingDiscardedBlock(block, tx); + txindex++; continue; } @@ -633,6 +634,7 @@ private BlockResult executeForMiningAfterRSKIP144( return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } loggingDiscardedBlock(block, tx); + txindex++; continue; } @@ -661,6 +663,7 @@ private BlockResult executeForMiningAfterRSKIP144( loggingExecuteTxAndReceipt(block, i, tx); i++; + txindex++; receiptsByTx.put(tx, receipt); diff --git a/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java index ebd9cb7d08c..637b5b71c04 100644 --- a/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java +++ b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java @@ -19,29 +19,34 @@ public class ValidTxExecutionSublistsEdgesRule implements BlockValidationRule { @Override public boolean isValid(Block block) { short[] edges = block.getHeader().getTxExecutionSublistsEdges(); - int nTxs = block.getTransactionsList().size(); - if (edges == null) { + if (edges == null || edges.length == 0) { return true; } + if (edges.length > Constants.getTransactionExecutionThreads()) { logger.warn("Invalid block: number of execution lists edges is greater than number of execution threads ({} vs {})", edges.length, Constants.getTransactionExecutionThreads()); return false; } - short prev = 0; - for (short edge : edges) { - if (edge <= prev) { + if (edges[0] <= 0) { + logger.warn("Invalid block: execution list edge is out of bounds"); + return false; + } + + for (int i = 0; i < edges.length - 1; i++) { + if (edges[i] >= edges[i + 1]) { logger.warn("Invalid block: execution lists edges are not in ascending order"); return false; } - if (edge > nTxs) { - logger.warn("Invalid block: execution list edge is out of bounds: {}", edge); - return false; - } - prev = edge; } + + if (edges[edges.length-1] > block.getTransactionsList().size() - 1) { + logger.warn("Invalid block: execution list edge is out of bounds"); + return false; + } + return true; } } diff --git a/rskj-core/src/main/java/org/ethereum/core/Transaction.java b/rskj-core/src/main/java/org/ethereum/core/Transaction.java index 36096fd7bfa..0e3b34aea33 100644 --- a/rskj-core/src/main/java/org/ethereum/core/Transaction.java +++ b/rskj-core/src/main/java/org/ethereum/core/Transaction.java @@ -545,7 +545,9 @@ private boolean checkRemascAddress() { } private boolean checkRemascTxZeroValues() { - if (null != getData() || null != getSignature()) { + byte[] currentData = getData(); + + if ((null != currentData && currentData.length != 0) || null != getSignature()) { return false; } diff --git a/rskj-core/src/test/java/co/rsk/core/TransactionIsRemascTest.java b/rskj-core/src/test/java/co/rsk/core/TransactionIsRemascTest.java new file mode 100644 index 00000000000..8a6c727f023 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/TransactionIsRemascTest.java @@ -0,0 +1,154 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package co.rsk.core; + +import org.bouncycastle.util.encoders.Hex; +import org.ethereum.core.*; +import org.ethereum.crypto.ECKey; +import org.ethereum.crypto.HashUtil; +import org.ethereum.util.ByteUtil; +import org.ethereum.util.RLP; +import org.ethereum.vm.PrecompiledContracts; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TransactionIsRemascTest { + int txPosition = 6; + int txsSize = 7; + RskAddress destination = PrecompiledContracts.REMASC_ADDR; + byte[] data = null; + Coin value = Coin.ZERO; + byte[] gasPrice = Hex.decode("00"); + byte[] gasLimit = Hex.decode("00"); + + private Transaction buildTx( + RskAddress destination, + byte[] data, + Coin value, + byte[] gasPrice, + byte[] gasLimit + ) { + return Transaction.builder() + .destination(destination) + .data(data) + .value(value) + .gasPrice(gasPrice) + .gasLimit(gasLimit) + .build(); + } + + private void assertIsRemascTransaction( + RskAddress destination, + byte[] data, + Coin value, + byte[] gasPrice, + byte[] gasLimit, + int txPosition, + int txsSize + ) { + Transaction tx = buildTx(destination, data, value, gasPrice, gasLimit); + + Assertions.assertTrue(tx.isRemascTransaction(txPosition, txsSize)); + } + + private void assertIsNotRemascTransaction( + Transaction tx, + int txPosition, + int txsSize + ) { + Assertions.assertFalse(tx.isRemascTransaction(txPosition, txsSize)); + } + + private void assertIsNotRemascTransaction( + RskAddress destination, + byte[] data, + Coin value, + byte[] gasPrice, + byte[] gasLimit, + int txPosition, + int txsSize + ) { + Transaction tx = buildTx(destination, data, value, gasPrice, gasLimit); + + assertIsNotRemascTransaction(tx, txPosition, txsSize); + } + + @Test + void validRemascTransactionNullData() { + assertIsRemascTransaction(destination, data, value, gasPrice, gasLimit, txPosition, txsSize); + } + + @Test + void validRemascTransactionEmptyData() { + byte[] data = {}; + assertIsRemascTransaction(destination, data, value, gasPrice, gasLimit, txPosition, txsSize); + } + + @Test + void notRemascTransactionNotLastTx() { + int txPosition = 3; + assertIsNotRemascTransaction(destination, data, value, gasPrice, gasLimit, txPosition, txsSize); + } + + @Test + void notRemascTransactionNotEmptyData() { + byte[] data = { 1, 2, 3, 4 }; + assertIsNotRemascTransaction(destination, data, value, gasPrice, gasLimit, txPosition, txsSize); + } + + @Test + void notRemascTransactionNotNullSig() { + byte[] senderPrivateKey = HashUtil.keccak256("cow".getBytes()); + Transaction tx = buildTx(destination, data, value, gasPrice, gasLimit); + tx.sign(senderPrivateKey); + + assertIsNotRemascTransaction(tx, txPosition, txsSize); + } + + @Test + void notRemascTransactionReceiverIsNotRemasc() { + byte[] privateKey = HashUtil.keccak256("cat".getBytes()); + ECKey ecKey = ECKey.fromPrivate(privateKey); + RskAddress destination = RLP.parseRskAddress(ByteUtil.cloneBytes(ecKey.getAddress())); + + assertIsNotRemascTransaction(destination, data, value, gasPrice, gasLimit, txPosition, txsSize); + } + + + @Test + void notRemascTransactionValueIsNotZero() { + Coin value = Coin.valueOf(10); + assertIsNotRemascTransaction(destination, data, value, gasPrice, gasLimit, txPosition, txsSize); + } + + + @Test + void notRemascTransactionGasPriceIsNotZero() { + byte[] gasPrice = { 10 }; + assertIsNotRemascTransaction(destination, data, value, gasPrice, gasLimit, txPosition, txsSize); + } + + + @Test + void notRemascTransactionGasLimitIsNotZero() { + byte[] gasLimit = { 10 }; + assertIsNotRemascTransaction(destination, data, value, gasPrice, gasLimit, txPosition, txsSize); + } +} + diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index 9176732c1ef..bfc57a692e9 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -55,7 +55,6 @@ import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -334,10 +333,8 @@ void executeAndFillBlockWithOneTransaction(boolean activeRskip144) { Assertions.assertEquals(3000000, new BigInteger(1, block.getGasLimit()).longValue()); } - @ParameterizedTest - @ValueSource(booleans = {true, false}) - void executeAndFillBlockWithTxToExcludeBecauseSenderHasNoBalance(boolean activeRskip144) { - doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + + private Block createBlockWithExcludedTransaction(boolean withRemasc, boolean activeRskip144) { TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Repository repository = new MutableRepository(new MutableTrieImpl(trieStore, new Trie(trieStore))); @@ -379,6 +376,16 @@ void executeAndFillBlockWithTxToExcludeBecauseSenderHasNoBalance(boolean activeR txs.add(tx); txs.add(tx2); + + List expectedTxList = new ArrayList(); + expectedTxList.add(tx); + + if (withRemasc) { + Transaction remascTx = new RemascTransaction(1L); + txs.add(remascTx); + expectedTxList.add(remascTx); + } + List uncles = new ArrayList<>(); BlockGenerator blockGenerator = new BlockGenerator(Constants.regtest(), activationConfig); @@ -388,16 +395,27 @@ void executeAndFillBlockWithTxToExcludeBecauseSenderHasNoBalance(boolean activeR executor.executeAndFill(block, genesis.getHeader()); + Assertions.assertEquals(tx, block.getTransactionsList().get(0)); + Assertions.assertArrayEquals( + calculateTxTrieRoot(expectedTxList, block.getNumber()), + block.getTxTrieRoot() + ); + + return block; + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void executeAndFillBlockWithTxToExcludeBecauseSenderHasNoBalance(boolean activeRskip144) { + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + Block block = createBlockWithExcludedTransaction(false, activeRskip144); + short[] expectedEdges = activeRskip144 ? new short[]{(short) block.getTransactionsList().size()} : null; Assertions.assertArrayEquals(expectedEdges, block.getHeader().getTxExecutionSublistsEdges()); // Check tx2 was excluded + Assertions.assertEquals(1, block.getTransactionsList().size()); - Assertions.assertEquals(tx, block.getTransactionsList().get(0)); - Assertions.assertArrayEquals( - calculateTxTrieRoot(Collections.singletonList(tx), block.getNumber()), - block.getTxTrieRoot() - ); Assertions.assertEquals(3141592, new BigInteger(1, block.getGasLimit()).longValue()); } @@ -720,6 +738,60 @@ void executeParallelBlockTwice(boolean activeRskip144) { Assertions.assertArrayEquals(block1.getHash().getBytes(), block2.getHash().getBytes()); } + private void testBlockWithTxTxEdgesMatchAndRemascTxIsAtLastPosition (int txAmount, short [] expectedSublistsEdges, Boolean activeRskip144) { + Block block = getBlockWithNIndependentTransactions(txAmount, BigInteger.valueOf(21000), true); + + assertBlockResultHasTxEdgesAndRemascAtLastPosition(block, txAmount, expectedSublistsEdges, activeRskip144); + } + + private void assertBlockResultHasTxEdgesAndRemascAtLastPosition (Block block, int txAmount, short [] expectedSublistsEdges, Boolean activeRskip144) { + Block parent = blockchain.getBestBlock(); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); + + int expectedTxSize = txAmount + 1; + + Assertions.assertArrayEquals(expectedSublistsEdges, blockResult.getTxEdges()); + Assertions.assertEquals(expectedTxSize, blockResult.getExecutedTransactions().size()); + Assertions.assertTrue(blockResult.getExecutedTransactions().get(txAmount).isRemascTransaction(txAmount, expectedTxSize)); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void blockWithOnlyRemascShouldGoToSequentialSublist (boolean activeRskip144) { + if (!activeRskip144) return; + testBlockWithTxTxEdgesMatchAndRemascTxIsAtLastPosition(0, new short[]{}, activeRskip144); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void blockWithOneTxRemascShouldGoToSequentialSublist (boolean activeRskip144) { + if (!activeRskip144) return; + testBlockWithTxTxEdgesMatchAndRemascTxIsAtLastPosition(1, new short[]{ 1 }, activeRskip144); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void blockWithManyTxsRemascShouldGoToSequentialSublist (boolean activeRskip144) { + if (!activeRskip144) return; + testBlockWithTxTxEdgesMatchAndRemascTxIsAtLastPosition(3, new short[]{ 1, 2, 3 }, activeRskip144); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void blockWithMoreThanThreadsTxsRemascShouldGoToSequentialSublist (boolean activeRskip144) { + if (!activeRskip144) return; + testBlockWithTxTxEdgesMatchAndRemascTxIsAtLastPosition(5, new short[]{ 2, 3, 4, 5 }, activeRskip144); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void blockWithExcludedTransactionHasRemascInSequentialSublist (boolean activeRskip144) { + if (!activeRskip144) return; + Block block = createBlockWithExcludedTransaction(true, activeRskip144); + assertBlockResultHasTxEdgesAndRemascAtLastPosition(block, 0, new short[]{}, activeRskip144); + } + @ParameterizedTest @ValueSource(booleans = {true, false}) void validateStateRootWithRskip126DisabledAndValidStateRoot(boolean activeRskip144) { diff --git a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java index ddc776d5ecb..9731540bebb 100644 --- a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java +++ b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java @@ -14,79 +14,97 @@ public class ValidTxExecutionSublistsEdgesTest { private BlockHeader blockHeader; private Block block; - private List txSublist; + private List txList; + private ValidTxExecutionSublistsEdgesRule rule; @BeforeEach public void setUp() { blockHeader = Mockito.mock(org.ethereum.core.BlockHeader.class); block = Mockito.mock(Block.class); - txSublist = Mockito.mock(LinkedList.class); + txList = Mockito.mock(LinkedList.class); Mockito.when(block.getHeader()).thenReturn(blockHeader); - Mockito.when(block.getTransactionsList()).thenReturn(txSublist); - Mockito.when(txSublist.size()).thenReturn(10); + Mockito.when(block.getTransactionsList()).thenReturn(txList); + Mockito.when(txList.size()).thenReturn(10); + + rule = new ValidTxExecutionSublistsEdgesRule(); + } + + private void mockGetTxExecutionListsEdges (short[] edges) { + Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(edges); } + // valid cases @Test void blockWithValidEdges() { - Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{2, 5, 6}); + mockGetTxExecutionListsEdges(new short[]{2, 5, 6}); - ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); Assertions.assertTrue(rule.isValid(block)); } @Test void blockWithNullEdges() { - Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(null); + mockGetTxExecutionListsEdges(null); - ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); Assertions.assertTrue(rule.isValid(block)); } @Test void blockWithEmptyEdges() { - Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[0]); + mockGetTxExecutionListsEdges(new short[0]); - ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); Assertions.assertTrue(rule.isValid(block)); } + // invalid cases @Test void blockWithTooManyEdges() { - Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{2, 5, 6, 8, 10, 12, 14}); + mockGetTxExecutionListsEdges(new short[]{1, 2, 3, 4, 5}); + + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + void blockWithOutOfBoundsEdgesBecauseOfRemascTx() { + // include the last tx in a parallelized thread + // shouldn't be valid because the last transaction + // is the remasc transaction and cannot be parallelized + mockGetTxExecutionListsEdges(new short[]{10}); - ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); Assertions.assertFalse(rule.isValid(block)); } @Test void blockWithOutOfBoundsEdges() { - Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{12}); + mockGetTxExecutionListsEdges(new short[]{12}); - ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); Assertions.assertFalse(rule.isValid(block)); } @Test void blockWithNegativeEdge() { - Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{-2}); + mockGetTxExecutionListsEdges(new short[]{-2}); + + Assertions.assertFalse(rule.isValid(block)); + } + + @Test + void blockWithEdgeZero() { + mockGetTxExecutionListsEdges(new short[]{0, 2}); - ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); Assertions.assertFalse(rule.isValid(block)); } @Test - void blockWithEmptyListDefined() { - Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{2, 2}); + void blockWithRepeatedEdge() { + mockGetTxExecutionListsEdges(new short[]{2, 2}); - ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); Assertions.assertFalse(rule.isValid(block)); } @Test void blockWithEdgesNotInOrder() { - Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(new short[]{2, 4, 3}); + mockGetTxExecutionListsEdges(new short[]{2, 4, 3}); - ValidTxExecutionSublistsEdgesRule rule = new ValidTxExecutionSublistsEdgesRule(); Assertions.assertFalse(rule.isValid(block)); } } From 90e5ea8fe0ed87f0756f8d62d31ce3d564b96db6 Mon Sep 17 00:00:00 2001 From: julianlen Date: Fri, 23 Sep 2022 12:28:41 -0300 Subject: [PATCH 18/89] Research/main parallel tx revision (#1865) * B1. Confusing Naming of conflict sets - Solved * B3. Incorrect use of LongAccumulator * B4. Truncated BigInteger Pays Incorrect Fees * B5. Setup of new Precompiles is not correctly tracked * B7. Determinism even on conflict (#1873) * 2X performance improvement, and better tests (#1884) Co-authored-by: Sergio Demian Lerner <1752347+SergioDemianLerner@users.noreply.github.com> --- .../rsk/core/TransactionExecutorFactory.java | 19 +- .../co/rsk/core/TransactionListExecutor.java | 57 ++- .../java/co/rsk/core/bc/BlockExecutor.java | 105 ++-- .../rsk/core/bc/IReadWrittenKeysTracker.java | 12 +- .../bc/ParallelizeTransactionHandler.java | 36 +- .../rsk/core/bc/ReadWrittenKeysTracker.java | 120 +++-- .../ethereum/core/TransactionExecutor.java | 10 +- .../db/DummyReadWrittenKeysTracker.java | 30 +- .../co/rsk/core/bc/BlockExecutorTest.java | 2 +- .../core/bc/ReadWrittenKeysTrackerTest.java | 477 ++++++++++++++---- .../co/rsk/db/RepositoryTrackingTest.java | 4 +- .../src/test/java/co/rsk/vm/MinerHelper.java | 10 +- .../core/TransactionExecutorTest.java | 9 +- .../runners/StateTestRunner.java | 8 +- 14 files changed, 617 insertions(+), 282 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java b/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java index d41e446fefb..fb500c9afcd 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java @@ -29,7 +29,6 @@ import java.util.HashSet; import java.util.Set; -import java.util.concurrent.atomic.LongAccumulator; public class TransactionExecutorFactory { @@ -65,17 +64,7 @@ public TransactionExecutor newInstance( Repository track, Block block, long totalGasUsed) { - return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, new LongAccumulator(Long::sum, 0)); - } - - public TransactionExecutor newInstance( - Transaction tx, - int txindex, - RskAddress coinbase, - Repository track, - Block block, - long totalGasUsed, LongAccumulator remascFees) { - return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, false, 0, new HashSet<>(), remascFees); + return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, false, 0, new HashSet<>()); } public TransactionExecutor newInstance( @@ -87,8 +76,7 @@ public TransactionExecutor newInstance( long totalGasUsed, boolean vmTrace, int vmTraceOptions, - Set deletedAccounts, - LongAccumulator remascFees) { + Set deletedAccounts) { // Tracing configuration is scattered across different files (VM, DetailedProgramTrace, etc.) and // TransactionExecutor#extractTrace doesn't work when called independently. // It would be great to decouple from VmConfig#vmTrace, but sadly that's a major refactor we can't do now. @@ -121,8 +109,7 @@ public TransactionExecutor newInstance( config.isRemascEnabled(), precompiledContracts, deletedAccounts, - blockTxSignatureCache, - remascFees + blockTxSignatureCache ); } } diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index a5905f5e84b..802d22b7295 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -1,6 +1,5 @@ package co.rsk.core; -import co.rsk.core.bc.IReadWrittenKeysTracker; import co.rsk.crypto.Keccak256; import org.ethereum.core.*; import org.ethereum.vm.DataWord; @@ -12,7 +11,6 @@ import javax.annotation.Nullable; import java.util.*; import java.util.concurrent.Callable; -import java.util.concurrent.atomic.LongAccumulator; public class TransactionListExecutor implements Callable { @@ -20,7 +18,6 @@ public class TransactionListExecutor implements Callable { private final TransactionExecutorFactory transactionExecutorFactory; private final List transactions; - private final IReadWrittenKeysTracker readWrittenKeysTracker; private final Block block; private final Repository track; private final boolean vmTrace; @@ -32,16 +29,14 @@ public class TransactionListExecutor implements Callable { private final Map receipts; private final Map transactionResults; private final ProgramTraceProcessor programTraceProcessor; - private final LongAccumulator remascFees; - private final LongAccumulator accumulatedFees; - private final LongAccumulator accumulatedGas; + private Coin totalFees; + private long totalGas; private int i; private final boolean registerProgramResults; public TransactionListExecutor( List transactions, - IReadWrittenKeysTracker readWrittenKeysTracker, Block block, TransactionExecutorFactory transactionExecutorFactory, Repository track, @@ -55,11 +50,7 @@ public TransactionListExecutor( Map transactionResults, boolean registerProgramResults, @Nullable ProgramTraceProcessor programTraceProcessor, - LongAccumulator remascFees, - LongAccumulator accumulatedFees, - LongAccumulator accumulatedGas, int firstTxIndex) { - this.readWrittenKeysTracker = readWrittenKeysTracker; this.block = block; this.transactionExecutorFactory = transactionExecutorFactory; this.track = track; @@ -74,10 +65,9 @@ public TransactionListExecutor( this.registerProgramResults = registerProgramResults; this.transactionResults = transactionResults; this.programTraceProcessor = programTraceProcessor; - this.accumulatedFees = accumulatedFees; - this.accumulatedGas = accumulatedGas; + this.totalFees = Coin.ZERO; + this.totalGas = 0L; this.i = firstTxIndex; - this.remascFees = remascFees; } @Override @@ -95,15 +85,10 @@ public Boolean call() { totalGasUsed, vmTrace, vmTraceOptions, - deletedAccounts, - remascFees + deletedAccounts ); boolean transactionExecuted = txExecutor.executeTransaction(); - if (readWrittenKeysTracker.hasCollided()) { - return false; - } - if (!acceptInvalidTransactions && !transactionExecuted) { if (!discardInvalidTxs) { logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", @@ -158,9 +143,37 @@ public Boolean call() { logger.trace("tx[{}] done", i); } - accumulatedGas.accumulate(totalGasUsed); - accumulatedFees.accumulate(totalPaidFees.asBigInteger().longValue()); + totalGas += totalGasUsed; + totalFees = totalFees.add(totalPaidFees); return true; } + + public Repository getRepository() { + return this.track; + } + + public Set getDeletedAccounts() { + return new HashSet<>(this.deletedAccounts); + } + + public Map getReceipts() { + return this.receipts; + } + + public Map getExecutedTransactions() { + return this.executedTransactions; + } + + public Map getTransactionResults() { + return this.transactionResults; + } + + public Coin getTotalFees() { + return this.totalFees; + } + + public long getTotalGas() { + return this.totalGas; + } } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 62f0ef6de6a..e79d58e5975 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -18,10 +18,7 @@ package co.rsk.core.bc; -import co.rsk.core.Coin; -import co.rsk.core.RskAddress; -import co.rsk.core.TransactionExecutorFactory; -import co.rsk.core.TransactionListExecutor; +import co.rsk.core.*; import co.rsk.crypto.Keccak256; import co.rsk.db.RepositoryLocator; import co.rsk.metrics.profilers.Metric; @@ -43,7 +40,6 @@ import javax.annotation.Nullable; import java.util.*; import java.util.concurrent.*; -import java.util.concurrent.atomic.LongAccumulator; import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP126; import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP85; @@ -336,7 +332,6 @@ private BlockResult executePreviousRSKIP144( List receipts = new ArrayList<>(); List executedTransactions = new ArrayList<>(); Set deletedAccounts = new HashSet<>(); - LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); int txindex = 0; @@ -352,8 +347,8 @@ private BlockResult executePreviousRSKIP144( totalGasUsed, vmTrace, vmTraceOptions, - deletedAccounts, - remascFees); + deletedAccounts + ); boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { @@ -395,7 +390,7 @@ private BlockResult executePreviousRSKIP144( loggingTxDone(); } - addFeesToRemasc(remascFees, track); + addFeesToRemasc(totalPaidFees, track); saveOrCommitTrackState(saveState, track); BlockResult result = new BlockResult( @@ -437,19 +432,13 @@ private BlockResult executeParallel( // of the repository to the state post execution, so it's necessary to get it to // the state prior execution again. Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE); - IReadWrittenKeysTracker readWrittenKeysTracker = new ReadWrittenKeysTracker(); - Repository track = repositoryLocator.startTrackingAt(parent, readWrittenKeysTracker); - maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); - - LongAccumulator totalGasUsed = new LongAccumulator(Long::sum, 0); - LongAccumulator totalPaidFees = new LongAccumulator(Long::sum, 0); - Map receipts = new ConcurrentSkipListMap<>(); - Map executedTransactions = new ConcurrentSkipListMap<>(); - Set deletedAccounts = ConcurrentHashMap.newKeySet(); - LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); - ExecutorService executorService = Executors.newFixedThreadPool(Constants.getTransactionExecutionThreads()); ExecutorCompletionService completionService = new ExecutorCompletionService<>(executorService); + List transactionListExecutors = new ArrayList<>(); + ReadWrittenKeysTracker aTracker = new ReadWrittenKeysTracker(); + Repository track = repositoryLocator.startTrackingAt(parent, aTracker); + maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); + aTracker.clear(); int nTasks = 0; // execute parallel subsets of transactions @@ -458,26 +447,23 @@ private BlockResult executeParallel( List sublist = block.getTransactionsList().subList(start, end); TransactionListExecutor txListExecutor = new TransactionListExecutor( sublist, - readWrittenKeysTracker, block, transactionExecutorFactory, - track, + track.startTracking(), vmTrace, vmTraceOptions, - deletedAccounts, + new HashSet<>(), discardInvalidTxs, acceptInvalidTransactions, - receipts, - executedTransactions, - transactionResults, + new HashMap<>(), + new HashMap<>(), + new HashMap<>(), registerProgramResults, programTraceProcessor, - remascFees, - totalPaidFees, - totalGasUsed, start ); completionService.submit(txListExecutor); + transactionListExecutors.add(txListExecutor); nTasks++; start = end; } @@ -505,12 +491,35 @@ private BlockResult executeParallel( } } - readWrittenKeysTracker.clear(); + // Review collision + if (aTracker.detectCollision()) { + logger.warn("block: [{}] execution failed", block.getNumber()); + profiler.stop(metric); + return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; + } + + // Merge maps. + Map executedTransactions = new HashMap<>(); + Set deletedAccounts = new HashSet<>(); + Map receipts = new HashMap<>(); + Map mergedTransactionResults = new HashMap<>(); + Coin totalPaidFees = Coin.ZERO; + long totalGasUsed = 0; + + for (TransactionListExecutor tle : transactionListExecutors) { + tle.getRepository().commit(); + deletedAccounts.addAll(tle.getDeletedAccounts()); + executedTransactions.putAll(tle.getExecutedTransactions()); + receipts.putAll(tle.getReceipts()); + mergedTransactionResults.putAll(tle.getTransactionResults()); + totalPaidFees = totalPaidFees.add(tle.getTotalFees()); + totalGasUsed += tle.getTotalGas(); + } + // execute remaining transactions after the parallel subsets List sublist = block.getTransactionsList().subList(start, block.getTransactionsList().size()); TransactionListExecutor txListExecutor = new TransactionListExecutor( sublist, - readWrittenKeysTracker, block, transactionExecutorFactory, track, @@ -521,12 +530,9 @@ private BlockResult executeParallel( acceptInvalidTransactions, receipts, executedTransactions, - transactionResults, + mergedTransactionResults, registerProgramResults, programTraceProcessor, - remascFees, - totalPaidFees, - totalGasUsed, start ); Boolean success = txListExecutor.call(); @@ -534,16 +540,18 @@ private BlockResult executeParallel( return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } - addFeesToRemasc(remascFees, track); + totalPaidFees = totalPaidFees.add(txListExecutor.getTotalFees()); + totalGasUsed += txListExecutor.getTotalGas(); + addFeesToRemasc(totalPaidFees, track); saveOrCommitTrackState(saveState, track); BlockResult result = new BlockResult( block, new LinkedList<>(executedTransactions.values()), new LinkedList<>(receipts.values()), - new short[0], - totalGasUsed.longValue(), - Coin.valueOf(totalPaidFees.longValue()), + block.getHeader().getTxExecutionSublistsEdges(), + totalGasUsed, + totalPaidFees, vmTrace ? null : track.getTrie() ); profiler.stop(metric); @@ -551,10 +559,10 @@ private BlockResult executeParallel( return result; } - private void addFeesToRemasc(LongAccumulator remascFees, Repository track) { - long fee = remascFees.get(); - if (fee > 0) { - track.addBalance(PrecompiledContracts.REMASC_ADDR, Coin.valueOf(fee)); + private void addFeesToRemasc(Coin remascFees, Repository track) { + if (remascFees.compareTo(Coin.ZERO) > 0) { + logger.trace("Adding fee to remasc contract account"); + track.addBalance(PrecompiledContracts.REMASC_ADDR, remascFees); } } @@ -584,13 +592,12 @@ private BlockResult executeForMiningAfterRSKIP144( IReadWrittenKeysTracker readWrittenKeysTracker = new ReadWrittenKeysTracker(); Repository track = repositoryLocator.startTrackingAt(parent, readWrittenKeysTracker); maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); - + readWrittenKeysTracker.clear(); int i = 1; long gasUsedInBlock = 0; Coin totalPaidFees = Coin.ZERO; Map receiptsByTx = new HashMap<>(); Set deletedAccounts = new HashSet<>(); - LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) Constants.getTransactionExecutionThreads(), GasCost.toGas(block.getGasLimit())); @@ -608,8 +615,8 @@ private BlockResult executeForMiningAfterRSKIP144( parallelizeTransactionHandler.getGasUsedInSequential(), false, 0, - deletedAccounts, - remascFees); + deletedAccounts + ); boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { @@ -626,7 +633,7 @@ private BlockResult executeForMiningAfterRSKIP144( if (tx.isRemascTransaction(txindex, transactionsList.size())) { sublistGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, txExecutor.getGasUsed()); } else { - sublistGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getTemporalReadKeys(), readWrittenKeysTracker.getTemporalWrittenKeys(), txExecutor.getGasUsed()); + sublistGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getThisThreadReadKeys(), readWrittenKeysTracker.getThisThreadWrittenKeys(), txExecutor.getGasUsed()); } if (!acceptInvalidTransactions && !sublistGasAccumulated.isPresent()) { @@ -670,7 +677,7 @@ private BlockResult executeForMiningAfterRSKIP144( loggingTxDone(); } - addFeesToRemasc(remascFees, track); + addFeesToRemasc(totalPaidFees, track); saveOrCommitTrackState(saveState, track); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java b/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java index c0aa9d11cc0..fdc29fef320 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/IReadWrittenKeysTracker.java @@ -19,18 +19,24 @@ package co.rsk.core.bc; import org.ethereum.db.ByteArrayWrapper; + +import java.util.Map; import java.util.Set; public interface IReadWrittenKeysTracker { - Set getTemporalReadKeys(); + Set getThisThreadReadKeys(); + + Set getThisThreadWrittenKeys(); - Set getTemporalWrittenKeys(); + Map> getReadKeysByThread(); - boolean hasCollided(); + Map> getWrittenKeysByThread(); void addNewReadKey(ByteArrayWrapper key); void addNewWrittenKey(ByteArrayWrapper key); + boolean detectCollision(); + void clear(); } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index dce2549903c..0d3861cf058 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -26,15 +26,15 @@ import java.util.*; public class ParallelizeTransactionHandler { - private final HashMap sublistByWrittenKey; - private final HashMap> sublistsByReadKey; - private final Map sublistBySender; + private final HashMap sublistsHavingWrittenToKey; + private final HashMap> sublistsHavingReadFromKey; + private final Map sublistOfSender; private final ArrayList sublists; public ParallelizeTransactionHandler(short numberOfSublists, long sublistGasLimit) { - this.sublistBySender = new HashMap<>(); - this.sublistByWrittenKey = new HashMap<>(); - this.sublistsByReadKey = new HashMap<>(); + this.sublistOfSender = new HashMap<>(); + this.sublistsHavingWrittenToKey = new HashMap<>(); + this.sublistsHavingReadFromKey = new HashMap<>(); this.sublists = new ArrayList<>(); for (short i = 0; i < numberOfSublists; i++){ this.sublists.add(new TransactionSublist(sublistGasLimit, false)); @@ -116,25 +116,25 @@ public long getGasUsedInSequential() { private void addNewKeysToMaps(RskAddress sender, TransactionSublist sublist, Set newReadKeys, Set newWrittenKeys) { for (ByteArrayWrapper key : newReadKeys) { - Set sublistsAlreadyRead = sublistsByReadKey.getOrDefault(key, new HashSet<>()); + Set sublistsAlreadyRead = sublistsHavingReadFromKey.getOrDefault(key, new HashSet<>()); sublistsAlreadyRead.add(sublist); - sublistsByReadKey.put(key, sublistsAlreadyRead); + sublistsHavingReadFromKey.put(key, sublistsAlreadyRead); } if (sublist.isSequential()) { - sublistBySender.put(sender, sublist); + sublistOfSender.put(sender, sublist); return; } else { - sublistBySender.putIfAbsent(sender, sublist); + sublistOfSender.putIfAbsent(sender, sublist); } for (ByteArrayWrapper key: newWrittenKeys) { - sublistByWrittenKey.putIfAbsent(key, sublist); + sublistsHavingWrittenToKey.putIfAbsent(key, sublist); } } private Optional getSublistBySender(Transaction tx) { - return Optional.ofNullable(sublistBySender.get(tx.getSender())); + return Optional.ofNullable(sublistOfSender.get(tx.getSender())); } private Optional getAvailableSublistWithLessUsedGas(long txGasLimit) { @@ -161,8 +161,8 @@ private TransactionSublist getSublistCandidates(Transaction tx, Set returnsSequentialIfBothAreDifferent(sc, sublist)).orElse(sublist)); } } @@ -173,8 +173,8 @@ private TransactionSublist getSublistCandidates(Transaction tx, Set returnsSequentialIfBothAreDifferent(sc, sublist)).orElse(sublist)); } @@ -182,8 +182,8 @@ private TransactionSublist getSublistCandidates(Transaction tx, Set sublist = sublistsByReadKey.get(newWrittenKey); + if (sublistsHavingReadFromKey.containsKey(newWrittenKey)) { + Set sublist = sublistsHavingReadFromKey.get(newWrittenKey); if (sublist.size() > 1) { return getSequentialSublist(); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java b/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java index 5aea1f0d32a..968465ba20d 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ReadWrittenKeysTracker.java @@ -20,68 +20,116 @@ import org.ethereum.db.ByteArrayWrapper; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class ReadWrittenKeysTracker implements IReadWrittenKeysTracker { - private Map> threadByReadKey; - private Map threadByWrittenKey; - private boolean collision; + + private Map> readKeysByThread; + + private Map> writtenKeysByThread; + public ReadWrittenKeysTracker() { - this.threadByReadKey = new HashMap<>(); - this.threadByWrittenKey = new HashMap<>(); - this.collision = false; + this.readKeysByThread = new HashMap<>(); + this.writtenKeysByThread = new HashMap<>(); + } + + @Override + public Set getThisThreadReadKeys(){ + long threadId = Thread.currentThread().getId(); + if (this.readKeysByThread.containsKey(threadId)) { + return new HashSet<>(this.readKeysByThread.get(threadId)); + } else { + return new HashSet<>(); + } } @Override - public Set getTemporalReadKeys(){ - return new HashSet<>(this.threadByReadKey.keySet()); + public Set getThisThreadWrittenKeys(){ + long threadId = Thread.currentThread().getId(); + if (this.writtenKeysByThread.containsKey(threadId)) { + return new HashSet<>(this.writtenKeysByThread.get(threadId)); + } else { + return new HashSet<>(); + } } @Override - public Set getTemporalWrittenKeys(){ - return new HashSet<>(this.threadByWrittenKey.keySet()); + public Map> getReadKeysByThread() { + return new HashMap<>(this.readKeysByThread); } - public boolean hasCollided() { return this.collision;} + @Override + public Map> getWrittenKeysByThread() { + return new HashMap<>(this.writtenKeysByThread); + } @Override public synchronized void addNewReadKey(ByteArrayWrapper key) { long threadId = Thread.currentThread().getId(); - if (threadByWrittenKey.containsKey(key)) { - collision = collision || (threadId != threadByWrittenKey.get(key)); - } - Set threadSet; - if (threadByReadKey.containsKey(key)) { - threadSet = threadByReadKey.get(key); - } else { - threadSet = new HashSet<>(); - } - threadSet.add(threadId); - threadByReadKey.put(key, threadSet); + addNewReadKeyToThread(threadId,key); + } + + public synchronized void addNewReadKeyToThread(long threadId,ByteArrayWrapper key) { + Set readKeys = readKeysByThread.containsKey(threadId)? readKeysByThread.get(threadId) : new HashSet<>(); + readKeys.add(key); + readKeysByThread.put(threadId, readKeys); + } + public synchronized void removeReadKeyToThread(long threadId,ByteArrayWrapper key) { + Set readKeys = readKeysByThread.containsKey(threadId)? readKeysByThread.get(threadId) : new HashSet<>(); + readKeys.remove(key); + readKeysByThread.put(threadId, readKeys); } @Override public synchronized void addNewWrittenKey(ByteArrayWrapper key) { long threadId = Thread.currentThread().getId(); - if (threadByWrittenKey.containsKey(key)) { - collision = collision || (threadId != threadByWrittenKey.get(key)); - } + addNewWrittenKeyToThread(threadId,key); - if (threadByReadKey.containsKey(key)) { - Set threadSet = threadByReadKey.get(key); - collision = collision || !(threadSet.contains(threadId)) || (threadSet.size() > 1); - } + } + public synchronized void removeWrittenKeyToThread(long threadId,ByteArrayWrapper key) { + Set writtenKeys = writtenKeysByThread.containsKey(threadId)? writtenKeysByThread.get(threadId) : new HashSet<>(); + writtenKeys.remove(key); + writtenKeysByThread.put(threadId, writtenKeys); + } - threadByWrittenKey.put(key, threadId); + public synchronized void addNewWrittenKeyToThread(long threadId,ByteArrayWrapper key) { + Set writtenKeys = writtenKeysByThread.containsKey(threadId)? writtenKeysByThread.get(threadId) : new HashSet<>(); + writtenKeys.add(key); + writtenKeysByThread.put(threadId, writtenKeys); } @Override public synchronized void clear() { - this.threadByReadKey = new HashMap<>(); - this.threadByWrittenKey = new HashMap<>(); + this.readKeysByThread = new HashMap<>(); + this.writtenKeysByThread = new HashMap<>(); + } + + public boolean detectCollision() { + Set threads = new HashSet<>(); + threads.addAll(readKeysByThread.keySet()); + threads.addAll(writtenKeysByThread.keySet()); + + for (Long threadId : threads) { + Set baseReadKeys = readKeysByThread.getOrDefault(threadId, new HashSet<>()); + Set baseWrittenKeys = writtenKeysByThread.getOrDefault(threadId, new HashSet<>()); + + for (Long threadId2 : threads) { + if (threadId >= threadId2) { + continue; + } + + Set temporalReadKeys = readKeysByThread.getOrDefault(threadId2, new HashSet<>()); + Set temporalWrittenKeys = writtenKeysByThread.getOrDefault(threadId2, new HashSet<>()); + + boolean isDisjoint = Collections.disjoint(baseWrittenKeys, temporalWrittenKeys) && Collections.disjoint(baseWrittenKeys, temporalReadKeys) + && Collections.disjoint(baseReadKeys, temporalWrittenKeys); + + if (!isDisjoint) { + return true; + } + } + } + return false; } } diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index 9746fa8c86d..dc321626c34 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -47,7 +47,6 @@ import java.math.BigInteger; import java.util.*; -import java.util.concurrent.atomic.LongAccumulator; import static co.rsk.util.ListArrayUtil.getLength; import static co.rsk.util.ListArrayUtil.isEmpty; @@ -77,7 +76,6 @@ public class TransactionExecutor { private final VmConfig vmConfig; private final PrecompiledContracts precompiledContracts; private final boolean enableRemasc; - private LongAccumulator remascFee; private String executionError = ""; private final long gasUsedInTheBlock; private Coin paidFees; @@ -108,7 +106,7 @@ public TransactionExecutor( Repository track, BlockStore blockStore, ReceiptStore receiptStore, BlockFactory blockFactory, ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsedInTheBlock, VmConfig vmConfig, boolean remascEnabled, PrecompiledContracts precompiledContracts, Set deletedAccounts, - SignatureCache signatureCache, LongAccumulator remascFee) { + SignatureCache signatureCache) { this.constants = constants; this.signatureCache = signatureCache; this.activations = activationConfig.forBlock(executionBlock.getNumber()); @@ -127,7 +125,6 @@ public TransactionExecutor( this.precompiledContracts = precompiledContracts; this.enableRemasc = remascEnabled; this.deletedAccounts = new HashSet<>(deletedAccounts); - this.remascFee = remascFee; } /** @@ -530,10 +527,7 @@ private void finalization() { Coin summaryFee = summary.getFee(); //TODO: REMOVE THIS WHEN THE LocalBLockTests starts working with REMASC - if (enableRemasc) { - logger.trace("Adding fee to remasc contract account"); - remascFee.accumulate(summaryFee.asBigInteger().longValue()); - } else { + if (!enableRemasc) { track.addBalance(coinbase, summaryFee); } diff --git a/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java b/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java index 8716f1549d5..d26f0e9494b 100644 --- a/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java +++ b/rskj-core/src/main/java/org/ethereum/db/DummyReadWrittenKeysTracker.java @@ -9,27 +9,32 @@ public class DummyReadWrittenKeysTracker implements IReadWrittenKeysTracker { - private final Map> temporalReadKeys; - private final Map temporalWrittenKeys; + private final Map> readKeysByThread; + private final Map> writtenKeysByThread; public DummyReadWrittenKeysTracker() { - this.temporalReadKeys = new HashMap<>(); - this.temporalWrittenKeys = new HashMap<>(); + this.readKeysByThread = new HashMap<>(); + this.writtenKeysByThread = new HashMap<>(); } @Override - public Set getTemporalReadKeys() { - return new HashSet<>(this.temporalReadKeys.keySet()); + public Set getThisThreadReadKeys() { + return new HashSet<>(); } @Override - public Set getTemporalWrittenKeys() { - return new HashSet<>(this.temporalWrittenKeys.keySet()); + public Set getThisThreadWrittenKeys() { + return new HashSet<>(); } @Override - public boolean hasCollided() { - return false; + public Map> getReadKeysByThread() { + return readKeysByThread; + } + + @Override + public Map> getWrittenKeysByThread() { + return writtenKeysByThread; } @Override @@ -42,6 +47,11 @@ public void addNewWrittenKey(ByteArrayWrapper key) { //Dummy tracker does not store added keys } + @Override + public boolean detectCollision(){ + return false; + } + @Override public void clear() { //Dummy tracker does not store added keys diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index bfc57a692e9..a97e449a4f7 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -690,7 +690,7 @@ void executeParallelBlockAgainstSequentialBlock(boolean activeRskip144) { @ParameterizedTest @ValueSource(booleans = {true, false}) - void executeInvalidParallelBlock(boolean activeRskip144) { + void executeInvalidParallelBlockDueToCollision(boolean activeRskip144) { if (!activeRskip144) { return; } diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java index e438e0e8630..297c648f669 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java @@ -25,9 +25,7 @@ import org.junit.jupiter.api.Test; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.*; public class ReadWrittenKeysTrackerTest { @@ -46,76 +44,217 @@ void setup() { this.key2 = new ByteArrayWrapper(new byte[]{2}); } + ByteArrayWrapper getKey(int thread, int readWrite,int i ) { + // Supports upto 65536 keys + return new ByteArrayWrapper(new byte[]{(byte)thread, (byte) readWrite, + (byte) (i >> 8), (byte) (i & 0xff)}); + } + @Test - void createATrackerShouldHaveEmptyMaps() { - Assertions.assertEquals(0, tracker.getTemporalReadKeys().size()); - Assertions.assertEquals(0, tracker.getTemporalWrittenKeys().size()); + void collisionWithLongerSets() { + //.. + ReadWrittenKeysTracker myTracker = (ReadWrittenKeysTracker) this.tracker; + int keysPerThread = 10; + int maxThreads = 4; + + // Add read 10 distinct keys for each one of 4 threads + for (int thread=0; thread temporalReadKeys = tracker.getTemporalReadKeys(); + Set temporalReadKeys = tracker.getThisThreadReadKeys(); assertKeyWasAddedInMap(temporalReadKeys, key1); } @Test - void addReadKeyToTheTrackerAndShouldntBeInWrittenMap() { + void addReadKeyToTheTrackerAndShouldBeInReadKeysForAllThreads() { tracker.addNewReadKey(key1); - Assertions.assertEquals(0, tracker.getTemporalWrittenKeys().size()); + Map> readKeys = tracker.getReadKeysByThread(); + Set readKeysByThisThread = readKeys.get(Thread.currentThread().getId()); + + Assertions.assertEquals(1, readKeys.size()); + Assertions.assertEquals(1, readKeysByThisThread.size()); + Assertions.assertTrue(readKeysByThisThread.contains(key1)); } @Test - void addWrittenKeyToTheTrackerAndShouldBeInWrittenMap() { + void addReadKeyToTheTrackerAndShouldNotBeInWrittenMapForThisThread() { + tracker.addNewReadKey(key1); + Assertions.assertEquals(0, tracker.getThisThreadWrittenKeys().size()); + } + + @Test + void addReadKeyToTheTrackerAndShouldNotBeInWrittenMapForAllThreads() { + tracker.addNewReadKey(key1); + Assertions.assertEquals(0, tracker.getWrittenKeysByThread().size()); + } + + @Test + void addWrittenKeyToTheTrackerAndShouldBeInWrittenMapForThisThread() { tracker.addNewWrittenKey(key1); - Set temporalWrittenKeys = tracker.getTemporalWrittenKeys(); + Set temporalWrittenKeys = tracker.getThisThreadWrittenKeys(); assertKeyWasAddedInMap(temporalWrittenKeys, key1); } @Test - void addWrittenKeyToTheTrackerAndShouldntBeInReadMap() { + void addWrittenKeyToTheTrackerAndShouldBeInWrittenMapForAllThreads() { tracker.addNewWrittenKey(key1); - Assertions.assertEquals(0, tracker.getTemporalReadKeys().size()); + Map> writtenKeys = tracker.getWrittenKeysByThread(); + + Set writtenKeysByThisThread = writtenKeys.get(Thread.currentThread().getId()); + Assertions.assertEquals(1, writtenKeys.size()); + Assertions.assertEquals(1, writtenKeysByThisThread.size()); + Assertions.assertTrue(writtenKeysByThisThread.contains(key1)); } @Test - void clearTrackerShouldEmptyTheMaps() { + void addWrittenKeyToTheTrackerAndShouldNotBeInReadMapForThisThread() { + tracker.addNewWrittenKey(key1); + Assertions.assertEquals(0, tracker.getThisThreadReadKeys().size()); + } + + @Test + void addWrittenKeyToTheTrackerAndShouldNotBeInReadMapForAllThreads() { + tracker.addNewWrittenKey(key1); + Assertions.assertEquals(0, tracker.getReadKeysByThread().size()); + } + + @Test + void clearTrackerShouldEmptyAllTheMaps() { tracker.addNewWrittenKey(key1); - tracker.addNewReadKey(key1); tracker.addNewWrittenKey(key2); + tracker.addNewReadKey(key1); tracker.addNewReadKey(key2); - Assertions.assertEquals(2, tracker.getTemporalReadKeys().size()); - Assertions.assertEquals(2, tracker.getTemporalWrittenKeys().size()); + Assertions.assertEquals(1, tracker.getWrittenKeysByThread().size()); + Assertions.assertEquals(2, tracker.getThisThreadWrittenKeys().size()); + Assertions.assertEquals(1, tracker.getReadKeysByThread().size()); + Assertions.assertEquals(2, tracker.getThisThreadReadKeys().size()); + tracker.clear(); - Assertions.assertEquals(0, tracker.getTemporalReadKeys().size()); - Assertions.assertEquals(0, tracker.getTemporalWrittenKeys().size()); + Assertions.assertEquals(0, tracker.getWrittenKeysByThread().size()); + Assertions.assertEquals(0, tracker.getThisThreadWrittenKeys().size()); + Assertions.assertEquals(0, tracker.getReadKeysByThread().size()); + Assertions.assertEquals(0, tracker.getThisThreadReadKeys().size()); } @Test void createADummyTrackerShouldHaveEmptyMaps() { - Assertions.assertEquals(0, dummyTracker.getTemporalReadKeys().size()); - Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + Assertions.assertEquals(0, dummyTracker.getReadKeysByThread().size()); + Assertions.assertEquals(0, dummyTracker.getWrittenKeysByThread().size()); + Assertions.assertEquals(0, dummyTracker.getThisThreadReadKeys().size()); + Assertions.assertEquals(0, dummyTracker.getThisThreadWrittenKeys().size()); } @Test void addReadKeyToTheDummyTrackerShouldDoNothing() { dummyTracker.addNewReadKey(key1); - Assertions.assertEquals(0, dummyTracker.getTemporalReadKeys().size()); - } - - @Test - void addReadKeyToTheTrackerShouldDoNothing() { - dummyTracker.addNewReadKey(key1); - Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + Assertions.assertEquals(0, dummyTracker.getReadKeysByThread().size()); + Assertions.assertEquals(0, dummyTracker.getThisThreadReadKeys().size()); } @Test - void addWrittenKeyToTheDummyTrackerShouldDoNothing() { + void addWrittenKeyToTheTrackerShouldDoNothing() { dummyTracker.addNewWrittenKey(key1); - Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + Assertions.assertEquals(0, dummyTracker.getThisThreadWrittenKeys().size()); + Assertions.assertEquals(0, dummyTracker.getWrittenKeysByThread().size()); } @Test @@ -125,149 +264,287 @@ void clearDummyTrackerShouldDoNothing() { dummyTracker.addNewWrittenKey(key2); dummyTracker.addNewReadKey(key2); - Assertions.assertEquals(0, dummyTracker.getTemporalReadKeys().size()); - Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + Assertions.assertEquals(0, dummyTracker.getThisThreadReadKeys().size()); + Assertions.assertEquals(0, dummyTracker.getThisThreadWrittenKeys().size()); dummyTracker.clear(); - Assertions.assertEquals(0, dummyTracker.getTemporalReadKeys().size()); - Assertions.assertEquals(0, dummyTracker.getTemporalWrittenKeys().size()); + Assertions.assertEquals(0, dummyTracker.getThisThreadReadKeys().size()); + Assertions.assertEquals(0, dummyTracker.getThisThreadWrittenKeys().size()); + } + + @Test + void ifAThreadReadsAndWritesTheSameKeyCollideShouldBeFalse() { + int nThreads = 1; + + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); + + for (int i = 0; i < nThreads; i++) { + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.singleton(key1), Collections.singleton(key1)); + completionService.submit(rwKeys); + } + + getTrackerHelperAfterCompletion(nThreads, completionService); + Assertions.assertFalse(tracker.detectCollision()); + } + + @Test + void ifAThreadWritesTwiceTheSameKeyCollideShouldBeFalse() { + ReadWrittenKeysTracker myTracker = (ReadWrittenKeysTracker) this.tracker; + myTracker.addNewWrittenKeyToThread(0, key1); + myTracker.addNewWrittenKeyToThread(0, key1); + Assertions.assertFalse(myTracker.detectCollision()); + } + + @Test + void ifAThreadReadsTwiceTheSameKeyCollideShouldBeFalse() { + ReadWrittenKeysTracker myTracker = (ReadWrittenKeysTracker) this.tracker; + myTracker.addNewReadKeyToThread(0, key1); + myTracker.addNewReadKeyToThread(0, key1); + Assertions.assertFalse(myTracker.detectCollision()); } @Test - public void ifTwoThreadsWriteTheSameKeyCollideShouldBeTrue() { + void ifTwoThreadsDontWriteAnyKeyCollideShouldBeFalse() { int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); - CompletionService completionService = new ExecutorCompletionService<>(service); + CompletionService completionService = new ExecutorCompletionService<>(service); for (int i = 0; i < nThreads; i++) { - ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.singletonList(key1), Collections.emptyList()); + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.emptySet(), Collections.emptySet()); completionService.submit(rwKeys); } - assertThereWasACollision(nThreads, service, completionService); + getTrackerHelperAfterCompletion(nThreads, completionService); + Assertions.assertFalse(tracker.detectCollision()); } @Test - public void ifTwoThreadsReadAndWriteTheSameKeyShouldCollide() { + void ifTwoThreadsReadDifferentKeysCollideShouldBeFalse() { int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); - CompletionService completionService = new ExecutorCompletionService<>(service); - List writtenKeys; - List readKeys; + CompletionService completionService = new ExecutorCompletionService<>(service); + for (int i = 0; i < nThreads; i++) { - if (i == 0) { - writtenKeys = Collections.singletonList(key1); - readKeys = Collections.emptyList(); - } else { - writtenKeys = Collections.emptyList(); - readKeys = Collections.singletonList(key1); - } + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.emptySet(), Collections.singleton(i % 2 ==0 ? key1 : key2)); + completionService.submit(rwKeys); + } - ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, writtenKeys, readKeys); + getTrackerHelperAfterCompletion(nThreads, completionService); + Assertions.assertFalse(tracker.detectCollision()); + } + + @Test + void ifTwoThreadsReadTheSameKeyCollideShouldBeFalse() { + int nThreads = 2; + + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); + + for (int i = 0; i < nThreads; i++) { + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.emptySet(), Collections.singleton(key1)); completionService.submit(rwKeys); } - assertThereWasACollision(nThreads, service, completionService); + getTrackerHelperAfterCompletion(nThreads, completionService); + Assertions.assertFalse(tracker.detectCollision()); } @Test - public void ifTwoThreadsWriteDifferentKeyCollideShouldBeFalse() { + void ifTwoThreadsWriteDifferentKeysCollideShouldBeFalse() { int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); - CompletionService completionService = new ExecutorCompletionService<>(service); + CompletionService completionService = new ExecutorCompletionService<>(service); for (int i = 0; i < nThreads; i++) { - ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.singletonList(i == 0? key1 : key2), Collections.emptyList()); + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.singleton(i % 2 ==0 ? key1 : key2), Collections.emptySet()); completionService.submit(rwKeys); } - assertThereWasNotACollision(nThreads, service, completionService); + + getTrackerHelperAfterCompletion(nThreads, completionService); + Assertions.assertFalse(tracker.detectCollision()); } @Test - public void allThreadIdsShouldBeStoredInTheReadKeysMap() { + void ifTwoThreadsWriteTheSameKeyCollideShouldBeTrue() { int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); - CompletionService completionService = new ExecutorCompletionService<>(service); - boolean hasCollided = false; + CompletionService completionService = new ExecutorCompletionService<>(service); - ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.emptyList(), Collections.singletonList(key1)); - completionService.submit(rwKeys); + for (int i = 0; i < nThreads; i++) { + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.singleton(key1), Collections.emptySet()); + completionService.submit(rwKeys); + } - try { - Future hasCollidedFuture = completionService.take(); - hasCollided = hasCollidedFuture.get(); - } catch (Exception e) { - Assertions.fail(); + getTrackerHelperAfterCompletion(nThreads, completionService); + Assertions.assertTrue(tracker.detectCollision()); + } + + @Test + void ifTwoThreadsReadAndWriteTheSameKeyCollideShouldBeTrue() { + int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); + Set writtenKeys; + Set readKeys; + for (int i = 0; i < nThreads; i++) { + boolean isEven = i % 2 == 0; + writtenKeys = isEven? Collections.singleton(this.key1) : Collections.emptySet(); + readKeys = isEven? Collections.emptySet() : Collections.singleton(this.key1); + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, writtenKeys, readKeys); + completionService.submit(rwKeys); } - Assertions.assertFalse(hasCollided); - ReadWrittenKeysHelper rwKeys2 = new ReadWrittenKeysHelper(this.tracker, Collections.singletonList(key1), Collections.singletonList(key1)); - completionService.submit(rwKeys2); + getTrackerHelperAfterCompletion(nThreads, completionService); + Assertions.assertTrue(tracker.detectCollision()); + } + + @Test + void ifTwoThreadsWriteTheSameKeyShouldBeStored() { + int nThreads = 2; + + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); - try { - Future hasCollidedFuture = completionService.take(); - hasCollided = hasCollidedFuture.get(); - } catch (Exception e) { - Assertions.fail(); + for (int i = 0; i < nThreads; i++) { + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, Collections.singleton(key1), Collections.emptySet()); + completionService.submit(rwKeys); } - service.shutdown(); - Assertions.assertTrue(hasCollided); + List helpers = getTrackerHelperAfterCompletion(nThreads, completionService); + + Map> writtenKeysByThread = this.tracker.getWrittenKeysByThread(); + Assertions.assertEquals(nThreads, writtenKeysByThread.size()); + Map> readKeysByThread = this.tracker.getReadKeysByThread(); + Assertions.assertEquals(0, readKeysByThread.size()); + assertKeysAreAddedCorrectlyIntoTheTracker(helpers, writtenKeysByThread, readKeysByThread); } - private void assertThereWasNotACollision(int nThreads, ExecutorService service, CompletionService completionService) { - boolean hasCollided = hasCollided(nThreads, completionService); - Assertions.assertFalse(hasCollided); - service.shutdown(); + @Test + void ifTwoThreadsReadAndWriteAKeyTheyShouldBeStored() { + int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); + Set writtenKeys; + Set readKeys; + for (int i = 0; i < nThreads; i++) { + boolean isEven = i % 2 == 0; + writtenKeys = isEven? Collections.singleton(this.key1) : Collections.emptySet(); + readKeys = isEven? Collections.emptySet() : Collections.singleton(this.key1); + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, writtenKeys, readKeys); + completionService.submit(rwKeys); + } + + List helpers = getTrackerHelperAfterCompletion(nThreads, completionService); + + Map> writtenKeysByThread = this.tracker.getWrittenKeysByThread(); + Assertions.assertEquals(1, writtenKeysByThread.size()); + Map> readKeysByThread = this.tracker.getReadKeysByThread(); + Assertions.assertEquals(1, readKeysByThread.size()); + assertKeysAreAddedCorrectlyIntoTheTracker(helpers, writtenKeysByThread, readKeysByThread); } - private void assertThereWasACollision(int nThreads, ExecutorService service, CompletionService completionService) { - boolean hasCollided = hasCollided(nThreads, completionService); - System.out.println(hasCollided); - Assertions.assertTrue(hasCollided); - service.shutdown(); + @Test + void ifTwoThreadsReadSomeKeysTheyShouldBeStored() { + int nThreads = 2; + ExecutorService service = Executors.newFixedThreadPool(nThreads); + CompletionService completionService = new ExecutorCompletionService<>(service); + Set writtenKeys; + Set readKeys; + for (int i = 0; i < nThreads; i++) { + writtenKeys = Collections.emptySet(); + readKeys = Collections.singleton(this.key1); + ReadWrittenKeysHelper rwKeys = new ReadWrittenKeysHelper(this.tracker, writtenKeys, readKeys); + completionService.submit(rwKeys); + } + + List helpers = getTrackerHelperAfterCompletion(nThreads, completionService); + Map> writtenKeysByThread = this.tracker.getWrittenKeysByThread(); + Assertions.assertEquals(0, writtenKeysByThread.size()); + Map> readKeysByThread = this.tracker.getReadKeysByThread(); + Assertions.assertEquals(2, readKeysByThread.size()); + assertKeysAreAddedCorrectlyIntoTheTracker(helpers, writtenKeysByThread, readKeysByThread); } - private boolean hasCollided(int nThreads, CompletionService completionService) { - boolean hasCollided = false; + private List getTrackerHelperAfterCompletion(int nThreads, CompletionService completionService) { + List helpers = new ArrayList<>(); for (int i = 0; i < nThreads; i++) { try { - Future hasCollidedFuture = completionService.take(); - hasCollided |= hasCollidedFuture.get(); + Future helperFuture = completionService.take(); + helpers.add(helperFuture.get()); } catch (Exception e) { Assertions.fail(); } } - return hasCollided; + + return helpers; + } + + private void assertKeysAreAddedCorrectlyIntoTheTracker(List helpers, Map> writtenKeysByThread, Map> readKeysByThread) { + for (ReadWrittenKeysHelper h: helpers) { + if (h.getWrittenKeys().size() == 0) { + Assertions.assertNull(writtenKeysByThread.get(h.getThreadId())); + } else { + Assertions.assertEquals(h.getWrittenKeys().size(), writtenKeysByThread.get(h.getThreadId()).size()); + Assertions.assertTrue(h.getWrittenKeys().containsAll(writtenKeysByThread.get(h.getThreadId()))); + } + + if (h.getReadKeys().size() == 0) { + Assertions.assertNull(readKeysByThread.get(h.getThreadId())); + } else { + Assertions.assertEquals(h.getReadKeys().size(), readKeysByThread.get(h.getThreadId()).size()); + Assertions.assertTrue(h.getReadKeys().containsAll(readKeysByThread.get(h.getThreadId()))); + } + } } private void assertKeyWasAddedInMap(Set map, ByteArrayWrapper key) { Assertions.assertEquals(1, map.size()); Assertions.assertTrue(map.contains(key)); } - private static class ReadWrittenKeysHelper implements Callable { + private static class ReadWrittenKeysHelper implements Callable { - private final List readKeys; - private final List writtenKeys; + private final Set readKeys; + private final Set writtenKeys; private final IReadWrittenKeysTracker tracker; + private long threadId; - public ReadWrittenKeysHelper(IReadWrittenKeysTracker tracker, List writtenKeys, List readKeys) { + public ReadWrittenKeysHelper(IReadWrittenKeysTracker tracker, Set writtenKeys, Set readKeys) { this.tracker = tracker; this.readKeys = readKeys; this.writtenKeys = writtenKeys; + this.threadId = -1L; } //At first, it reads and then it writes. - public Boolean call() { - for (ByteArrayWrapper rk : readKeys) { - tracker.addNewReadKey(rk); + public ReadWrittenKeysHelper call() { + + this.threadId = Thread.currentThread().getId(); + + for (ByteArrayWrapper rk : this.readKeys) { + this.tracker.addNewReadKey(rk); } - for (ByteArrayWrapper wk : writtenKeys) { - tracker.addNewWrittenKey(wk); + for (ByteArrayWrapper wk : this.writtenKeys) { + this.tracker.addNewWrittenKey(wk); } + return this; + } + + public Set getReadKeys() { + return this.readKeys; + } + + public Set getWrittenKeys() { + return this.writtenKeys; + } - return tracker.hasCollided(); + public long getThreadId() { + return this.threadId; } } diff --git a/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java index 61958fb7070..a087a05eff4 100644 --- a/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java +++ b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java @@ -35,8 +35,8 @@ public void setUp() { } void assertRepositoryHasSize(int readRepoSize, int writtenRepoSize) { - Assertions.assertEquals(readRepoSize, tracker.getTemporalReadKeys().size()); - Assertions.assertEquals(writtenRepoSize, tracker.getTemporalWrittenKeys().size()); + Assertions.assertEquals(readRepoSize, tracker.getThisThreadReadKeys().size()); + Assertions.assertEquals(writtenRepoSize, tracker.getThisThreadWrittenKeys().size()); } @Test diff --git a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java index c3cf7c7a155..b425e2e1103 100644 --- a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java +++ b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java @@ -72,8 +72,6 @@ public void processBlock( Block block, Block parent) { totalGasUsed = 0; totalPaidFees = Coin.ZERO; txReceipts = new ArrayList<>(); - LongAccumulator remascFees = new LongAccumulator(Long::sum, 0); - Repository track = repositoryLocator.startTrackingAt(parent.getHeader()); @@ -106,7 +104,7 @@ public void processBlock( Block block, Block parent) { null, new PrecompiledContracts(config, bridgeSupportFactory), blockTxSignatureCache); TransactionExecutor executor = transactionExecutorFactory - .newInstance(tx, txindex++, block.getCoinbase(), track, block, totalGasUsed, remascFees); + .newInstance(tx, txindex++, block.getCoinbase(), track, block, totalGasUsed); executor.executeTransaction(); @@ -121,10 +119,8 @@ public void processBlock( Block block, Block parent) { * fees are sent to the Remasc address once all the transactions are executed. * Since the only test using this helper processes one transaction, so the loop has no sense. * */ - long fees = remascFees.get(); - if (fees > 0) { - track.addBalance(PrecompiledContracts.REMASC_ADDR, Coin.valueOf(fees)); - track.commit(); + if (config.isRemascEnabled() && totalPaidFees.compareTo(Coin.ZERO) > 0) { + track.addBalance(PrecompiledContracts.REMASC_ADDR, totalPaidFees); } track.commit(); diff --git a/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java b/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java index 96d354b4a9a..e7e9a9c2aaf 100644 --- a/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java @@ -21,7 +21,6 @@ import java.math.BigInteger; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.atomic.LongAccumulator; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; @@ -76,7 +75,7 @@ void testInitHandlesFreeTransactionsOK() { repository, blockStore, receiptStore, blockFactory, programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, true, precompiledContracts, deletedAccounts, - blockTxSignatureCache, new LongAccumulator(Long::sum, 0) + blockTxSignatureCache ); @@ -182,7 +181,7 @@ void InvalidTxsIsInBlockAndShouldntBeInCache(){ repository, blockStore, receiptStore, blockFactory, programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, true, precompiledContracts, deletedAccounts, - blockTxSignatureCache, new LongAccumulator(Long::sum, 0) + blockTxSignatureCache ); assertEquals(0, transaction.transactionCost(constants, activationConfig.forBlock(executionBlock.getNumber()))); @@ -215,7 +214,7 @@ void remascTxIsReceivedAndShouldntBeInCache(){ repository, blockStore, receiptStore, blockFactory, programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, true, precompiledContracts, deletedAccounts, - blockTxSignatureCache, new LongAccumulator(Long::sum, 0) + blockTxSignatureCache ); assertEquals(0, transaction.transactionCost(constants, activationConfig.forBlock(executionBlock.getNumber()))); @@ -291,7 +290,7 @@ private boolean executeValidTransaction(Transaction transaction, BlockTxSignatur repository, blockStore, receiptStore, blockFactory, programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, true, precompiledContracts, deletedAccounts, - blockTxSignatureCache, new LongAccumulator(Long::sum, 0) + blockTxSignatureCache ); return txExecutor.executeTransaction(); diff --git a/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java b/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java index ead7a90f0b6..b6f68e3d3c1 100644 --- a/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java +++ b/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java @@ -61,7 +61,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.atomic.LongAccumulator; import static org.ethereum.util.ByteUtil.byteArrayToLong; @@ -106,7 +105,6 @@ public StateTestRunner setstateTestUSeREMASC(boolean v) { protected ProgramResult executeTransaction(Transaction tx) { Repository track = repository.startTracking(); - LongAccumulator remascFee = new LongAccumulator(Long::sum, 0); TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory( config, @@ -117,7 +115,7 @@ protected ProgramResult executeTransaction(Transaction tx) { precompiledContracts, new BlockTxSignatureCache(new ReceivedTxSignatureCache())); TransactionExecutor executor = transactionExecutorFactory - .newInstance(transaction, 0, new RskAddress(env.getCurrentCoinbase()), track, blockchain.getBestBlock(), 0, remascFee); + .newInstance(transaction, 0, new RskAddress(env.getCurrentCoinbase()), track, blockchain.getBestBlock(), 0); try{ executor.executeTransaction(); @@ -126,8 +124,8 @@ protected ProgramResult executeTransaction(Transaction tx) { System.exit(-1); } - if (remascFee.get() > 0) { - track.addBalance(PrecompiledContracts.REMASC_ADDR, Coin.valueOf(remascFee.get())); + if (config.isRemascEnabled() && executor.getPaidFees().compareTo(Coin.ZERO) > 0) { + track.addBalance(PrecompiledContracts.REMASC_ADDR, executor.getPaidFees()); } track.commit(); From 5a63b2e6c17ee86910b524781a12c3160530c736 Mon Sep 17 00:00:00 2001 From: julianlen Date: Mon, 14 Nov 2022 12:25:41 -0300 Subject: [PATCH 19/89] Research/main parallel tx remasc (#1895) * it checks if remasc is enabled before performing the payment to the remasc address * modified remasc payment so it is paid just before the remasc transaction is processed --- .../src/main/java/co/rsk/RskContext.java | 3 +- .../co/rsk/core/TransactionListExecutor.java | 44 ++++++++++++--- .../java/co/rsk/core/bc/BlockExecutor.java | 55 +++++++++++++++---- .../co/rsk/core/bc/BlockExecutorTest.java | 4 +- .../co/rsk/mine/TransactionModuleTest.java | 5 +- .../java/co/rsk/net/SyncProcessorTest.java | 4 +- .../remasc/RemascProcessMinerFeesTest.java | 4 +- .../rsk/remasc/RemascStorageProviderTest.java | 5 +- .../java/co/rsk/remasc/RemascTestRunner.java | 4 +- .../src/test/java/co/rsk/test/World.java | 4 +- .../co/rsk/test/builders/BlockBuilder.java | 4 +- .../rsk/test/builders/BlockChainBuilder.java | 4 +- .../co/rsk/test/dsl/WorldDslProcessor.java | 4 +- .../src/test/java/co/rsk/vm/MinerHelper.java | 22 +++++--- .../org/ethereum/core/ImportLightTest.java | 4 +- .../ethereum/jsontestsuite/TestRunner.java | 4 +- .../runners/StateTestRunner.java | 4 +- 17 files changed, 124 insertions(+), 54 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/RskContext.java b/rskj-core/src/main/java/co/rsk/RskContext.java index 1aadb064b06..8e5a7a0f04f 100644 --- a/rskj-core/src/main/java/co/rsk/RskContext.java +++ b/rskj-core/src/main/java/co/rsk/RskContext.java @@ -474,7 +474,8 @@ public synchronized BlockExecutor getBlockExecutor() { blockExecutor = new BlockExecutor( getRskSystemProperties().getActivationConfig(), getRepositoryLocator(), - getTransactionExecutorFactory() + getTransactionExecutorFactory(), + getRskSystemProperties().isRemascEnabled() ); } diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 802d22b7295..1cbcba0944c 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -3,6 +3,7 @@ import co.rsk.crypto.Keccak256; import org.ethereum.core.*; import org.ethereum.vm.DataWord; +import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.program.ProgramResult; import org.ethereum.vm.trace.ProgramTraceProcessor; import org.slf4j.Logger; @@ -29,11 +30,11 @@ public class TransactionListExecutor implements Callable { private final Map receipts; private final Map transactionResults; private final ProgramTraceProcessor programTraceProcessor; - private Coin totalFees; + private final boolean remascEnabled; private long totalGas; - private int i; private final boolean registerProgramResults; + private Coin totalPaidFees; public TransactionListExecutor( List transactions, @@ -50,7 +51,9 @@ public TransactionListExecutor( Map transactionResults, boolean registerProgramResults, @Nullable ProgramTraceProcessor programTraceProcessor, - int firstTxIndex) { + int firstTxIndex, + Coin totalPaidFees, + boolean remascEnabled) { this.block = block; this.transactionExecutorFactory = transactionExecutorFactory; this.track = track; @@ -65,17 +68,25 @@ public TransactionListExecutor( this.registerProgramResults = registerProgramResults; this.transactionResults = transactionResults; this.programTraceProcessor = programTraceProcessor; - this.totalFees = Coin.ZERO; this.totalGas = 0L; this.i = firstTxIndex; + this.totalPaidFees = totalPaidFees; + this.remascEnabled = remascEnabled; } @Override public Boolean call() { long totalGasUsed = 0; - Coin totalPaidFees = Coin.ZERO; for (Transaction tx : transactions) { + + int numberOfTransactions = block.getTransactionsList().size(); + boolean isRemascTransaction = tx.isRemascTransaction(this.i, numberOfTransactions); + + if (this.remascEnabled && isRemascTransaction) { + addFeesToRemasc(); + } + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( tx, i, @@ -90,6 +101,8 @@ public Boolean call() { boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { + // It's used just for testing, the last tx should be always the REMASC. + payToRemascWhenThereIsNoRemascTx(numberOfTransactions, isRemascTransaction); if (!discardInvalidTxs) { logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", block.getNumber(), tx.getHash() @@ -122,6 +135,9 @@ public Boolean call() { totalPaidFees = totalPaidFees.add(txPaidFees); } + // It's used just for testing, the last tx should be always the REMASC. + payToRemascWhenThereIsNoRemascTx(numberOfTransactions, isRemascTransaction); + deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); TransactionReceipt receipt = new TransactionReceipt(); @@ -144,11 +160,23 @@ public Boolean call() { logger.trace("tx[{}] done", i); } totalGas += totalGasUsed; - totalFees = totalFees.add(totalPaidFees); - return true; } + private void payToRemascWhenThereIsNoRemascTx(int numberOfTransactions, boolean isRemascTransaction) { + boolean isLastTx = this.i == numberOfTransactions - 1; + if (this.remascEnabled && isLastTx && !isRemascTransaction) { + addFeesToRemasc(); + } + } + + private void addFeesToRemasc() { + if (this.totalPaidFees.compareTo(Coin.ZERO) > 0) { + logger.trace("Adding fee to remasc contract account"); + track.addBalance(PrecompiledContracts.REMASC_ADDR, this.totalPaidFees); + } + } + public Repository getRepository() { return this.track; } @@ -170,7 +198,7 @@ public Map getTransactionResults() { } public Coin getTotalFees() { - return this.totalFees; + return this.totalPaidFees; } public long getTotalGas() { diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index e79d58e5975..e7011f2d7d6 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -59,6 +59,7 @@ public class BlockExecutor { private final RepositoryLocator repositoryLocator; private final TransactionExecutorFactory transactionExecutorFactory; private final ActivationConfig activationConfig; + private boolean remascEnabled; private final Map transactionResults = new ConcurrentHashMap<>(); private boolean registerProgramResults; @@ -66,10 +67,12 @@ public class BlockExecutor { public BlockExecutor( ActivationConfig activationConfig, RepositoryLocator repositoryLocator, - TransactionExecutorFactory transactionExecutorFactory) { + TransactionExecutorFactory transactionExecutorFactory, + boolean remascEnabled) { this.repositoryLocator = repositoryLocator; this.transactionExecutorFactory = transactionExecutorFactory; this.activationConfig = activationConfig; + this.remascEnabled = remascEnabled; } /** @@ -336,6 +339,11 @@ private BlockResult executePreviousRSKIP144( int txindex = 0; for (Transaction tx : block.getTransactionsList()) { + + if (this.isRemascEnabled() && tx.isRemascTransaction(block.getTransactionsList().size(), txindex)) { + addFeesToRemasc(totalPaidFees, track); + } + loggingApplyBlockToTx(block, i); TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( @@ -390,7 +398,7 @@ private BlockResult executePreviousRSKIP144( loggingTxDone(); } - addFeesToRemasc(totalPaidFees, track); + saveOrCommitTrackState(saveState, track); BlockResult result = new BlockResult( @@ -460,7 +468,9 @@ private BlockResult executeParallel( new HashMap<>(), registerProgramResults, programTraceProcessor, - start + start, + Coin.ZERO, + remascEnabled ); completionService.submit(txListExecutor); transactionListExecutors.add(txListExecutor); @@ -533,16 +543,18 @@ private BlockResult executeParallel( mergedTransactionResults, registerProgramResults, programTraceProcessor, - start + start, + totalPaidFees, + remascEnabled ); Boolean success = txListExecutor.call(); if (!Boolean.TRUE.equals(success)) { return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } - totalPaidFees = totalPaidFees.add(txListExecutor.getTotalFees()); + Coin totalBlockPaidFees = txListExecutor.getTotalFees(); totalGasUsed += txListExecutor.getTotalGas(); - addFeesToRemasc(totalPaidFees, track); + saveOrCommitTrackState(saveState, track); BlockResult result = new BlockResult( @@ -551,7 +563,7 @@ private BlockResult executeParallel( new LinkedList<>(receipts.values()), block.getHeader().getTxExecutionSublistsEdges(), totalGasUsed, - totalPaidFees, + totalBlockPaidFees, vmTrace ? null : track.getTrie() ); profiler.stop(metric); @@ -566,6 +578,10 @@ private void addFeesToRemasc(Coin remascFees, Repository track) { } } + private boolean isRemascEnabled() { + return this.remascEnabled; + } + private BlockResult executeForMiningAfterRSKIP144( Block block, BlockHeader parent, @@ -606,6 +622,13 @@ private BlockResult executeForMiningAfterRSKIP144( for (Transaction tx : transactionsList) { loggingApplyBlockToTx(block, i); + int numberOfTransactions = transactionsList.size(); + boolean isRemascTransaction = tx.isRemascTransaction(txindex, numberOfTransactions); + + if (this.isRemascEnabled() && isRemascTransaction) { + addFeesToRemasc(totalPaidFees, track); + } + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( tx, txindex, @@ -620,6 +643,8 @@ private BlockResult executeForMiningAfterRSKIP144( boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { + payToRemascWhenThereIsNoRemascTx(track, totalPaidFees, txindex, numberOfTransactions, isRemascTransaction); + if (!discardInvalidTxs) { return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } @@ -630,16 +655,19 @@ private BlockResult executeForMiningAfterRSKIP144( } Optional sublistGasAccumulated; - if (tx.isRemascTransaction(txindex, transactionsList.size())) { + if (isRemascTransaction) { sublistGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, txExecutor.getGasUsed()); } else { sublistGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getThisThreadReadKeys(), readWrittenKeysTracker.getThisThreadWrittenKeys(), txExecutor.getGasUsed()); } if (!acceptInvalidTransactions && !sublistGasAccumulated.isPresent()) { + payToRemascWhenThereIsNoRemascTx(track, totalPaidFees, txindex, numberOfTransactions, isRemascTransaction); + if (!discardInvalidTxs) { return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } + loggingDiscardedBlock(block, tx); txindex++; continue; @@ -659,6 +687,7 @@ private BlockResult executeForMiningAfterRSKIP144( totalPaidFees = totalPaidFees.add(paidFees); } + payToRemascWhenThereIsNoRemascTx(track, totalPaidFees, txindex, numberOfTransactions, isRemascTransaction); deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); @@ -677,7 +706,7 @@ private BlockResult executeForMiningAfterRSKIP144( loggingTxDone(); } - addFeesToRemasc(totalPaidFees, track); + saveOrCommitTrackState(saveState, track); @@ -714,8 +743,14 @@ private void saveOrCommitTrackState(boolean saveState, Repository track) { track.commit(); logger.trace("End committing track."); } + } - logger.trace("Building execution results."); + // This method is used just when a block does not contain REMASC as last transaction. It happens only for testing. + private void payToRemascWhenThereIsNoRemascTx(Repository track, Coin totalPaidFees, int txindex, int numberOfTransactions, boolean isRemascTransaction) { + boolean isLastTx = txindex == numberOfTransactions - 1; + if (this.remascEnabled && isLastTx && !isRemascTransaction) { + addFeesToRemasc(totalPaidFees, track); + } } private TransactionReceipt buildTransactionReceipt(Transaction tx, TransactionExecutor txExecutor, long gasUsed, long cumulativeGas) { diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index a97e449a4f7..ec3cc2a20df 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -1413,8 +1413,8 @@ private static BlockExecutor buildBlockExecutor(TrieStore store, RskSystemProper new ProgramInvokeFactoryImpl(), new PrecompiledContracts(cfg, bridgeSupportFactory), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) - ) - ); + ), + cfg.isRemascEnabled()); } public static class TestObjects { diff --git a/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java b/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java index 450bb7c18a9..88010d4b89d 100644 --- a/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java @@ -584,9 +584,8 @@ private Web3Impl internalCreateEnvironment(Blockchain blockchain, BlockExecutor blockExecutor = new BlockExecutor( config.getActivationConfig(), repositoryLocator, -// stateRootHandler, - this.transactionExecutorFactory - ); + this.transactionExecutorFactory, + config.isRemascEnabled()); MinerServer minerServer = new MinerServerImpl( config, diff --git a/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java b/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java index 085154e6b9e..b5de1e84312 100644 --- a/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java +++ b/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java @@ -794,8 +794,8 @@ void processBodyResponseWithTransactionAddsToBlockchain() { new ProgramInvokeFactoryImpl(), new PrecompiledContracts(config, bridgeSupportFactory), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) - ) - ); + ), + config.isRemascEnabled()); Assertions.assertEquals(1, block.getTransactionsList().size()); blockExecutor.executeAndFillAll(block, genesis.getHeader()); Assertions.assertEquals(21000, block.getFeesPaidToMiner().asBigInteger().intValueExact()); diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascProcessMinerFeesTest.java b/rskj-core/src/test/java/co/rsk/remasc/RemascProcessMinerFeesTest.java index bf9ab02fefe..ccabedfa79f 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascProcessMinerFeesTest.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascProcessMinerFeesTest.java @@ -1022,7 +1022,7 @@ private BlockExecutor buildBlockExecutor(RepositoryLocator repositoryLocator, Bl new ProgramInvokeFactoryImpl(), new PrecompiledContracts(config, bridgeSupportFactory), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) - ) - ); + ), + config.isRemascEnabled()); } } diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascStorageProviderTest.java b/rskj-core/src/test/java/co/rsk/remasc/RemascStorageProviderTest.java index c8acf8e187e..59b0d21e364 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascStorageProviderTest.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascStorageProviderTest.java @@ -37,7 +37,6 @@ import org.ethereum.core.*; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.Keccak256Helper; -import org.ethereum.datasource.HashMapDB; import org.ethereum.db.MutableRepository; import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl; @@ -455,8 +454,8 @@ void paysOnlyBlocksWithEnoughBalanceAccumulatedAfterRFS() throws IOException { new ProgramInvokeFactoryImpl(), new PrecompiledContracts(config, bridgeSupportFactory), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) - ) - ); + ), + config.isRemascEnabled()); for (Block b : blocks) { blockExecutor.executeAndFillAll(b, blockchain.getBestBlock().getHeader()); diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java index 763f62b069c..762b2d4406f 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java @@ -146,8 +146,8 @@ public void start() { programInvokeFactory, precompiledContracts, blockTxSignatureCache - ) - ); + ), + builder.getConfig().isRemascEnabled()); for(int i = 0; i <= this.initialHeight; i++) { int finalI = i; diff --git a/rskj-core/src/test/java/co/rsk/test/World.java b/rskj-core/src/test/java/co/rsk/test/World.java index a20e3122b54..2387cc825e8 100644 --- a/rskj-core/src/test/java/co/rsk/test/World.java +++ b/rskj-core/src/test/java/co/rsk/test/World.java @@ -183,8 +183,8 @@ public BlockExecutor getBlockExecutor() { programInvokeFactory, new PrecompiledContracts(config, bridgeSupportFactory), blockTxSignatureCache - ) - ); + ), + config.isRemascEnabled()); } return this.blockExecutor; diff --git a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java index b2814c8a3b8..d344cf75837 100644 --- a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java +++ b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java @@ -116,8 +116,8 @@ public Block build() { new ProgramInvokeFactoryImpl(), new PrecompiledContracts(config, bridgeSupportFactory), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) - ) - ); + ), + config.isRemascEnabled()); executor.executeAndFill(block, parent.getHeader()); } diff --git a/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java b/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java index 60e55bf8a8f..bd4dbbad5df 100644 --- a/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java +++ b/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java @@ -246,8 +246,8 @@ config, repositoryLocator, this.blockStore, blockFactory, new TestCompositeEther BlockExecutor blockExecutor = new BlockExecutor( config.getActivationConfig(), repositoryLocator, - transactionExecutorFactory - ); + transactionExecutorFactory, + config.isRemascEnabled()); BlockChainImpl blockChain = new BlockChainLoader( blockStore, receiptStore, diff --git a/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java b/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java index 6536c1506a4..5cf6764f19b 100644 --- a/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java +++ b/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java @@ -324,8 +324,8 @@ private void processBlockChainCommand(DslCommand cmd) { programInvokeFactory, null, world.getBlockTxSignatureCache() - ) - ); + ), + config.isRemascEnabled()); executor.executeAndFill(block, parent.getHeader()); world.saveBlock(name, block); parent = block; diff --git a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java index b425e2e1103..4065b990261 100644 --- a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java +++ b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java @@ -96,6 +96,19 @@ public void processBlock( Block block, Block parent) { BlockTxSignatureCache blockTxSignatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); for (Transaction tx : block.getTransactionsList()) { + + /* + * This method is a helper of the test "testSEND_1". It is replicating the + * behavior of BlockExecutor but slightly different. In the BlockExecutor, the + * fees are sent to the Remasc address once all the transactions are executed. + * Since the only test using this helper processes one transaction, so the loop has no sense. + * */ + + boolean isRemascTx = tx.isRemascTransaction(txindex, block.getTransactionsList().size()); + if (config.isRemascEnabled() && isRemascTx && totalPaidFees.compareTo(Coin.ZERO) > 0) { + track.addBalance(PrecompiledContracts.REMASC_ADDR, totalPaidFees); + } + TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory( config, null, @@ -113,13 +126,8 @@ public void processBlock( Block block, Block parent) { totalGasUsed += gasUsed; totalPaidFees = totalPaidFees.add(paidFees); - /* - * This method is a helper of the test "testSEND_1". It is replicating the - * behavior of BlockExecutor but slightly different. In the BlockExecutor, the - * fees are sent to the Remasc address once all the transactions are executed. - * Since the only test using this helper processes one transaction, so the loop has no sense. - * */ - if (config.isRemascEnabled() && totalPaidFees.compareTo(Coin.ZERO) > 0) { + boolean isLastTx = txindex == block.getTransactionsList().size(); + if (config.isRemascEnabled() && isLastTx && !isRemascTx && totalPaidFees.compareTo(Coin.ZERO) > 0) { track.addBalance(PrecompiledContracts.REMASC_ADDR, totalPaidFees); } diff --git a/rskj-core/src/test/java/org/ethereum/core/ImportLightTest.java b/rskj-core/src/test/java/org/ethereum/core/ImportLightTest.java index f4119da5dec..13fb9a10edc 100644 --- a/rskj-core/src/test/java/org/ethereum/core/ImportLightTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/ImportLightTest.java @@ -88,8 +88,8 @@ public static BlockChainImpl createBlockchain( new BlockExecutor( config.getActivationConfig(), repositoryLocator, - transactionExecutorFactory - ), + transactionExecutorFactory, + config.isRemascEnabled()), stateRootHandler ); diff --git a/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java b/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java index 0b53e529f04..6bae52c0cc7 100644 --- a/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java +++ b/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java @@ -167,8 +167,8 @@ public List runTestCase(BlockTestCase testCase) { new BlockExecutor( config.getActivationConfig(), new RepositoryLocator(trieStore, stateRootHandler), - transactionExecutorFactory - ), + transactionExecutorFactory, + config.isRemascEnabled()), stateRootHandler ); diff --git a/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java b/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java index b6f68e3d3c1..b457e2fac96 100644 --- a/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java +++ b/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java @@ -160,8 +160,8 @@ public List runImpl() { new ProgramInvokeFactoryImpl(), precompiledContracts, new BlockTxSignatureCache(new ReceivedTxSignatureCache()) - ) - ), + ), + config.isRemascEnabled()), stateRootHandler ); From 194fa4bedb71cad424339c5c8f486c0c7e546b1d Mon Sep 17 00:00:00 2001 From: Ilan Date: Fri, 18 Nov 2022 16:09:09 -0300 Subject: [PATCH 20/89] Move edges to block header extension --- .../java/org/ethereum/core/BlockFactory.java | 16 ++--- .../java/org/ethereum/core/BlockHeader.java | 60 +++++++------------ .../ethereum/core/BlockHeaderExtensionV1.java | 27 +++++++-- .../java/org/ethereum/core/BlockHeaderV0.java | 18 +++++- .../java/org/ethereum/core/BlockHeaderV1.java | 34 +++++++++-- .../main/java/org/ethereum/util/ByteUtil.java | 17 ++++++ .../co/rsk/core/BlockHeaderExtensionTest.java | 7 ++- .../rsk/core/BlockHeaderExtensionV1Test.java | 44 +++++++++++--- .../java/co/rsk/core/BlockHeaderTest.java | 14 +++-- .../java/co/rsk/core/BlockHeaderV0Test.java | 15 +++-- .../java/co/rsk/core/BlockHeaderV1Test.java | 3 +- .../net/messages/BodyResponseMessageTest.java | 3 +- .../java/co/rsk/net/messages/MessageTest.java | 3 +- .../java/org/ethereum/util/ByteUtilTest.java | 6 ++ 14 files changed, 187 insertions(+), 80 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java index 4062899b59f..d7c8352d011 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java @@ -26,13 +26,12 @@ import org.bouncycastle.util.BigIntegers; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ConsensusRule; +import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; import org.ethereum.util.RLPElement; import org.ethereum.util.RLPList; import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -156,10 +155,8 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { } short[] txExecutionSublistsEdges = null; - if (activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)) { - byte[] edgesBytes = rlpHeader.get(r++).getRLPRawData(); - txExecutionSublistsEdges = new short[edgesBytes.length / 2]; - ByteBuffer.wrap(edgesBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(txExecutionSublistsEdges); + if (rlpHeader.size() > r && activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)) { + txExecutionSublistsEdges = ByteUtil.rlpToShorts(rlpHeader.get(r++).getRLPRawData()); } byte[] bitcoinMergedMiningHeader = null; @@ -221,7 +218,12 @@ private boolean canBeDecoded(RLPList rlpHeader, long blockNumber) { int preParallelSizeAdjustment = activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber) ? 0 : 1; int expectedSize = RLP_HEADER_SIZE - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment; int expectedSizeMM = RLP_HEADER_SIZE_WITH_MERGED_MINING - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment; - return rlpHeader.size() == expectedSize || rlpHeader.size() == expectedSizeMM; + + int preHeaderExtensionAdjustment = activationConfig.isActive(ConsensusRule.RSKIP351, blockNumber) ? 1 : 0; + + return rlpHeader.size() == expectedSize || rlpHeader.size() == expectedSizeMM || + rlpHeader.size() == expectedSize - preHeaderExtensionAdjustment || + rlpHeader.size() == expectedSizeMM - preHeaderExtensionAdjustment; } private static BigInteger parseBigInteger(byte[] bytes) { diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index 10b59aba71f..361af5155df 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -9,13 +9,12 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import org.ethereum.crypto.HashUtil; +import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; import org.ethereum.util.Utils; import javax.annotation.Nullable; import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import java.util.Arrays; import java.util.List; @@ -32,11 +31,16 @@ public abstract class BlockHeader { public abstract byte getVersion(); public abstract BlockHeaderExtension getExtension(); public abstract void setExtension(BlockHeaderExtension extension); + // fields from block header extension public abstract byte[] getLogsBloom(); public abstract void setLogsBloom(byte[] logsBloom); + public abstract short[] getTxExecutionSublistsEdges(); // Edges of the transaction execution lists + public abstract void setTxExecutionSublistsEdges(short[] edges); + // encoding to use in logs bloom field on header response message public abstract byte[] getLogsBloomFieldEncoded(); + public abstract void addExtraFieldsToEncoded(boolean useExtensionEncoding, List fieldsToEncode); private static final int HASH_FOR_MERGED_MINING_PREFIX_LENGTH = 20; private static final int FORK_DETECTION_DATA_LENGTH = 12; @@ -94,9 +98,6 @@ public abstract class BlockHeader { private byte[] miningForkDetectionData; - /* Edges of the transaction execution lists */ - private short[] txExecutionSublistsEdges; - private final byte[] ummRoot; /** @@ -123,8 +124,7 @@ public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, by Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, Coin minimumGasPrice, int uncleCount, boolean sealed, - boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot, - short[] txExecutionSublistsEdges) { + boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot) { this.parentHash = parentHash; this.unclesHash = unclesHash; this.coinbase = coinbase; @@ -149,7 +149,6 @@ public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, by this.useRskip92Encoding = useRskip92Encoding; this.includeForkDetectionData = includeForkDetectionData; this.ummRoot = ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; - this.txExecutionSublistsEdges = txExecutionSublistsEdges != null ? Arrays.copyOf(txExecutionSublistsEdges, txExecutionSublistsEdges.length) : null; } @VisibleForTesting @@ -295,24 +294,14 @@ public Keccak256 getHash() { return this.hash; } - public byte[] getFullEncoded() { - // the encoded block header must include all fields, even the bitcoin PMT and coinbase which are not used for - // calculating RSKIP92 block hashes - return this.getEncoded(true, true, false); - } - - public byte[] getEncoded() { - // the encoded block header used for calculating block hashes including RSKIP92 - return this.getEncoded(true, !useRskip92Encoding, false); - } - - public byte[] getEncodedForHeaderMessage() { - return this.getEncoded(true, true, true); - } - public byte[] getEncodedForHash() { - return this.getEncoded(true, !useRskip92Encoding, true); - } + // the encoded block header must include all fields, even the bitcoin PMT and coinbase which are not used for + // calculating RSKIP92 block hashes + public byte[] getFullEncoded() { return this.getEncoded(true, true, false); } + // the encoded block header used for calculating block hashes including RSKIP92 + public byte[] getEncoded() { return this.getEncoded(true, !useRskip92Encoding, false); } + public byte[] getEncodedForHeaderMessage() { return this.getEncoded(true, true, true); } + public byte[] getEncodedForHash() { return this.getEncoded(true, !useRskip92Encoding, true); } @Nullable public Coin getMinimumGasPrice() { @@ -359,11 +348,7 @@ public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProof fieldToEncodeList.add(RLP.encodeElement(this.ummRoot)); } - if (this.txExecutionSublistsEdges != null) { - byte[] edgesBytes = new byte[this.txExecutionSublistsEdges.length * 2]; - ByteBuffer.wrap(edgesBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(this.txExecutionSublistsEdges); - fieldToEncodeList.add(RLP.encodeElement(edgesBytes)); - } + this.addExtraFieldsToEncoded(useExtensionEncoding, fieldToEncodeList); if (withMergedMiningFields && hasMiningFields()) { byte[] bitcoinMergedMiningHeader = RLP.encodeElement(this.bitcoinMergedMiningHeader); @@ -378,6 +363,13 @@ public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProof return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); } + public void addTxExecutionSublistsEdgesIfAny(List fieldsToEncode) { + short[] txExecutionSublistsEdges = this.getTxExecutionSublistsEdges(); + if (txExecutionSublistsEdges != null) { + fieldsToEncode.add(ByteUtil.shortsToRLP(txExecutionSublistsEdges)); + } + } + /** * This is here to override specific non-minimal instances such as the mainnet Genesis */ @@ -441,7 +433,7 @@ private String toStringWithSuffix(final String suffix) { toStringBuff.append(" timestamp=").append(timestamp).append(" (").append(Utils.longToDateTime(timestamp)).append(")").append(suffix); toStringBuff.append(" extraData=").append(toHexStringOrEmpty(extraData)).append(suffix); toStringBuff.append(" minGasPrice=").append(minimumGasPrice).append(suffix); - toStringBuff.append(" txExecutionSublistsEdges=").append(Arrays.toString(txExecutionSublistsEdges)).append(suffix); + toStringBuff.append(" txExecutionSublistsEdges=").append(Arrays.toString(this.getTxExecutionSublistsEdges())).append(suffix); return toStringBuff.toString(); } @@ -606,10 +598,4 @@ public boolean isParentOf(BlockHeader header) { public byte[] getUmmRoot() { return ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; } - - public short[] getTxExecutionSublistsEdges() { return this.txExecutionSublistsEdges != null ? Arrays.copyOf(this.txExecutionSublistsEdges, this.txExecutionSublistsEdges.length) : null; } - - public void setTxExecutionSublistsEdges(short[] edges) { - this.txExecutionSublistsEdges = edges != null? Arrays.copyOf(edges, edges.length) : null; - } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java index 9809413d352..93e07b63562 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java @@ -1,19 +1,33 @@ package org.ethereum.core; +import com.google.common.collect.Lists; import org.ethereum.crypto.HashUtil; +import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; +import java.util.Arrays; +import java.util.List; + public class BlockHeaderExtensionV1 extends BlockHeaderExtension { @Override public byte getHeaderVersion() { return 0x1; } private byte[] logsBloom; + private short[] txExecutionSublistsEdges; + public byte[] getLogsBloom() { return this.logsBloom; } public void setLogsBloom(byte[] logsBloom) { this.logsBloom = logsBloom; } - public BlockHeaderExtensionV1(byte[] logsBloom) { + public short[] getTxExecutionSublistsEdges() { return this.txExecutionSublistsEdges != null ? Arrays.copyOf(this.txExecutionSublistsEdges, this.txExecutionSublistsEdges.length) : null; } + + public void setTxExecutionSublistsEdges(short[] edges) { + this.txExecutionSublistsEdges = edges != null? Arrays.copyOf(edges, edges.length) : null; + } + + public BlockHeaderExtensionV1(byte[] logsBloom, short[] edges) { this.logsBloom = logsBloom; + this.setTxExecutionSublistsEdges(edges); } public byte[] getHash() { @@ -22,16 +36,17 @@ public byte[] getHash() { @Override public byte[] getEncoded() { - return RLP.encodeList( - RLP.encodeByte(this.getHeaderVersion()), - RLP.encodeElement(this.getLogsBloom()) - ); + List fieldToEncodeList = Lists.newArrayList(RLP.encodeByte(this.getHeaderVersion()), RLP.encodeElement(this.getLogsBloom())); + short[] txExecutionSublistsEdges = this.getTxExecutionSublistsEdges(); + if (txExecutionSublistsEdges != null) fieldToEncodeList.add(ByteUtil.shortsToRLP(this.getTxExecutionSublistsEdges())); + return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); } public static BlockHeaderExtensionV1 fromEncoded(byte[] encoded) { RLPList rlpExtension = RLP.decodeList(encoded); return new BlockHeaderExtensionV1( - rlpExtension.get(1).getRLPData() + rlpExtension.get(1).getRLPData(), + rlpExtension.size() == 3 ? ByteUtil.rlpToShorts(rlpExtension.get(2).getRLPData()): null ); } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java index 6c3f3621720..824ffedbf47 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java @@ -23,6 +23,9 @@ import co.rsk.core.RskAddress; import org.ethereum.util.RLP; +import java.util.Arrays; +import java.util.List; + public class BlockHeaderV0 extends BlockHeader { // block header for blocks before rskip351 @Override @@ -33,6 +36,7 @@ public class BlockHeaderV0 extends BlockHeader { public void setExtension(BlockHeaderExtension extension) {} private byte[] logsBloom; + private short[] txExecutionSublistsEdges; public BlockHeaderV0(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] logsBloom, BlockDifficulty difficulty, @@ -47,8 +51,9 @@ public BlockHeaderV0(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, mergedMiningForkDetectionData, minimumGasPrice, uncleCount, sealed, - useRskip92Encoding, includeForkDetectionData, ummRoot, txExecutionSublistsEdges); + useRskip92Encoding, includeForkDetectionData, ummRoot); this.logsBloom = logsBloom; + this.txExecutionSublistsEdges = txExecutionSublistsEdges != null ? Arrays.copyOf(txExecutionSublistsEdges, txExecutionSublistsEdges.length) : null; } @Override @@ -63,8 +68,19 @@ public void setLogsBloom(byte[] logsBloom) { this.logsBloom = logsBloom; } + public short[] getTxExecutionSublistsEdges() { return this.txExecutionSublistsEdges != null ? Arrays.copyOf(this.txExecutionSublistsEdges, this.txExecutionSublistsEdges.length) : null; } + + public void setTxExecutionSublistsEdges(short[] edges) { + this.txExecutionSublistsEdges = edges != null? Arrays.copyOf(edges, edges.length) : null; + } + @Override public byte[] getLogsBloomFieldEncoded() { return RLP.encodeElement(this.logsBloom); } + + @Override + public void addExtraFieldsToEncoded(boolean useExtensionEncoding, List fieldsToEncode) { + this.addTxExecutionSublistsEdgesIfAny(fieldsToEncode); + } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java index bea40f70c9b..408006ddba5 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java @@ -5,6 +5,9 @@ import co.rsk.core.RskAddress; import org.ethereum.util.RLP; +import java.util.Arrays; +import java.util.List; + public class BlockHeaderV1 extends BlockHeader { @Override public byte getVersion() { return 0x1; } @@ -26,9 +29,9 @@ public BlockHeaderV1(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, mergedMiningForkDetectionData, minimumGasPrice, uncleCount, sealed, - useRskip92Encoding, includeForkDetectionData, ummRoot, txExecutionSublistsEdges); + useRskip92Encoding, includeForkDetectionData, ummRoot); - this.extension = new BlockHeaderExtensionV1(logsBloom); + this.extension = new BlockHeaderExtensionV1(logsBloom, txExecutionSublistsEdges); } @Override @@ -46,12 +49,35 @@ public void setLogsBloom(byte[] logsBloom) { this.extension.setLogsBloom(logsBloom); } + @Override + public short[] getTxExecutionSublistsEdges() { return this.extension.getTxExecutionSublistsEdges(); } + + @Override + public void setTxExecutionSublistsEdges(short[] edges) { + /* A sealed block header is immutable, cannot be changed */ + if (this.sealed) { + throw new SealedBlockHeaderException("trying to alter logs bloom"); + } + this.hash = null; + + this.extension.setTxExecutionSublistsEdges(edges != null ? Arrays.copyOf(edges, edges.length) : null); + } + @Override public byte[] getLogsBloomFieldEncoded() { byte[] ecnoded = new byte[Bloom.BLOOM_BYTES]; ecnoded[0] = 0x1; - byte[] logsBloomHash = this.extension.getHash(); - System.arraycopy(logsBloomHash, 0, ecnoded, 1, logsBloomHash.length); + byte[] hash = this.extension.getHash(); + System.arraycopy(hash, 0, ecnoded, 1, hash.length); return RLP.encodeElement(ecnoded); } + + @Override + public void addExtraFieldsToEncoded(boolean useExtensionEncoding, List fieldsToEncode) { + if (!useExtensionEncoding) { + this.addTxExecutionSublistsEdgesIfAny(fieldsToEncode); + } + } + + } diff --git a/rskj-core/src/main/java/org/ethereum/util/ByteUtil.java b/rskj-core/src/main/java/org/ethereum/util/ByteUtil.java index 12c22f62f9b..ed4f2448868 100644 --- a/rskj-core/src/main/java/org/ethereum/util/ByteUtil.java +++ b/rskj-core/src/main/java/org/ethereum/util/ByteUtil.java @@ -26,6 +26,7 @@ import javax.annotation.Nullable; import java.math.BigInteger; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.Arrays; import java.util.HashSet; import java.util.Objects; @@ -674,4 +675,20 @@ public static boolean fastEquals(byte[] left, byte[] right) { left, 0, left.length, right, 0, right.length) == 0; } + + public static byte[] shortsToRLP(short[] shorts) { + byte[] edgesBytes = new byte[shorts.length * 2]; + ByteBuffer.wrap(edgesBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shorts); + return RLP.encodeElement(edgesBytes); + } + + public static short[] rlpToShorts(byte[] rlpData) { + if(rlpData.length == 0) return new short[]{}; + short[] shorts = new short[rlpData.length / 2]; + ByteBuffer.wrap(rlpData) + .order(ByteOrder.LITTLE_ENDIAN) + .asShortBuffer() + .get(shorts); + return shorts; + } } \ No newline at end of file diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java index e9443c1c43c..6d1cb2ae9c8 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java @@ -8,8 +8,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.Arrays; - public class BlockHeaderExtensionTest { @Test public void decodeV1() { @@ -18,7 +16,10 @@ public void decodeV1() { logsBloom[1] = 0x02; logsBloom[2] = 0x03; logsBloom[3] = 0x04; - BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(logsBloom); + + short[] edges = { 1, 2, 3, 4 }; + + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(logsBloom, edges); BlockHeaderExtension decoded = BlockHeaderExtension.fromEncoded( RLP.decodeList(extension.getEncoded()) diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java index 1caa1231146..835503781ed 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java @@ -10,28 +10,30 @@ import java.util.Arrays; public class BlockHeaderExtensionV1Test { + private static short[] EDGES = new short[] { 1, 2, 3, 4 }; @Test public void hasVersion1 () { - BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(TestUtils.randomBytes(256)); + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(TestUtils.randomBytes(256), EDGES); Assertions.assertEquals(1, extension.getHeaderVersion()); } @Test - public void createWithLogsBloom() { + public void createWithLogsBloomAndEdges() { byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; logsBloom[0] = 0x01; logsBloom[1] = 0x02; logsBloom[2] = 0x03; logsBloom[3] = 0x04; - BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(logsBloom); + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(logsBloom, EDGES); Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); + Assertions.assertArrayEquals(EDGES, extension.getTxExecutionSublistsEdges()); } @Test public void setLogsBloom() { - BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(new byte[32]); + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(new byte[32], EDGES); byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; logsBloom[0] = 0x01; @@ -44,6 +46,18 @@ public void setLogsBloom() { Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); } + + @Test + public void setEdges() { + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(new byte[32], EDGES); + + short[] edges = new short[] { 5, 6, 7, 8}; + + extension.setTxExecutionSublistsEdges(edges); + + Assertions.assertArrayEquals(edges, extension.getTxExecutionSublistsEdges()); + } + @Test public void hashIncludesLogsBloom() { byte[] logsBloom1 = new byte[Bloom.BLOOM_BYTES]; @@ -51,14 +65,29 @@ public void hashIncludesLogsBloom() { logsBloom1[1] = 0x02; logsBloom1[2] = 0x03; logsBloom1[3] = 0x04; - BlockHeaderExtensionV1 extension1 = new BlockHeaderExtensionV1(logsBloom1); + BlockHeaderExtensionV1 extension1 = new BlockHeaderExtensionV1(logsBloom1, EDGES); byte[] logsBloom2 = new byte[Bloom.BLOOM_BYTES]; logsBloom2[0] = 0x01; logsBloom2[1] = 0x02; logsBloom2[2] = 0x03; logsBloom2[3] = 0x05; - BlockHeaderExtensionV1 extension2 = new BlockHeaderExtensionV1(logsBloom2); + BlockHeaderExtensionV1 extension2 = new BlockHeaderExtensionV1(logsBloom2, EDGES); + + Assertions.assertFalse(Arrays.equals(extension1.getHash(), extension2.getHash())); + } + + @Test + public void hashIncludesEdges() { + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 0x01; + logsBloom[1] = 0x02; + logsBloom[2] = 0x03; + logsBloom[3] = 0x04; + BlockHeaderExtensionV1 extension1 = new BlockHeaderExtensionV1(logsBloom, EDGES); + + short[] edges2 = new short[] { 5, 6, 7, 8 }; + BlockHeaderExtensionV1 extension2 = new BlockHeaderExtensionV1(logsBloom, edges2); Assertions.assertFalse(Arrays.equals(extension1.getHash(), extension2.getHash())); } @@ -72,9 +101,10 @@ public void encodeDecode() { logsBloom[3] = 0x04; BlockHeaderExtensionV1 extension = BlockHeaderExtensionV1.fromEncoded( - new BlockHeaderExtensionV1(logsBloom).getEncoded() + new BlockHeaderExtensionV1(logsBloom, EDGES).getEncoded() ); Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); + Assertions.assertArrayEquals(EDGES, extension.getTxExecutionSublistsEdges()); } } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java index 08b8af5b4e4..4a033579fe8 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java @@ -366,7 +366,7 @@ void verifyRecalculatedHashForAmendedBlocks() { BlockHeader header = createBlockHeader(new byte[80], new byte[32], new byte[128], new byte[0], false, new byte[0], new short[0], false, false); - assertArrayEquals(HashUtil.keccak256(header.getEncoded()), header.getHash().getBytes()); + assertArrayEquals(HashUtil.keccak256(header.getEncodedForHash()), header.getHash().getBytes()); List> stateModifiers = Arrays.asList( h -> h.setBitcoinMergedMiningCoinbaseTransaction(HashUtil.keccak256("BitcoinMergedMiningCoinbaseTransaction".getBytes())), @@ -384,7 +384,7 @@ void verifyRecalculatedHashForAmendedBlocks() { stateModifiers.forEach(sm -> { sm.accept(header); - assertArrayEquals(HashUtil.keccak256(header.getEncoded()), + assertArrayEquals(HashUtil.keccak256(header.getEncodedForHash()), header.getHash().getBytes(), "Block header returned invalid hash after modification"); }); @@ -481,7 +481,7 @@ public void fullEncodedV0IsTheSameAsEncodedForHeaderMessage () { } @Test - public void fullEncodedV1IsTheSameAsEncodedForHeaderMessageButLogsBloom () { + public void fullEncodedV1IsTheSameAsEncodedForHeaderMessageButLogsBloomAndEdges () { byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; logsBloom[0] = 0x01; logsBloom[1] = 0x02; @@ -494,11 +494,13 @@ public void fullEncodedV1IsTheSameAsEncodedForHeaderMessageButLogsBloom () { RLPList fullEncoded = RLP.decodeList(headerV1.getFullEncoded()); RLPList encodedForHeaderMessage = RLP.decodeList(headerV1.getEncodedForHeaderMessage()); - Assertions.assertEquals(fullEncoded.size(), encodedForHeaderMessage.size()); + Assertions.assertEquals(fullEncoded.size() - 1, encodedForHeaderMessage.size()); - for (int i = 0; i < fullEncoded.size(); i++) + for (int i = 0; i < encodedForHeaderMessage.size(); i++) { + int j = i < 16 ? i : i + 1; //padding if extension has edges if (i != 6) // logs bloom field - Assertions.assertArrayEquals(fullEncoded.get(i).getRLPData(), encodedForHeaderMessage.get(i).getRLPData()); + Assertions.assertArrayEquals(fullEncoded.get(j).getRLPData(), encodedForHeaderMessage.get(i).getRLPData()); + } Assertions.assertFalse(Arrays.equals(fullEncoded.get(6).getRLPData(), encodedForHeaderMessage.get(6).getRLPData())); } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java index 181a3352b2e..94c61d78ffa 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java @@ -15,7 +15,7 @@ import java.util.Arrays; public class BlockHeaderV0Test { - private BlockHeaderV0 createBlockHeader(byte[] logsBloom) { + private BlockHeaderV0 createBlockHeader(byte[] logsBloom, short[] edges) { return new BlockHeaderV0( PegTestUtils.createHash3().getBytes(), HashUtil.keccak256(RLP.encodeList()), @@ -41,21 +41,23 @@ private BlockHeaderV0 createBlockHeader(byte[] logsBloom) { false, false, null, - new short[0] + edges ); } @Test void hasNullExtension() { - BlockHeaderV0 header = createBlockHeader(TestUtils.randomBytes(256)); + short[] edges = new short[]{ 1, 2, 3, 4 }; + BlockHeaderV0 header = createBlockHeader(TestUtils.randomBytes(256), edges); Assertions.assertEquals(null, header.getExtension()); } @Test void setsExtensionIsVoid() { - BlockHeaderV0 header = createBlockHeader(TestUtils.randomBytes(256)); + short[] edges = new short[]{ 1, 2, 3, 4 }; + BlockHeaderV0 header = createBlockHeader(TestUtils.randomBytes(256), edges); byte[] bloom = Arrays.copyOf(header.getLogsBloom(), header.getLogsBloom().length); - header.setExtension(new BlockHeaderExtensionV1(TestUtils.randomBytes(256))); + header.setExtension(new BlockHeaderExtensionV1(TestUtils.randomBytes(256), edges)); Assertions.assertEquals(null, header.getExtension()); Assertions.assertArrayEquals(bloom, header.getLogsBloom()); } @@ -63,7 +65,8 @@ void setsExtensionIsVoid() { @Test void logsBloomFieldEncoded() { byte[] bloom = TestUtils.randomBytes(256); - BlockHeaderV0 header = createBlockHeader(bloom); + short[] edges = new short[]{ 1, 2, 3, 4 }; + BlockHeaderV0 header = createBlockHeader(bloom, edges); byte[] field = RLP.decode2(header.getLogsBloomFieldEncoded()).get(0).getRLPData(); Assertions.assertArrayEquals(bloom, field); } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java index 3c15c333c69..670702d67bf 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java @@ -55,8 +55,9 @@ void createsAnExtensionWithGivenData() { @Test void setsExtension() { byte[] bloom = TestUtils.randomBytes(256); + short[] edges = new short[]{ 1, 2, 3, 4 }; BlockHeaderV1 header = createBlockHeader(bloom); - BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(bloom); + BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(bloom, edges); header.setExtension(extension); Assertions.assertArrayEquals(extension.getEncoded(), header.getExtension().getEncoded()); } diff --git a/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java b/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java index 0c7644ebcde..52254ee9e4d 100644 --- a/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java +++ b/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java @@ -60,7 +60,8 @@ void createMessage() { @Test void createMessageWithExtension() { Bloom bloom = new Bloom(); - BlockHeaderExtension extension = new BlockHeaderExtensionV1(bloom.getData()); + short[] edges = new short[]{ 1, 2, 3, 4 }; + BlockHeaderExtension extension = new BlockHeaderExtensionV1(bloom.getData(), edges); BodyResponseMessage message = testCreateMessage(extension); Assertions.assertArrayEquals(extension.getEncoded(), message.getBlockHeaderExtension().getEncoded()); } diff --git a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java index 5fa6e74d962..4037cbab5c1 100644 --- a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java +++ b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java @@ -546,7 +546,8 @@ void encodeDecodeBodyResponseMessageWithExtension() { } byte[] bloom = new byte[]{ 1, 2, 3, 4 }; - BlockHeaderExtension extension = new BlockHeaderExtensionV1(bloom); + short[] edges = new short[]{ 1, 2, 3, 4 }; + BlockHeaderExtension extension = new BlockHeaderExtensionV1(bloom, edges); BodyResponseMessage message = new BodyResponseMessage(100, transactions, uncles, extension); diff --git a/rskj-core/src/test/java/org/ethereum/util/ByteUtilTest.java b/rskj-core/src/test/java/org/ethereum/util/ByteUtilTest.java index 9741a99ef15..2f2c4ead3bd 100644 --- a/rskj-core/src/test/java/org/ethereum/util/ByteUtilTest.java +++ b/rskj-core/src/test/java/org/ethereum/util/ByteUtilTest.java @@ -513,4 +513,10 @@ void testToBytesWithLeadingZeros_WithLeadingZeros() { Assertions.assertEquals(src[i - srcStart], actualResult[i]); } } + + @Test + void shortsRLP() { + short[] shorts = new short[]{ 1, 2, 3, 5, 8, 13 }; + Assertions.assertArrayEquals(shorts, ByteUtil.rlpToShorts(RLP.decode2(ByteUtil.shortsToRLP(shorts)).get(0).getRLPData())); + } } From e53293325e827e14c5bf13ce3e37df2c1dc4c888 Mon Sep 17 00:00:00 2001 From: Ilan Date: Tue, 22 Nov 2022 11:40:41 -0300 Subject: [PATCH 21/89] Add RSKIP 144 to fingerroot500 --- rskj-core/src/main/resources/reference.conf | 2 +- .../src/test/java/co/rsk/cli/tools/CliToolsTest.java | 11 +++++++---- .../blockchain/upgrades/ActivationConfigTest.java | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/rskj-core/src/main/resources/reference.conf b/rskj-core/src/main/resources/reference.conf index 8471b19f9f4..91c899a691a 100644 --- a/rskj-core/src/main/resources/reference.conf +++ b/rskj-core/src/main/resources/reference.conf @@ -64,7 +64,7 @@ blockchain = { rskip294 = hop400 rskip297 = hop400 rskip351 = fingerroot500 - rskip144 = hop400 + rskip144 = fingerroot500 rskip353 = hop401 rskip357 = hop401 } diff --git a/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java b/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java index 3c307270e5b..9eb8797e7d5 100644 --- a/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java +++ b/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java @@ -335,11 +335,12 @@ void testImportBlocks(World world) throws IOException, DslProcessorException { } @Test - void importBlocks() throws IOException, DslProcessorException { + void importBlocksWithoutRskip351() throws IOException, DslProcessorException { ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); TestSystemProperties config = new TestSystemProperties(rawConfig -> - rawConfig.withValue("blockchain.config.hardforkActivationHeights.fingerroot500", ConfigValueFactory.fromAnyRef(-1)) + rawConfig.withValue("blockchain.config.consensusRules.rskip351", ConfigValueFactory.fromAnyRef(-1)) ); + Assertions.assertFalse(config.getActivationConfig().isActive(ConsensusRule.RSKIP351, 3)); World world = new World(receiptStore, config); testImportBlocks(world); } @@ -348,14 +349,16 @@ void importBlocks() throws IOException, DslProcessorException { void importBlocksWithRskip351InMiddle() throws IOException, DslProcessorException { ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); TestSystemProperties config = new TestSystemProperties(rawConfig -> - rawConfig.withValue("blockchain.config.hardforkActivationHeights.fingerroot500", ConfigValueFactory.fromAnyRef(2)) + rawConfig.withValue("blockchain.config.consensusRules.rskip351", ConfigValueFactory.fromAnyRef(2)) ); + Assertions.assertFalse(config.getActivationConfig().isActive(ConsensusRule.RSKIP351, 1)); + Assertions.assertTrue(config.getActivationConfig().isActive(ConsensusRule.RSKIP351, 2)); World world = new World(receiptStore, config); testImportBlocks(world); } @Test - void importBlocksWithRskip351() throws IOException, DslProcessorException { + void importBlocks() throws IOException, DslProcessorException { ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); World world = new World(receiptStore); testImportBlocks(world); diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index adeb03fd736..cadb3ed2036 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -101,7 +101,7 @@ class ActivationConfigTest { " rskip290: hop400", " rskip293: hop400", " rskip294: hop400", - " rskip144: hop400", + " rskip144: fingerroot500", " rskip297: hop400", " rskip351: fingerroot500", " rskip353: hop401", From 51608e39f60c03c8cc2db83cc130e2a87512c684 Mon Sep 17 00:00:00 2001 From: Ilan Date: Wed, 23 Nov 2022 14:59:06 -0300 Subject: [PATCH 22/89] General fixes + refactor getSublistCandidate --- .../co/rsk/core/TransactionListExecutor.java | 72 +++++---- .../java/co/rsk/core/bc/BlockExecutor.java | 139 +++++++++++------- .../bc/ParallelizeTransactionHandler.java | 82 ++++++----- .../java/org/ethereum/core/BlockHeader.java | 20 ++- .../ethereum/core/BlockHeaderExtension.java | 8 +- .../ethereum/core/BlockHeaderExtensionV1.java | 8 +- .../java/org/ethereum/core/BlockHeaderV0.java | 6 +- .../java/co/rsk/core/BlockFactoryTest.java | 4 +- .../co/rsk/core/BlockHeaderExtensionTest.java | 21 +++ .../java/co/rsk/core/BlockHeaderTest.java | 2 +- .../java/co/rsk/core/BlockHeaderV1Test.java | 2 +- .../bc/ParallelizeTransactionHandlerTest.java | 42 ++++++ 12 files changed, 278 insertions(+), 128 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 1cbcba0944c..e49da9efb5b 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -83,9 +83,7 @@ public Boolean call() { int numberOfTransactions = block.getTransactionsList().size(); boolean isRemascTransaction = tx.isRemascTransaction(this.i, numberOfTransactions); - if (this.remascEnabled && isRemascTransaction) { - addFeesToRemasc(); - } + addFeesToRemascIfEnabled(isRemascTransaction); TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( tx, @@ -101,16 +99,7 @@ public Boolean call() { boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { - // It's used just for testing, the last tx should be always the REMASC. - payToRemascWhenThereIsNoRemascTx(numberOfTransactions, isRemascTransaction); - if (!discardInvalidTxs) { - logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", - block.getNumber(), tx.getHash() - ); - return false; - } - - logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); + if (discardIfInvalid(tx, numberOfTransactions, isRemascTransaction)) return false; continue; } @@ -130,31 +119,21 @@ public Boolean call() { long txGasUsed = txExecutor.getGasUsed(); totalGasUsed += txGasUsed; - Coin txPaidFees = txExecutor.getPaidFees(); - if (txPaidFees != null) { - totalPaidFees = totalPaidFees.add(txPaidFees); - } + addPaidFeesToToal(txExecutor); // It's used just for testing, the last tx should be always the REMASC. payToRemascWhenThereIsNoRemascTx(numberOfTransactions, isRemascTransaction); deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); - TransactionReceipt receipt = new TransactionReceipt(); - receipt.setGasUsed(txGasUsed); - receipt.setCumulativeGas(totalGasUsed); - - receipt.setTxStatus(txExecutor.getReceipt().isSuccessful()); - receipt.setTransaction(tx); - receipt.setLogInfoList(txExecutor.getVMLogs()); - receipt.setStatus(txExecutor.getReceipt().getStatus()); + TransactionReceipt receipt = createTransactionReceipt(totalGasUsed, tx, txExecutor, txGasUsed); logger.trace("block: [{}] executed tx: [{}]", block.getNumber(), tx.getHash()); - logger.trace("tx[{}].receipt", i + 1); - i++; + logger.trace("tx[{}].receipt", i); + receipts.put(i, receipt); logger.trace("tx[{}] done", i); @@ -163,6 +142,45 @@ public Boolean call() { return true; } + private boolean discardIfInvalid(Transaction tx, int numberOfTransactions, boolean isRemascTransaction) { + // It's used just for testing, the last tx should be always the REMASC. + payToRemascWhenThereIsNoRemascTx(numberOfTransactions, isRemascTransaction); + if (!discardInvalidTxs) { + logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", + block.getNumber(), tx.getHash() + ); + return true; + } + + logger.warn("block: [{}] discarded tx: [{}]", block.getNumber(), tx.getHash()); + return false; + } + + private TransactionReceipt createTransactionReceipt(long totalGasUsed, Transaction tx, TransactionExecutor txExecutor, long txGasUsed) { + TransactionReceipt receipt = new TransactionReceipt(); + receipt.setGasUsed(txGasUsed); + receipt.setCumulativeGas(totalGasUsed); + + receipt.setTxStatus(txExecutor.getReceipt().isSuccessful()); + receipt.setTransaction(tx); + receipt.setLogInfoList(txExecutor.getVMLogs()); + receipt.setStatus(txExecutor.getReceipt().getStatus()); + return receipt; + } + + private void addPaidFeesToToal(TransactionExecutor txExecutor) { + Coin txPaidFees = txExecutor.getPaidFees(); + if (txPaidFees != null) { + totalPaidFees = totalPaidFees.add(txPaidFees); + } + } + + private void addFeesToRemascIfEnabled(boolean isRemascTransaction) { + if (this.remascEnabled && isRemascTransaction) { + addFeesToRemasc(); + } + } + private void payToRemascWhenThereIsNoRemascTx(int numberOfTransactions, boolean isRemascTransaction) { boolean isLastTx = this.i == numberOfTransactions - 1; if (this.remascEnabled && isLastTx && !isRemascTransaction) { diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index e7011f2d7d6..7f46e7f9a5a 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -340,9 +340,7 @@ private BlockResult executePreviousRSKIP144( for (Transaction tx : block.getTransactionsList()) { - if (this.isRemascEnabled() && tx.isRemascTransaction(block.getTransactionsList().size(), txindex)) { - addFeesToRemasc(totalPaidFees, track); - } + addFeesToRemascIfRemascTx(block, track, totalPaidFees, txindex, tx); loggingApplyBlockToTx(block, i); @@ -367,23 +365,11 @@ private BlockResult executePreviousRSKIP144( continue; } - executedTransactions.add(tx); - - if (this.registerProgramResults) { - this.transactionResults.put(tx.getHash(), txExecutor.getResult()); - } - - if (vmTrace) { - txExecutor.extractTrace(programTraceProcessor); - } - - loggingTxExecuted(); + registerExecutedTx(programTraceProcessor, vmTrace, executedTransactions, tx, txExecutor); long gasUsed = txExecutor.getGasUsed(); totalGasUsed += gasUsed; - Coin paidFees = txExecutor.getPaidFees(); - if (paidFees != null) { - totalPaidFees = totalPaidFees.add(paidFees); - } + + totalPaidFees = addTotalPaidFees(totalPaidFees, txExecutor); deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); @@ -416,6 +402,34 @@ private BlockResult executePreviousRSKIP144( return result; } + private void registerExecutedTx(ProgramTraceProcessor programTraceProcessor, boolean vmTrace, List executedTransactions, Transaction tx, TransactionExecutor txExecutor) { + executedTransactions.add(tx); + + if (this.registerProgramResults) { + this.transactionResults.put(tx.getHash(), txExecutor.getResult()); + } + + if (vmTrace) { + txExecutor.extractTrace(programTraceProcessor); + } + + loggingTxExecuted(); + } + + private void addFeesToRemascIfRemascTx(Block block, Repository track, Coin totalPaidFees, int txindex, Transaction tx) { + if (this.isRemascEnabled() && tx.isRemascTransaction(block.getTransactionsList().size(), txindex)) { + addFeesToRemasc(totalPaidFees, track); + } + } + + private Coin addTotalPaidFees(Coin totalPaidFees, TransactionExecutor txExecutor) { + Coin paidFees = txExecutor.getPaidFees(); + if (paidFees != null) { + totalPaidFees = totalPaidFees.add(paidFees); + } + return totalPaidFees; + } + private BlockResult executeParallel( @Nullable ProgramTraceProcessor programTraceProcessor, int vmTraceOptions, @@ -625,9 +639,7 @@ private BlockResult executeForMiningAfterRSKIP144( int numberOfTransactions = transactionsList.size(); boolean isRemascTransaction = tx.isRemascTransaction(txindex, numberOfTransactions); - if (this.isRemascEnabled() && isRemascTransaction) { - addFeesToRemasc(totalPaidFees, track); - } + addFeesToRemascIfRemascTxAndTrack(track, totalPaidFees, isRemascTransaction); TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( tx, @@ -643,53 +655,32 @@ private BlockResult executeForMiningAfterRSKIP144( boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { - payToRemascWhenThereIsNoRemascTx(track, totalPaidFees, txindex, numberOfTransactions, isRemascTransaction); - - if (!discardInvalidTxs) { + if (discardIfInvalid(block, discardInvalidTxs, track, totalPaidFees, txindex, tx, numberOfTransactions, isRemascTransaction)) { return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } - - loggingDiscardedBlock(block, tx); txindex++; continue; } - Optional sublistGasAccumulated; - if (isRemascTransaction) { - sublistGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, txExecutor.getGasUsed()); - } else { - sublistGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getThisThreadReadKeys(), readWrittenKeysTracker.getThisThreadWrittenKeys(), txExecutor.getGasUsed()); - } + Optional sublistGasAccumulated = calculateSublistGasAccumulated(readWrittenKeysTracker, parallelizeTransactionHandler, tx, isRemascTransaction, txExecutor); if (!acceptInvalidTransactions && !sublistGasAccumulated.isPresent()) { - payToRemascWhenThereIsNoRemascTx(track, totalPaidFees, txindex, numberOfTransactions, isRemascTransaction); - - if (!discardInvalidTxs) { + if (discardIfInvalid(block, discardInvalidTxs, track, totalPaidFees, txindex, tx, numberOfTransactions, isRemascTransaction)) { return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } - - loggingDiscardedBlock(block, tx); txindex++; continue; } - readWrittenKeysTracker.clear(); - - if (this.registerProgramResults) { - this.transactionResults.put(tx.getHash(), txExecutor.getResult()); - } + registerTxExecutedForMiningAfterRSKIP144(readWrittenKeysTracker, tx, txExecutor); - loggingTxExecuted(); long gasUsed = txExecutor.getGasUsed(); gasUsedInBlock += gasUsed; - Coin paidFees = txExecutor.getPaidFees(); - if (paidFees != null) { - totalPaidFees = totalPaidFees.add(paidFees); - } + totalPaidFees = addTotalPaidFees(totalPaidFees, txExecutor); payToRemascWhenThereIsNoRemascTx(track, totalPaidFees, txindex, numberOfTransactions, isRemascTransaction); - deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); + deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); //orElseGet is used for testing only when acceptInvalidTransactions is set. long cumulativeGas = sublistGasAccumulated @@ -712,11 +703,7 @@ private BlockResult executeForMiningAfterRSKIP144( List executedTransactions = parallelizeTransactionHandler.getTransactionsInOrder(); short[] sublistOrder = parallelizeTransactionHandler.getTransactionsPerSublistInOrder(); - List receipts = new ArrayList<>(); - - for (Transaction tx : executedTransactions) { - receipts.add(receiptsByTx.get(tx)); - } + List receipts = getTransactionReceipts(receiptsByTx, executedTransactions); BlockResult result = new BlockResult( block, @@ -732,6 +719,52 @@ private BlockResult executeForMiningAfterRSKIP144( return result; } + private void registerTxExecutedForMiningAfterRSKIP144(IReadWrittenKeysTracker readWrittenKeysTracker, Transaction tx, TransactionExecutor txExecutor) { + readWrittenKeysTracker.clear(); + + if (this.registerProgramResults) { + this.transactionResults.put(tx.getHash(), txExecutor.getResult()); + } + + loggingTxExecuted(); + } + + private List getTransactionReceipts(Map receiptsByTx, List executedTransactions) { + List receipts = new ArrayList<>(); + + for (Transaction tx : executedTransactions) { + receipts.add(receiptsByTx.get(tx)); + } + return receipts; + } + + private boolean discardIfInvalid(Block block, boolean discardInvalidTxs, Repository track, Coin totalPaidFees, int txindex, Transaction tx, int numberOfTransactions, boolean isRemascTransaction) { + payToRemascWhenThereIsNoRemascTx(track, totalPaidFees, txindex, numberOfTransactions, isRemascTransaction); + + if (!discardInvalidTxs) { + return true; + } + + loggingDiscardedBlock(block, tx); + return false; + } + + private Optional calculateSublistGasAccumulated(IReadWrittenKeysTracker readWrittenKeysTracker, ParallelizeTransactionHandler parallelizeTransactionHandler, Transaction tx, boolean isRemascTransaction, TransactionExecutor txExecutor) { + Optional sublistGasAccumulated; + if (isRemascTransaction) { + sublistGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, txExecutor.getGasUsed()); + } else { + sublistGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getThisThreadReadKeys(), readWrittenKeysTracker.getThisThreadWrittenKeys(), txExecutor.getGasUsed()); + } + return sublistGasAccumulated; + } + + private void addFeesToRemascIfRemascTxAndTrack(Repository track, Coin totalPaidFees, boolean isRemascTransaction) { + if (this.isRemascEnabled() && isRemascTransaction) { + addFeesToRemasc(totalPaidFees, track); + } + } + private void saveOrCommitTrackState(boolean saveState, Repository track) { logger.trace("End txs executions."); if (saveState) { diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index 0d3861cf058..c8fbf8f5e87 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -151,64 +151,78 @@ private Optional getAvailableSublistWithLessUsedGas(long txG return sublistCandidate; } - private TransactionSublist getSublistCandidates(Transaction tx, Set newReadKeys, Set newWrittenKeys) { Optional sublistCandidate = getSublistBySender(tx); if (sublistCandidate.isPresent() && sublistCandidate.get().isSequential()) { - return getSequentialSublist(); + // there is a tx with the same sender in the sequential sublist + return sublistCandidate.get(); } - // read - written + // analyze reads for (ByteArrayWrapper newReadKey : newReadKeys) { + // read - written if (sublistsHavingWrittenToKey.containsKey(newReadKey)) { TransactionSublist sublist = sublistsHavingWrittenToKey.get(newReadKey); - sublistCandidate = Optional.of(sublistCandidate.map(sc -> returnsSequentialIfBothAreDifferent(sc, sublist)).orElse(sublist)); - } - } - if (sublistCandidate.isPresent() && sublistCandidate.get().isSequential()) { - return sublistCandidate.get(); + if (sublist.isSequential()) { + // there is a tx with read-written collision in sequential sublist + return sublist; + } + if (!sublistCandidate.isPresent()) { + // this is the new candidate + sublistCandidate = Optional.of(sublist); + } else if (!sublistCandidate.get().equals(sublist)) { + // use the sequential sublist (greedy decision) + return getSequentialSublist(); + } + } } + // analyze writes for (ByteArrayWrapper newWrittenKey : newWrittenKeys) { - // written - written, + // write - written if (sublistsHavingWrittenToKey.containsKey(newWrittenKey)) { TransactionSublist sublist = sublistsHavingWrittenToKey.get(newWrittenKey); - sublistCandidate = Optional.of(sublistCandidate.map(sc -> returnsSequentialIfBothAreDifferent(sc, sublist)).orElse(sublist)); - } - if (sublistCandidate.isPresent() && sublistCandidate.get().isSequential()) { - return sublistCandidate.get(); + if (sublist.isSequential()) { + // there is a tx with write-written collision in sequential sublist + return sublist; + } + if (!sublistCandidate.isPresent()) { + // this is the new candidate + sublistCandidate = Optional.of(sublist); + } else if (!sublistCandidate.get().equals(sublist)) { + // use the sequential sublist (greedy decision) + return getSequentialSublist(); + } } - // read - written - if (sublistsHavingReadFromKey.containsKey(newWrittenKey)) { - Set sublist = sublistsHavingReadFromKey.get(newWrittenKey); - if (sublist.size() > 1) { + // write - read + if (sublistsHavingReadFromKey.containsKey(newWrittenKey)) { + Set sublists = sublistsHavingReadFromKey.get(newWrittenKey); + if (sublists.size() > 1) { + // there is a write-read collision with multiple sublists return getSequentialSublist(); } - sublistCandidate = Optional.of(sublistCandidate.map(sc -> getTransactionSublistForReadKeys(sublist, sc)).orElse(getNextSublist(sublist))); + // there is only one colluded sublist + TransactionSublist sublist = getNextSublist(sublists); + if (!sublistCandidate.isPresent()) { + // if there is no candidate, take the colluded sublist + sublistCandidate = Optional.of(sublist); + } else if (!sublistCandidate.get().equals(sublist)) { + // otherwise, check if the sublist is different from the candidate and return the sequential + return getSequentialSublist(); + } } } - return sublistCandidate.orElseGet(() -> getAvailableSublistWithLessUsedGas(GasCost.toGas(tx.getGasLimit())).orElseGet(this::getSequentialSublist)); - } - - private TransactionSublist getTransactionSublistForReadKeys(Set sublist, TransactionSublist sc) { - if (!sublist.contains(sc)) { - return getSequentialSublist(); - } - - return getNextSublist(sublist); - } - - private TransactionSublist returnsSequentialIfBothAreDifferent(TransactionSublist sublist1, TransactionSublist sublist2) { - if (!sublist1.equals(sublist2)) { - return getSequentialSublist(); - } - return sublist1; + // if there is no candidate use the sublist with more gas available + // if the is no more gas available in any parallel sublist use the sequential + return sublistCandidate + .orElseGet(() -> getAvailableSublistWithLessUsedGas(GasCost.toGas(tx.getGasLimit())) + .orElseGet(this::getSequentialSublist)); } private TransactionSublist getNextSublist(Set sublist) { diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index 361af5155df..bea5356d9fa 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -1,3 +1,21 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program 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. + * + * This program 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 this program. If not, see . + */ package org.ethereum.core; import co.rsk.config.RskMiningConstants; @@ -118,7 +136,7 @@ public abstract class BlockHeader { /* Indicates if Block hash for merged mining should have the format described in RSKIP-110 */ private final boolean includeForkDetectionData; - public BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, + protected BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, byte[] txTrieRoot, byte[] receiptTrieRoot, BlockDifficulty difficulty, long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java index bf0fcd8537f..db154461919 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java @@ -2,11 +2,11 @@ import org.ethereum.util.RLPList; -public abstract class BlockHeaderExtension { - public abstract byte getHeaderVersion(); - public abstract byte[] getEncoded(); +public interface BlockHeaderExtension { + byte getHeaderVersion(); + byte[] getEncoded(); - public static BlockHeaderExtension fromEncoded(RLPList encoded) { + static BlockHeaderExtension fromEncoded(RLPList encoded) { byte version = encoded.get(0).getRLPData()[0]; if (version == 0x1) return BlockHeaderExtensionV1.fromEncoded(encoded.getRLPData()); return null; diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java index 93e07b63562..7cce383f03f 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java @@ -9,7 +9,7 @@ import java.util.Arrays; import java.util.List; -public class BlockHeaderExtensionV1 extends BlockHeaderExtension { +public class BlockHeaderExtensionV1 implements BlockHeaderExtension { @Override public byte getHeaderVersion() { return 0x1; } @@ -27,7 +27,7 @@ public void setTxExecutionSublistsEdges(short[] edges) { public BlockHeaderExtensionV1(byte[] logsBloom, short[] edges) { this.logsBloom = logsBloom; - this.setTxExecutionSublistsEdges(edges); + this.txExecutionSublistsEdges = edges != null ? Arrays.copyOf(edges, edges.length) : null; } public byte[] getHash() { @@ -38,7 +38,9 @@ public byte[] getHash() { public byte[] getEncoded() { List fieldToEncodeList = Lists.newArrayList(RLP.encodeByte(this.getHeaderVersion()), RLP.encodeElement(this.getLogsBloom())); short[] txExecutionSublistsEdges = this.getTxExecutionSublistsEdges(); - if (txExecutionSublistsEdges != null) fieldToEncodeList.add(ByteUtil.shortsToRLP(this.getTxExecutionSublistsEdges())); + if (txExecutionSublistsEdges != null) { + fieldToEncodeList.add(ByteUtil.shortsToRLP(this.getTxExecutionSublistsEdges())); + } return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java index 824ffedbf47..d172b632b7a 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java @@ -31,9 +31,11 @@ public class BlockHeaderV0 extends BlockHeader { @Override public byte getVersion() { return 0x0; } @Override - public BlockHeaderExtension getExtension() { return null; } + public BlockHeaderExtension getExtension() { return null; } // block header v0 has no extension @Override - public void setExtension(BlockHeaderExtension extension) {} + public void setExtension(BlockHeaderExtension extension) { + // block header v0 has no extension + } private byte[] logsBloom; private short[] txExecutionSublistsEdges; diff --git a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java index 1ce61e475b6..00adc54fc44 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java @@ -299,7 +299,7 @@ public void headerIsVersion0BeforeActivation () { long number = 20L; enableRskip351At(number); BlockHeader header = factory.getBlockHeaderBuilder().setNumber(number - 1).build(); - Assertions.assertEquals(header.getVersion(), 0); + Assertions.assertEquals(0, header.getVersion()); } @Test @@ -307,7 +307,7 @@ public void headerIsVersion1AfterActivation () { long number = 20L; enableRskip351At(number); BlockHeader header = factory.getBlockHeaderBuilder().setNumber(number).build(); - Assertions.assertEquals(header.getVersion(), 1); + Assertions.assertEquals(1, header.getVersion()); } @Test diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java index 6d1cb2ae9c8..3454ccb5fbe 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java @@ -1,13 +1,17 @@ package co.rsk.core; +import com.google.common.collect.Lists; import org.ethereum.core.BlockHeaderExtension; import org.ethereum.core.BlockHeaderExtensionV1; import org.ethereum.core.Bloom; +import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.List; + public class BlockHeaderExtensionTest { @Test public void decodeV1() { @@ -28,4 +32,21 @@ public void decodeV1() { Assertions.assertEquals(extension.getHeaderVersion(), decoded.getHeaderVersion()); Assertions.assertArrayEquals(extension.getHash(), extension.getHash()); } + + @Test + public void invalidDecode() { + byte version = 0; + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + short[] edges = { 1, 2, 3, 4 }; + + BlockHeaderExtension decoded = BlockHeaderExtension.fromEncoded( + new RLPList(RLP.encodeList( + RLP.encodeByte(version), + RLP.encodeElement(logsBloom), + ByteUtil.shortsToRLP(edges) + ), 0) + ); + + Assertions.assertNull(decoded); + } } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java index 4a033579fe8..38a57d0c6cd 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java @@ -419,7 +419,7 @@ public void encodeForLogsBloomField() { Assertions.assertEquals(0x1, logsBloomField[0]); Assertions.assertArrayEquals(header.getExtension().getHash(), Arrays.copyOfRange(logsBloomField, 1, 33)); for (byte b:Arrays.copyOfRange(logsBloomField, 33, Bloom.BLOOM_BYTES)) Assertions.assertEquals(0x0, b); - Assertions.assertEquals(logsBloomField.length, Bloom.BLOOM_BYTES); + Assertions.assertEquals(Bloom.BLOOM_BYTES, logsBloomField.length); } private BlockHeaderV1 createV1FromV0(BlockHeaderV0 headerV0) { diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java index 670702d67bf..b27cb9f0256 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java @@ -77,7 +77,7 @@ void logsBloomFieldEncoded() { byte[] field = RLP.decode2(header.getLogsBloomFieldEncoded()).get(0).getRLPData(); Assertions.assertEquals((byte) 0x1, field[0]); for (int i = 33; i < 256; i++) Assertions.assertEquals((byte) 0x0, field[i]); - Assertions.assertEquals(field.length, Bloom.BLOOM_BYTES); + Assertions.assertEquals(Bloom.BLOOM_BYTES, field.length); } BlockHeaderV1 encodedHeaderWithRandomLogsBloom() { diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index 8ea027c2ac9..3cd9c52e6d6 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -714,6 +714,48 @@ void callGetGasUsedInWithAnInvalidSublistShouldThrowAnError2() { Assertions.assertTrue(true); } } + + @Test + void senderWritesAKeyAndReadsAnotherThatIsWrittenShouldGoToSequential() { + HashSet writeKeyX = createASetAndAddKeys(aWrappedKey); + HashSet writeKeyY = createASetAndAddKeys(aDifferentWrapperKey); + HashSet readKeyY = createASetAndAddKeys(aDifferentWrapperKey); + + Account senderA = new AccountBuilder().name("sender1").build(); + Account senderB = new AccountBuilder().name("sender2").build(); + + Transaction a_writes_x = new TransactionBuilder().nonce(1).sender(senderA).value(BigInteger.valueOf(0)).gasLimit(BigInteger.valueOf(16000)).build(); + Transaction b_writes_y = new TransactionBuilder().nonce(1).sender(senderB).value(BigInteger.valueOf(0)).gasLimit(BigInteger.valueOf(16000)).build(); + Transaction a_reads_y = new TransactionBuilder().nonce(2).sender(senderA).value(BigInteger.valueOf(0)).gasLimit(BigInteger.valueOf(16000)).build(); + + handler.addTransaction(a_writes_x, new HashSet<>(), writeKeyX, 1000); + handler.addTransaction(b_writes_y, new HashSet<>(), writeKeyY, 1000); + handler.addTransaction(a_reads_y, readKeyY, new HashSet<>(), 1000); + + Assertions.assertArrayEquals(new short[]{ 1, 2 }, handler.getTransactionsPerSublistInOrder()); + } + + @Test + void senderWritesAKeyAndReadsAnotherThatIsWrittenShouldGoToSequentialIfReadingOtherKeys() { + ByteArrayWrapper anotherKey = new ByteArrayWrapper(new byte[]{ 7, 7, 7 }); + HashSet writeKeyX = createASetAndAddKeys(aWrappedKey); + HashSet writeKeyYAndAnother = createASetAndAddKeys(anotherKey, aDifferentWrapperKey); + HashSet readKeyYAndAnother = createASetAndAddKeys(anotherKey, aDifferentWrapperKey); + + Account senderA = new AccountBuilder().name("sender1").build(); + Account senderB = new AccountBuilder().name("sender2").build(); + + Transaction a_writes_x = new TransactionBuilder().nonce(1).sender(senderA).value(BigInteger.valueOf(0)).gasLimit(BigInteger.valueOf(16000)).build(); + Transaction b_writes_y = new TransactionBuilder().nonce(1).sender(senderB).value(BigInteger.valueOf(0)).gasLimit(BigInteger.valueOf(16000)).build(); + Transaction a_reads_y = new TransactionBuilder().nonce(2).sender(senderA).value(BigInteger.valueOf(0)).gasLimit(BigInteger.valueOf(16000)).build(); + + handler.addTransaction(a_writes_x, new HashSet<>(), writeKeyX, 1000); + handler.addTransaction(b_writes_y, new HashSet<>(), writeKeyYAndAnother, 1000); + handler.addTransaction(a_reads_y, readKeyYAndAnother, new HashSet<>(), 1000); + + Assertions.assertArrayEquals(new short[]{ 1, 2 }, handler.getTransactionsPerSublistInOrder()); + } + private HashSet createASetAndAddKeys(ByteArrayWrapper... aKey) { return new HashSet<>(Arrays.asList(aKey)); } From 4306c4413ad3c724e3fb4f31d55f859e429d72e2 Mon Sep 17 00:00:00 2001 From: julianlen Date: Thu, 24 Nov 2022 12:34:06 -0300 Subject: [PATCH 23/89] Fixing addBalance with zero (#1932) * The checking for the parameter value was wrong in the addBalance method, it fixes it * Add tests back Co-authored-by: Ilan --- .../src/main/java/org/ethereum/db/MutableRepository.java | 2 +- rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java index b623d72d49c..42dd7336b25 100644 --- a/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java +++ b/rskj-core/src/main/java/org/ethereum/db/MutableRepository.java @@ -296,7 +296,7 @@ public synchronized Coin getBalance(RskAddress addr) { public synchronized Coin addBalance(RskAddress addr, Coin value) { AccountState account = getAccountStateOrCreateNew(addr); - if (value == Coin.ZERO) { + if (value.equals(Coin.ZERO)) { return account.getBalance(); } diff --git a/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java index a087a05eff4..5e434713064 100644 --- a/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java +++ b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java @@ -147,18 +147,21 @@ void tracksWriteOnAddStorageSameBytes() { assertRepositoryHasSize(1, 1); } + @Test public void tracksReadAndWriteOnAddBalanceOfNonExistent () { repository.addBalance(COW, Coin.valueOf(1)); assertRepositoryHasSize(1, 1); } + @Test public void tracksReadAndWriteOnAddBalanceZeroOfNonExistent () { repository.addBalance(COW, Coin.valueOf(0)); assertRepositoryHasSize(1, 1); } + @Test public void tracksReadAndWriteOnAddBalanceOfExistent () { repository.addBalance(COW, Coin.valueOf(1)); @@ -169,6 +172,7 @@ public void tracksReadAndWriteOnAddBalanceOfExistent () { assertRepositoryHasSize(1, 1); } + @Test public void doesntTrackWriteOnAddBalanceZeroOfExistent () { repository.addBalance(COW, Coin.valueOf(1)); From 016d57cd8e9f08c9b464580bb2675388c5a90ae7 Mon Sep 17 00:00:00 2001 From: Ilan <36084092+ilanolkies@users.noreply.github.com> Date: Wed, 14 Dec 2022 15:46:01 -0300 Subject: [PATCH 24/89] Fix writes after writing to parallel (#1949) --- .../bc/ParallelizeTransactionHandler.java | 3 +- .../bc/ParallelizeTransactionHandlerTest.java | 82 +++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index c8fbf8f5e87..7c41569f8f8 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -123,13 +123,12 @@ private void addNewKeysToMaps(RskAddress sender, TransactionSublist sublist, Set if (sublist.isSequential()) { sublistOfSender.put(sender, sublist); - return; } else { sublistOfSender.putIfAbsent(sender, sublist); } for (ByteArrayWrapper key: newWrittenKeys) { - sublistsHavingWrittenToKey.putIfAbsent(key, sublist); + sublistsHavingWrittenToKey.put(key, sublist); } } diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index 3cd9c52e6d6..fd08a57ef77 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -756,6 +756,88 @@ void senderWritesAKeyAndReadsAnotherThatIsWrittenShouldGoToSequentialIfReadingOt Assertions.assertArrayEquals(new short[]{ 1, 2 }, handler.getTransactionsPerSublistInOrder()); } + @Test + void writeSequentialAfterTwoParallelReadsAndAWriteShouldGoToSequential() { + HashSet setWithX = createASetAndAddKeys(aWrappedKey); + HashSet setWithY = createASetAndAddKeys(aDifferentWrapperKey); + HashSet setWithXAndY = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); + + AccountBuilder accountBuilder = new AccountBuilder(); + + // read X + handler.addTransaction( + new TransactionBuilder().sender(accountBuilder.name("sender1").build()).build(), + setWithX, new HashSet<>(), 10000 + ); + + // read Y + handler.addTransaction( + new TransactionBuilder().sender(accountBuilder.name("sender2").build()).build(), + setWithY, new HashSet<>(), 10000 + ); + + // write X and Y + handler.addTransaction( + new TransactionBuilder().sender(accountBuilder.name("sender3").build()).build(), + new HashSet<>(), setWithXAndY, 10000 + ); + + // last write of X is in sequential + Assertions.assertArrayEquals(new short[]{ 1, 2 }, handler.getTransactionsPerSublistInOrder()); + + // write X + handler.addTransaction( + new TransactionBuilder().sender(accountBuilder.name("sender4").build()).build(), + new HashSet<>(), setWithX, 10000 + ); + + // should go to sequential + // [[read x], [read y]] [write x and y, write x] + Assertions.assertArrayEquals(new short[]{ 1, 2 }, handler.getTransactionsPerSublistInOrder()); + } + + @Test + void writeAfterWriteToSequentialForOutOfGasShouldGoToSequential() { + HashSet setWithX = createASetAndAddKeys(aWrappedKey); + + AccountBuilder accountBuilder = new AccountBuilder(); + + ParallelizeTransactionHandler handler = new ParallelizeTransactionHandler((short) 2, 1000); + + // write X with 800 + handler.addTransaction( + new TransactionBuilder() + .sender(accountBuilder.name("sender1").build()) + .gasLimit(BigInteger.valueOf(800)) + .build(), + new HashSet<>(), setWithX, 800 + ); + + // write X with 300 + handler.addTransaction( + new TransactionBuilder() + .sender(accountBuilder.name("sender2").build()) + .gasLimit(BigInteger.valueOf(300)) + .build(), + new HashSet<>(), setWithX, 300 + ); + + // last write of X is in sequential because of out of gas. 800 + 300 > 1000 + Assertions.assertArrayEquals(new short[]{ 1 }, handler.getTransactionsPerSublistInOrder()); + + // read X with 100 + handler.addTransaction( + new TransactionBuilder() + .sender(accountBuilder.name("sender3").build()) + .gasLimit(BigInteger.valueOf(100)) + .build(), + setWithX, new HashSet<>(), 100 + ); + + // should go to sequential + Assertions.assertArrayEquals(new short[]{ 1 }, handler.getTransactionsPerSublistInOrder()); + } + private HashSet createASetAndAddKeys(ByteArrayWrapper... aKey) { return new HashSet<>(Arrays.asList(aKey)); } From d8bb35322343e0db7677690fd460ed76cb0b740d Mon Sep 17 00:00:00 2001 From: Ilan <36084092+ilanolkies@users.noreply.github.com> Date: Wed, 22 Feb 2023 12:43:01 -0300 Subject: [PATCH 25/89] RSKIP 144 - Fix state after executing parallel (#1973) * Fix state after executing parallel testSEND_1 still fails * testSEND_1 uses MinerHelper which implements (duplicating) a block excecution * Merged variables remascAdditionalBalance and coinbaseAddBalance in totalPaidFees + deleted commented code and prints * Clean up --------- Co-authored-by: Julian Len --- .../rsk/core/TransactionExecutorFactory.java | 19 +- .../co/rsk/core/TransactionListExecutor.java | 7 +- .../java/co/rsk/core/bc/BlockExecutor.java | 166 ++++++++---------- .../ethereum/core/TransactionExecutor.java | 25 ++- .../parallel/ParallelExecutionStateTest.java | 94 ++++++++++ .../rsk/test/dsl/BlockBuildDslProcessor.java | 9 +- .../test/dsltest/WorldDslProcessorTest.java | 6 +- .../src/test/java/co/rsk/vm/MinerHelper.java | 10 +- 8 files changed, 223 insertions(+), 113 deletions(-) create mode 100644 rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java b/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java index fb500c9afcd..1878ca81616 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java @@ -76,7 +76,8 @@ public TransactionExecutor newInstance( long totalGasUsed, boolean vmTrace, int vmTraceOptions, - Set deletedAccounts) { + Set deletedAccounts, + boolean postponeFeePayment) { // Tracing configuration is scattered across different files (VM, DetailedProgramTrace, etc.) and // TransactionExecutor#extractTrace doesn't work when called independently. // It would be great to decouple from VmConfig#vmTrace, but sadly that's a major refactor we can't do now. @@ -109,7 +110,21 @@ public TransactionExecutor newInstance( config.isRemascEnabled(), precompiledContracts, deletedAccounts, - blockTxSignatureCache + blockTxSignatureCache, + postponeFeePayment ); } + + public TransactionExecutor newInstance( + Transaction tx, + int txindex, + RskAddress coinbase, + Repository track, + Block block, + long totalGasUsed, + boolean vmTrace, + int vmTraceOptions, + Set deletedAccounts) { + return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, vmTrace, vmTraceOptions, deletedAccounts, false); + } } diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index e49da9efb5b..46c7ff5f570 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -94,12 +94,15 @@ public Boolean call() { totalGasUsed, vmTrace, vmTraceOptions, - deletedAccounts + deletedAccounts, + true ); boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { - if (discardIfInvalid(tx, numberOfTransactions, isRemascTransaction)) return false; + if (discardIfInvalid(tx, numberOfTransactions, isRemascTransaction)) { + return false; + } continue; } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 7f46e7f9a5a..79b225de708 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -263,7 +263,7 @@ public BlockResult executeForMining(Block block, BlockHeader parent, boolean dis if (rskip144Active || (block.getHeader().getTxExecutionSublistsEdges() != null)) { return executeForMiningAfterRSKIP144(block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); } else { - return executePreviousRSKIP144(null, 0, block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); + return executeInternal(null, 0, block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); } } @@ -296,11 +296,15 @@ public BlockResult execute( if (rskip144Active && block.getHeader().getTxExecutionSublistsEdges() != null) { return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); } else { - return executePreviousRSKIP144(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); + return executeInternal(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); } } - private BlockResult executePreviousRSKIP144( + // executes the block before RSKIP 144 + // when RSKIP 144 is active the block is in executed parallel + // miners use executeForMiningAfterRSKIP144 to create the parallel schedule + // new blocks are executed with executeParallel + private BlockResult executeInternal( @Nullable ProgramTraceProcessor programTraceProcessor, int vmTraceOptions, Block block, @@ -339,9 +343,6 @@ private BlockResult executePreviousRSKIP144( int txindex = 0; for (Transaction tx : block.getTransactionsList()) { - - addFeesToRemascIfRemascTx(block, track, totalPaidFees, txindex, tx); - loggingApplyBlockToTx(block, i); TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( @@ -353,8 +354,7 @@ private BlockResult executePreviousRSKIP144( totalGasUsed, vmTrace, vmTraceOptions, - deletedAccounts - ); + deletedAccounts); boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { @@ -366,6 +366,7 @@ private BlockResult executePreviousRSKIP144( } registerExecutedTx(programTraceProcessor, vmTrace, executedTransactions, tx, txExecutor); + long gasUsed = txExecutor.getGasUsed(); totalGasUsed += gasUsed; @@ -402,34 +403,6 @@ private BlockResult executePreviousRSKIP144( return result; } - private void registerExecutedTx(ProgramTraceProcessor programTraceProcessor, boolean vmTrace, List executedTransactions, Transaction tx, TransactionExecutor txExecutor) { - executedTransactions.add(tx); - - if (this.registerProgramResults) { - this.transactionResults.put(tx.getHash(), txExecutor.getResult()); - } - - if (vmTrace) { - txExecutor.extractTrace(programTraceProcessor); - } - - loggingTxExecuted(); - } - - private void addFeesToRemascIfRemascTx(Block block, Repository track, Coin totalPaidFees, int txindex, Transaction tx) { - if (this.isRemascEnabled() && tx.isRemascTransaction(block.getTransactionsList().size(), txindex)) { - addFeesToRemasc(totalPaidFees, track); - } - } - - private Coin addTotalPaidFees(Coin totalPaidFees, TransactionExecutor txExecutor) { - Coin paidFees = txExecutor.getPaidFees(); - if (paidFees != null) { - totalPaidFees = totalPaidFees.add(paidFees); - } - return totalPaidFees; - } - private BlockResult executeParallel( @Nullable ProgramTraceProcessor programTraceProcessor, int vmTraceOptions, @@ -454,17 +427,19 @@ private BlockResult executeParallel( // of the repository to the state post execution, so it's necessary to get it to // the state prior execution again. Metric metric = profiler.start(Profiler.PROFILING_TYPE.BLOCK_EXECUTE); + + ReadWrittenKeysTracker readWrittenKeysTracker = new ReadWrittenKeysTracker(); + Repository track = repositoryLocator.startTrackingAt(parent, readWrittenKeysTracker); + + maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); + readWrittenKeysTracker.clear(); + ExecutorService executorService = Executors.newFixedThreadPool(Constants.getTransactionExecutionThreads()); ExecutorCompletionService completionService = new ExecutorCompletionService<>(executorService); List transactionListExecutors = new ArrayList<>(); - ReadWrittenKeysTracker aTracker = new ReadWrittenKeysTracker(); - Repository track = repositoryLocator.startTrackingAt(parent, aTracker); - maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); - aTracker.clear(); - int nTasks = 0; - // execute parallel subsets of transactions short start = 0; + for (short end : block.getHeader().getTxExecutionSublistsEdges()) { List sublist = block.getTransactionsList().subList(start, end); TransactionListExecutor txListExecutor = new TransactionListExecutor( @@ -488,12 +463,11 @@ private BlockResult executeParallel( ); completionService.submit(txListExecutor); transactionListExecutors.add(txListExecutor); - nTasks++; start = end; } executorService.shutdown(); - for (int i = 0; i < nTasks; i++) { + for (int i = 0; i < transactionListExecutors.size(); i++) { try { Future success = completionService.take(); if (!Boolean.TRUE.equals(success.get())) { @@ -516,7 +490,7 @@ private BlockResult executeParallel( } // Review collision - if (aTracker.detectCollision()) { + if (readWrittenKeysTracker.detectCollision()) { logger.warn("block: [{}] execution failed", block.getNumber()); profiler.stop(metric); return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; @@ -585,17 +559,6 @@ private BlockResult executeParallel( return result; } - private void addFeesToRemasc(Coin remascFees, Repository track) { - if (remascFees.compareTo(Coin.ZERO) > 0) { - logger.trace("Adding fee to remasc contract account"); - track.addBalance(PrecompiledContracts.REMASC_ADDR, remascFees); - } - } - - private boolean isRemascEnabled() { - return this.remascEnabled; - } - private BlockResult executeForMiningAfterRSKIP144( Block block, BlockHeader parent, @@ -621,26 +584,23 @@ private BlockResult executeForMiningAfterRSKIP144( IReadWrittenKeysTracker readWrittenKeysTracker = new ReadWrittenKeysTracker(); Repository track = repositoryLocator.startTrackingAt(parent, readWrittenKeysTracker); + maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); readWrittenKeysTracker.clear(); + int i = 1; - long gasUsedInBlock = 0; + long totalGasUsed = 0; Coin totalPaidFees = Coin.ZERO; Map receiptsByTx = new HashMap<>(); Set deletedAccounts = new HashSet<>(); - ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) Constants.getTransactionExecutionThreads(), GasCost.toGas(block.getGasLimit())); - int txindex = 0; + ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) Constants.getTransactionExecutionThreads(), GasCost.toGas(block.getGasLimit())); + for (Transaction tx : transactionsList) { loggingApplyBlockToTx(block, i); - int numberOfTransactions = transactionsList.size(); - boolean isRemascTransaction = tx.isRemascTransaction(txindex, numberOfTransactions); - - addFeesToRemascIfRemascTxAndTrack(track, totalPaidFees, isRemascTransaction); - TransactionExecutor txExecutor = transactionExecutorFactory.newInstance( tx, txindex, @@ -650,24 +610,34 @@ private BlockResult executeForMiningAfterRSKIP144( parallelizeTransactionHandler.getGasUsedInSequential(), false, 0, - deletedAccounts + deletedAccounts, + true ); boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { - if (discardIfInvalid(block, discardInvalidTxs, track, totalPaidFees, txindex, tx, numberOfTransactions, isRemascTransaction)) { + if (!discardInvalidTxs) { return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } + + loggingDiscardedBlock(block, tx); txindex++; continue; } - Optional sublistGasAccumulated = calculateSublistGasAccumulated(readWrittenKeysTracker, parallelizeTransactionHandler, tx, isRemascTransaction, txExecutor); + Optional sublistGasAccumulated = calculateSublistGasAccumulated( + readWrittenKeysTracker, + parallelizeTransactionHandler, + tx, + tx.isRemascTransaction(txindex, transactionsList.size()), + txExecutor); if (!acceptInvalidTransactions && !sublistGasAccumulated.isPresent()) { - if (discardIfInvalid(block, discardInvalidTxs, track, totalPaidFees, txindex, tx, numberOfTransactions, isRemascTransaction)) { + if (!discardInvalidTxs) { return getBlockResultAndLogExecutionInterrupted(block, metric, tx); } + + loggingDiscardedBlock(block, tx); txindex++; continue; } @@ -675,11 +645,9 @@ private BlockResult executeForMiningAfterRSKIP144( registerTxExecutedForMiningAfterRSKIP144(readWrittenKeysTracker, tx, txExecutor); long gasUsed = txExecutor.getGasUsed(); - gasUsedInBlock += gasUsed; + totalGasUsed += gasUsed; totalPaidFees = addTotalPaidFees(totalPaidFees, txExecutor); - payToRemascWhenThereIsNoRemascTx(track, totalPaidFees, txindex, numberOfTransactions, isRemascTransaction); - deletedAccounts.addAll(txExecutor.getResult().getDeleteAccounts()); //orElseGet is used for testing only when acceptInvalidTransactions is set. @@ -697,6 +665,13 @@ private BlockResult executeForMiningAfterRSKIP144( loggingTxDone(); } + if (totalPaidFees.compareTo(Coin.ZERO) > 0) { + if (remascEnabled) { + track.addBalance(PrecompiledContracts.REMASC_ADDR, totalPaidFees); + } else { + track.addBalance(block.getCoinbase(), totalPaidFees); + } + } saveOrCommitTrackState(saveState, track); @@ -710,7 +685,7 @@ private BlockResult executeForMiningAfterRSKIP144( executedTransactions, receipts, sublistOrder, - gasUsedInBlock, + totalGasUsed, totalPaidFees, track.getTrie() ); @@ -719,6 +694,28 @@ private BlockResult executeForMiningAfterRSKIP144( return result; } + private void registerExecutedTx(ProgramTraceProcessor programTraceProcessor, boolean vmTrace, List executedTransactions, Transaction tx, TransactionExecutor txExecutor) { + executedTransactions.add(tx); + + if (this.registerProgramResults) { + this.transactionResults.put(tx.getHash(), txExecutor.getResult()); + } + + if (vmTrace) { + txExecutor.extractTrace(programTraceProcessor); + } + + loggingTxExecuted(); + } + + private Coin addTotalPaidFees(Coin totalPaidFees, TransactionExecutor txExecutor) { + Coin paidFees = txExecutor.getPaidFees(); + if (paidFees != null) { + totalPaidFees = totalPaidFees.add(paidFees); + } + return totalPaidFees; + } + private void registerTxExecutedForMiningAfterRSKIP144(IReadWrittenKeysTracker readWrittenKeysTracker, Transaction tx, TransactionExecutor txExecutor) { readWrittenKeysTracker.clear(); @@ -738,17 +735,6 @@ private List getTransactionReceipts(Map calculateSublistGasAccumulated(IReadWrittenKeysTracker readWrittenKeysTracker, ParallelizeTransactionHandler parallelizeTransactionHandler, Transaction tx, boolean isRemascTransaction, TransactionExecutor txExecutor) { Optional sublistGasAccumulated; if (isRemascTransaction) { @@ -759,12 +745,6 @@ private Optional calculateSublistGasAccumulated(IReadWrittenKeysTracker re return sublistGasAccumulated; } - private void addFeesToRemascIfRemascTxAndTrack(Repository track, Coin totalPaidFees, boolean isRemascTransaction) { - if (this.isRemascEnabled() && isRemascTransaction) { - addFeesToRemasc(totalPaidFees, track); - } - } - private void saveOrCommitTrackState(boolean saveState, Repository track) { logger.trace("End txs executions."); if (saveState) { @@ -778,14 +758,6 @@ private void saveOrCommitTrackState(boolean saveState, Repository track) { } } - // This method is used just when a block does not contain REMASC as last transaction. It happens only for testing. - private void payToRemascWhenThereIsNoRemascTx(Repository track, Coin totalPaidFees, int txindex, int numberOfTransactions, boolean isRemascTransaction) { - boolean isLastTx = txindex == numberOfTransactions - 1; - if (this.remascEnabled && isLastTx && !isRemascTransaction) { - addFeesToRemasc(totalPaidFees, track); - } - } - private TransactionReceipt buildTransactionReceipt(Transaction tx, TransactionExecutor txExecutor, long gasUsed, long cumulativeGas) { TransactionReceipt receipt = new TransactionReceipt(); receipt.setGasUsed(gasUsed); diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index 7eeb0682975..aa85ce9aa64 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -100,12 +100,27 @@ public class TransactionExecutor { private boolean localCall = false; + private boolean postponeFeePayment; + public TransactionExecutor( Constants constants, ActivationConfig activationConfig, Transaction tx, int txindex, RskAddress coinbase, Repository track, BlockStore blockStore, ReceiptStore receiptStore, BlockFactory blockFactory, ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsedInTheBlock, VmConfig vmConfig, boolean remascEnabled, PrecompiledContracts precompiledContracts, Set deletedAccounts, SignatureCache signatureCache) { + this(constants, activationConfig, tx, txindex, coinbase, + track, blockStore, receiptStore, blockFactory, + programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, + remascEnabled, precompiledContracts, deletedAccounts, + signatureCache, false); + } + + public TransactionExecutor( + Constants constants, ActivationConfig activationConfig, Transaction tx, int txindex, RskAddress coinbase, + Repository track, BlockStore blockStore, ReceiptStore receiptStore, BlockFactory blockFactory, + ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsedInTheBlock, VmConfig vmConfig, + boolean remascEnabled, PrecompiledContracts precompiledContracts, Set deletedAccounts, + SignatureCache signatureCache, boolean postponeFeePayment) { this.constants = constants; this.signatureCache = signatureCache; this.activations = activationConfig.forBlock(executionBlock.getNumber()); @@ -124,6 +139,7 @@ public TransactionExecutor( this.precompiledContracts = precompiledContracts; this.enableRemasc = remascEnabled; this.deletedAccounts = new HashSet<>(deletedAccounts); + this.postponeFeePayment = postponeFeePayment; } /** @@ -527,8 +543,13 @@ private void finalization() { Coin summaryFee = summary.getFee(); //TODO: REMOVE THIS WHEN THE LocalBLockTests starts working with REMASC - if (!enableRemasc) { - track.addBalance(coinbase, summaryFee); + if (!postponeFeePayment) { + if (enableRemasc) { + logger.trace("Adding fee to remasc contract account"); + track.addBalance(PrecompiledContracts.REMASC_ADDR, summaryFee); + } else { + track.addBalance(coinbase, summaryFee); + } } this.paidFees = summaryFee; diff --git a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java new file mode 100644 index 00000000000..beabd62bf23 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java @@ -0,0 +1,94 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package co.rsk.core.parallel; + +import co.rsk.config.TestSystemProperties; +import co.rsk.test.World; +import co.rsk.test.dsl.DslParser; +import co.rsk.test.dsl.DslProcessorException; +import co.rsk.test.dsl.WorldDslProcessor; +import com.typesafe.config.ConfigValueFactory; +import org.ethereum.datasource.HashMapDB; +import org.ethereum.db.ReceiptStore; +import org.ethereum.db.ReceiptStoreImpl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.Reader; +import java.io.StringReader; + +class ParallelExecutionStateTest { + private World createWorld(String dsl, int rskip144) throws DslProcessorException { + ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); + TestSystemProperties config = new TestSystemProperties(rawConfig -> + rawConfig.withValue("blockchain.config.consensusRules.rskip144", ConfigValueFactory.fromAnyRef(rskip144)) + ); + + World world = new World(receiptStore, config); + + DslParser parser = new DslParser(dsl); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + return world; + } + + private byte[] getStateRoot (World world) { + return world.getBlockChain().getBestBlock().getHeader().getStateRoot(); + } + + private void testProcessingBothAndAssertStateRootEquals (String dsl) throws DslProcessorException { + World parallel = this.createWorld(dsl, 0); + World series = this.createWorld(dsl, -1); + + Assertions.assertArrayEquals( + this.getStateRoot(series), + this.getStateRoot(parallel) + ); + } + + @Test + void empty() throws DslProcessorException { + this.testProcessingBothAndAssertStateRootEquals("block_chain g00"); + } + + @Test + void oneTx() throws DslProcessorException { + this.testProcessingBothAndAssertStateRootEquals("account_new acc1 10000000\n" + + "account_new acc2 0\n" + + "\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiver acc2\n" + + " value 1000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_balance acc2 1000\n" + + "assert_tx_success tx01\n" + + "\n"); + } +} \ No newline at end of file diff --git a/rskj-core/src/test/java/co/rsk/test/dsl/BlockBuildDslProcessor.java b/rskj-core/src/test/java/co/rsk/test/dsl/BlockBuildDslProcessor.java index aac2768e3fd..666c8c53c1b 100644 --- a/rskj-core/src/test/java/co/rsk/test/dsl/BlockBuildDslProcessor.java +++ b/rskj-core/src/test/java/co/rsk/test/dsl/BlockBuildDslProcessor.java @@ -18,6 +18,7 @@ package co.rsk.test.dsl; +import co.rsk.blockchain.utils.BlockGenerator; import co.rsk.test.World; import co.rsk.test.builders.BlockBuilder; import org.ethereum.core.Block; @@ -34,11 +35,15 @@ public class BlockBuildDslProcessor { private World world; private String name; - private BlockBuilder builder = new BlockBuilder(null, null, null); + private BlockBuilder builder; public BlockBuildDslProcessor(World world, String name) { this.world = world; this.name = name; + + this.builder = new BlockBuilder(world.getBlockChain(), world.getBridgeSupportFactory(), + world.getBlockStore(), new BlockGenerator(world.getConfig().getNetworkConstants(), world.getConfig().getActivationConfig()) + ).trieStore(world.getTrieStore()); } public void processCommands(DslParser parser) throws DslProcessorException { @@ -55,7 +60,7 @@ private void processCommand(DslCommand cmd) throws DslProcessorException { else if (cmd.isCommand("gasLimit")) this.builder.gasLimit(BigInteger.valueOf(Long.parseLong(cmd.getArgument(0)))); else if (cmd.isCommand("build")) { - Block block = this.builder.build(); + Block block = this.builder.build(this.world.getConfig()); this.world.saveBlock(this.name, block); } else if (cmd.isCommand("uncles")) { diff --git a/rskj-core/src/test/java/co/rsk/test/dsltest/WorldDslProcessorTest.java b/rskj-core/src/test/java/co/rsk/test/dsltest/WorldDslProcessorTest.java index f2b3d4a5c5f..0239814ffff 100644 --- a/rskj-core/src/test/java/co/rsk/test/dsltest/WorldDslProcessorTest.java +++ b/rskj-core/src/test/java/co/rsk/test/dsltest/WorldDslProcessorTest.java @@ -520,9 +520,9 @@ void processBlockBuildCommandWithTransactions() throws DslProcessorException { WorldDslProcessor processor = new WorldDslProcessor(world); - DslParser parser = new DslParser("account_new acc1\naccount_new acc2\n" + - "transaction_build tx01\nsender acc1\nreceiver acc2\nvalue 1000\nbuild\n" + - "transaction_build tx02\nsender acc1\nreceiver acc2\nvalue 1000\nbuild\n" + + DslParser parser = new DslParser("account_new acc1 100000000000\naccount_new acc2 100000000000\n" + + "transaction_build tx01\nsender acc1\nreceiver acc2\nvalue 1000\nnonce 0\nbuild\n" + + "transaction_build tx02\nsender acc1\nreceiver acc2\nvalue 1000\nnonce 1\nbuild\n" + "block_build b01\nparent g00\ntransactions tx01 tx02\nbuild\n"); processor.processCommands(parser); diff --git a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java index ea4bd3dcc4a..b8d64fb4483 100644 --- a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java +++ b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java @@ -103,13 +103,13 @@ public void processBlock( Block block, Block parent) { * This method is a helper of the test "testSEND_1". It is replicating the * behavior of BlockExecutor but slightly different. In the BlockExecutor, the * fees are sent to the Remasc address once all the transactions are executed. - * Since the only test using this helper processes one transaction, so the loop has no sense. + * Since the only test using this helper processes one transaction, so the loop makes no sense. * */ - boolean isRemascTx = tx.isRemascTransaction(txindex, block.getTransactionsList().size()); + /*boolean isRemascTx = tx.isRemascTransaction(txindex, block.getTransactionsList().size()); if (config.isRemascEnabled() && isRemascTx && totalPaidFees.compareTo(Coin.ZERO) > 0) { track.addBalance(PrecompiledContracts.REMASC_ADDR, totalPaidFees); - } + }*/ TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory( config, @@ -128,10 +128,10 @@ public void processBlock( Block block, Block parent) { totalGasUsed += gasUsed; totalPaidFees = totalPaidFees.add(paidFees); - boolean isLastTx = txindex == block.getTransactionsList().size(); + /*boolean isLastTx = txindex == block.getTransactionsList().size(); if (config.isRemascEnabled() && isLastTx && !isRemascTx && totalPaidFees.compareTo(Coin.ZERO) > 0) { track.addBalance(PrecompiledContracts.REMASC_ADDR, totalPaidFees); - } + }*/ track.commit(); From a690e36cae6d51dbd79668870444ee65cd1d94b5 Mon Sep 17 00:00:00 2001 From: Ilan <36084092+ilanolkies@users.noreply.github.com> Date: Wed, 1 Mar 2023 15:21:21 -0300 Subject: [PATCH 26/89] RSKIP 144 tests (#1983) * Add key tracking tests * Add sublist selection (2 tx) * Add sublist selection (3 tx) * Add key tracking tests with proxy contract * Rename methods * Fix activation config * Renames --- .../parallel/ParallelExecutionStateTest.java | 751 +++++++++++++++++- 1 file changed, 739 insertions(+), 12 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java index beabd62bf23..862934e4375 100644 --- a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java +++ b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java @@ -19,6 +19,7 @@ package co.rsk.core.parallel; import co.rsk.config.TestSystemProperties; +import co.rsk.core.RskAddress; import co.rsk.test.World; import co.rsk.test.dsl.DslParser; import co.rsk.test.dsl.DslProcessorException; @@ -27,20 +28,26 @@ import org.ethereum.datasource.HashMapDB; import org.ethereum.db.ReceiptStore; import org.ethereum.db.ReceiptStoreImpl; +import org.ethereum.core.Transaction; +import org.ethereum.vm.GasCost; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.io.Reader; -import java.io.StringReader; +import java.util.List; class ParallelExecutionStateTest { - private World createWorld(String dsl, int rskip144) throws DslProcessorException { + private World createWorld(int rskip144) { ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); TestSystemProperties config = new TestSystemProperties(rawConfig -> rawConfig.withValue("blockchain.config.consensusRules.rskip144", ConfigValueFactory.fromAnyRef(rskip144)) ); World world = new World(receiptStore, config); + return world; + } + + private World createWorldAndProcess(String dsl, int rskip144) throws DslProcessorException { + World world = createWorld(rskip144); DslParser parser = new DslParser(dsl); WorldDslProcessor processor = new WorldDslProcessor(world); @@ -49,28 +56,271 @@ private World createWorld(String dsl, int rskip144) throws DslProcessorException return world; } - private byte[] getStateRoot (World world) { - return world.getBlockChain().getBestBlock().getHeader().getStateRoot(); - } + /** + * compares the state root of a blockchain with and without rskip 144 + * @param dsl the dsl string of the blockchain + * @param expectedEdges the tx execution edges to assert equals + * @throws DslProcessorException + */ + private void compareStateRootPreAndPostRSKIP144AndTestEdges(String dsl, short[] expectedEdges) throws DslProcessorException { + World parallel = this.createWorldAndProcess(dsl, 0); + World series = this.createWorldAndProcess(dsl, -1); - private void testProcessingBothAndAssertStateRootEquals (String dsl) throws DslProcessorException { - World parallel = this.createWorld(dsl, 0); - World series = this.createWorld(dsl, -1); + compareTwoWorldsAndTestEdges(series, parallel, expectedEdges); + } + private void compareTwoWorldsAndTestEdges(World series, World parallel, short[] expectedEdges) { Assertions.assertArrayEquals( this.getStateRoot(series), this.getStateRoot(parallel) ); + + Assertions.assertArrayEquals(expectedEdges, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + private byte[] getStateRoot (World world) { + return world.getBlockChain().getBestBlock().getHeader().getStateRoot(); + } + + /** + * // SPDX-License-Identifier: UNLICENSED + * pragma solidity ^0.8.9; + * + * contract ReadWrite { + * uint x; + * uint another; + * + * receive() external payable {} + * function read() external { another = x; } + * function write(uint value) external { x = value; } + * function update(uint increment) external { x += increment; } + * + * function readWithRevert() external { another = x; revert(); } + * function writeWithRevert(uint value) external { x = value; revert(); } + * function updateWithRevert(uint increment) external { x += increment; revert(); } + * + * mapping (uint => uint) r; + * + * function wasteGas(uint gas, uint writeToX, uint writeToY) external { + * uint i = uint(keccak256(abi.encode(0x12349876))); + * r[writeToX] = i; + * r[writeToY] = i; + * uint gasLeft = gasleft(); + * while (gasLeft < gas + gasleft()) { + * unchecked { + * i = (i / 7 + 10) * 8; + * } + * } + * } + * } + * + * contract Proxy { + * ReadWrite readWrite; + * constructor (ReadWrite _readWrite) { readWrite = _readWrite; } + * function read() external { readWrite.read(); } + * function write(uint value) external { readWrite.write(value); } + * function update(uint increment) external { readWrite.update(increment); } + * } + */ + + // creation codes + + private final String creationData = "608060405234801561001057600080fd5b50610473806100206000396000f3fe6080604052600436106100745760003560e01c806334b091931161004e57806334b09193146100e957806357de26a41461011257806382ab890a14610129578063e2033a13146101525761007b565b80630d2a2d8d146100805780631b892f87146100975780632f048afa146100c05761007b565b3661007b57005b600080fd5b34801561008c57600080fd5b5061009561017b565b005b3480156100a357600080fd5b506100be60048036038101906100b991906102bb565b610188565b005b3480156100cc57600080fd5b506100e760048036038101906100e291906102bb565b6101a4565b005b3480156100f557600080fd5b50610110600480360381019061010b91906102e8565b6101ae565b005b34801561011e57600080fd5b5061012761024f565b005b34801561013557600080fd5b50610150600480360381019061014b91906102bb565b61025a565b005b34801561015e57600080fd5b50610179600480360381019061017491906102bb565b610275565b005b6000546001819055600080fd5b80600080828254610199919061036a565b925050819055600080fd5b8060008190555050565b600063123498766040516020016101c591906103f3565b6040516020818303038152906040528051906020012060001c905080600260008581526020019081526020016000208190555080600260008481526020019081526020016000208190555060005a90505b5a85610222919061036a565b811015610248576008600a6007848161023e5761023d61040e565b5b0401029150610216565b5050505050565b600054600181905550565b8060008082825461026b919061036a565b9250508190555050565b806000819055600080fd5b600080fd5b6000819050919050565b61029881610285565b81146102a357600080fd5b50565b6000813590506102b58161028f565b92915050565b6000602082840312156102d1576102d0610280565b5b60006102df848285016102a6565b91505092915050565b60008060006060848603121561030157610300610280565b5b600061030f868287016102a6565b9350506020610320868287016102a6565b9250506040610331868287016102a6565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061037582610285565b915061038083610285565b92508282019050808211156103985761039761033b565b5b92915050565b6000819050919050565b600063ffffffff82169050919050565b6000819050919050565b60006103dd6103d86103d38461039e565b6103b8565b6103a8565b9050919050565b6103ed816103c2565b82525050565b600060208201905061040860008301846103e4565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea264697066735822122081578079990ee4f4eaa55ebeeedcb31b8f178ab346b989e62541a894e60d381164736f6c63430008110033"; + private String getProxyCreationCode (String address) { + return "608060405234801561001057600080fd5b50604051610417380380610417833981810160405281019061003291906100ed565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061011a565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b60006100ba8261009d565b9050919050565b6100ca816100af565b81146100d557600080fd5b50565b6000815190506100e7816100c1565b92915050565b60006020828403121561010357610102610078565b5b6000610111848285016100d8565b91505092915050565b6102ee806101296000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80632f048afa1461004657806357de26a41461006257806382ab890a1461006c575b600080fd5b610060600480360381019061005b9190610261565b610088565b005b61006a610116565b005b61008660048036038101906100819190610261565b610198565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16632f048afa826040518263ffffffff1660e01b81526004016100e1919061029d565b600060405180830381600087803b1580156100fb57600080fd5b505af115801561010f573d6000803e3d6000fd5b5050505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166357de26a46040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561017e57600080fd5b505af1158015610192573d6000803e3d6000fd5b50505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166382ab890a826040518263ffffffff1660e01b81526004016101f1919061029d565b600060405180830381600087803b15801561020b57600080fd5b505af115801561021f573d6000803e3d6000fd5b5050505050565b600080fd5b6000819050919050565b61023e8161022b565b811461024957600080fd5b50565b60008135905061025b81610235565b92915050565b60006020828403121561027757610276610226565b5b60006102858482850161024c565b91505092915050565b6102978161022b565b82525050565b60006020820190506102b2600083018461028e565b9291505056fea264697066735822122034c249e40cf35f03e8970cf8c91dbaf8426d01814edfcb782e7548b32f6d9e7964736f6c63430008110033000000000000000000000000" + + address; + } + + // call data + + // write(10) + private final String writeTen = "2f048afa000000000000000000000000000000000000000000000000000000000000000a"; + // read() + private final String readData = "57de26a4"; + // update(10) + private final String updateByTen = "82ab890a000000000000000000000000000000000000000000000000000000000000000a"; + // writeWithRevert(10) + private final String writeTenWithRevert = "0d2a2d8d"; + // readWithRevert() + private final String readDataWithRevert = "e2033a13000000000000000000000000000000000000000000000000000000000000000a"; + // updateWithRevert(10) + private final String updateByTenWithRevert = "1b892f87000000000000000000000000000000000000000000000000000000000000000a"; + // wasteGas(2000000, 0, 0) + private final String wasteTwoMillionGas = "34b0919300000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + // wasteGas(100000, 0, 0) + private final String wasteHundredThousandGas = "34b0919300000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + // wasteGas(100000, 1, 1) + private final String writeToX = "34b0919300000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"; + // wasteGas(100000, 2, 2) + private final String writeToY = "34b0919300000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002"; + // wasteGas(100000, 1, 2) + private final String writeToXAndY = "34b0919300000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"; + // wasteGas(2000000, 1, 1) + private final String writeToXWastingGas = "34b0919300000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"; + // wasteGas(2000000, 2, 2) + private final String writeToYWastingGas = "34b0919300000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002"; + // wasteGas(2000000, 1, 2) + private final String writeToXAndYWastingGas = "34b0919300000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"; + + // substrings for dsl text + private final String createThreeAccounts = "account_new acc1 10000000\n" + + "account_new acc2 10000000\n" + + "account_new acc3 10000000\n" + + "\n"; + + private final String createContractInBlock01 = + "transaction_build tx01\n" + + " sender acc3\n" + + " receiverAddress 00\n" + + " data " + creationData + "\n" + + " gas 1200000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n"; + + private final String buildBlockWithTwoTxs = "block_build b02\n" + + " parent b01\n" + + " transactions tx02 tx03\n" + + " build\n" + + "\n" + + "block_connect b02\n" + + "\n" + + "assert_best b02\n" + + "\n"; + + private final String validateTxs = "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "assert_tx_success tx03\n" + + "\n"; + + private String skeleton(String txs, boolean validate) { + return createThreeAccounts + + createContractInBlock01 + + txs + + buildBlockWithTwoTxs + + (validate ? validateTxs : ""); + } + + /** + * creates the contract, performs two calls from different accounts + * tests the state root and the tx edges + * @param firstCall call data for the first tx + * @param secondCall call data for the second tx + * @param edges expected tx edges + * @param validate allows to prevent validating txs are successful + * @throws DslProcessorException + */ + private void createContractAndTestCallWith(String firstCall, String secondCall, short[] edges, boolean validate) throws DslProcessorException { + this.compareStateRootPreAndPostRSKIP144AndTestEdges(skeleton( + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + firstCall + "\n" + + " gas 100000\n" + + " build\n" + + "\n" + + "transaction_build tx03\n" + + " sender acc2\n" + + " contract tx01\n" + + " data " + secondCall + "\n" + + " gas 100000\n" + + " build\n" + + "\n", validate), edges); + } + + private void createContractAndTestCall(String firstCall, String secondCall, short[] edges) throws DslProcessorException { + createContractAndTestCallWith(firstCall, secondCall, edges, true); + } + + private void createContractAndTestCallWithRevert(String firstCall, String secondCall, short[] edges) throws DslProcessorException { + createContractAndTestCallWith(firstCall, secondCall, edges, false); + } + + // For tests calling with proxy contract + private World processCallWithContract(String firstCall, String secondCall, int rskip144) throws DslProcessorException { + World world = createWorld(rskip144); + + DslParser parser = new DslParser(createThreeAccounts + + createContractInBlock01 + + "assert_tx_success tx01\n"); + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + String readWriteAddress = world.getTransactionByName("tx01").getContractAddress().toHexString(); + + String createProxy = "transaction_build tx02\n" + + " sender acc3\n" + + " receiverAddress 00\n" + + " data " + getProxyCreationCode(readWriteAddress) + "\n" + + " gas 1200000\n" + + " nonce 1\n" + + " build\n" + + "\n" + + "block_build b02\n" + + " parent b01\n" + + " transactions tx02\n" + + " build\n" + + "\n" + + "block_connect b02\n" + + "assert_tx_success tx02\n" + + "\n"; + + String sendTwoTxs = "transaction_build tx03\n" + + " sender acc1\n" + + " contract tx02\n" + + " data " + firstCall + "\n" + + " gas 100000\n" + + " build\n" + + "\n" + + "transaction_build tx04\n" + + " sender acc2\n" + + " contract tx02\n" + + " data " + secondCall + "\n" + + " gas 100000\n" + + " build\n" + + "\n" + + "block_build b03\n" + + " parent b02\n" + + " transactions tx03 tx04\n" + + " build\n" + + "\n" + + "block_connect b03\n" + + "assert_tx_success tx03 tx04\n"; + + parser = new DslParser(createProxy + sendTwoTxs); + processor.processCommands(parser); + + RskAddress proxyAddress = world.getTransactionByName("tx02").getContractAddress(); + Assertions.assertNotNull(world.getRepository().getCode(proxyAddress)); + + List txList = world.getBlockChain().getBestBlock().getTransactionsList(); + Assertions.assertTrue(proxyAddress.equals(txList.get(0).getReceiveAddress())); + Assertions.assertTrue(proxyAddress.equals(txList.get(0).getReceiveAddress())); + + return world; + } + + private void createContractAndTestCallWithContract(String firstCall, String secondCall, short[] expectedEdges) throws DslProcessorException { + World parallel = processCallWithContract(firstCall, secondCall, 0); + World series = processCallWithContract(firstCall, secondCall, -1); + + compareTwoWorldsAndTestEdges(series, parallel, expectedEdges); } @Test void empty() throws DslProcessorException { - this.testProcessingBothAndAssertStateRootEquals("block_chain g00"); + this.compareStateRootPreAndPostRSKIP144AndTestEdges("block_chain g00", null); } @Test void oneTx() throws DslProcessorException { - this.testProcessingBothAndAssertStateRootEquals("account_new acc1 10000000\n" + + this.compareStateRootPreAndPostRSKIP144AndTestEdges("account_new acc1 10000000\n" + "account_new acc2 0\n" + "\n" + "transaction_build tx01\n" + @@ -87,8 +337,485 @@ void oneTx() throws DslProcessorException { "block_connect b01\n" + "\n" + "assert_best b01\n" + + "assert_tx_success tx01\n" + "assert_balance acc2 1000\n" + + "\n", new short[]{ 1 }); + } + + // 1. A and B have the same sender account + @Test + void sameSender() throws DslProcessorException { + this.compareStateRootPreAndPostRSKIP144AndTestEdges("account_new acc1 10000000\n" + + "account_new acc2 0\n" + + "\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiver acc2\n" + + " value 1000\n" + + " nonce 0\n" + + " build\n" + + "\n" + + "transaction_build tx02\n" + + " sender acc1\n" + + " receiver acc2\n" + + " value 1000\n" + + " nonce 1\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01 tx02\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "assert_balance acc2 2000\n" + + "\n", new short[]{ 2 }); + } + + // 2. A and B transfer value to the same destination account + @Test + void sameRecipient() throws DslProcessorException { + this.compareStateRootPreAndPostRSKIP144AndTestEdges("account_new acc1 10000000\n" + + "account_new acc2 10000000\n" + + "account_new acc3 0\n" + + "\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiver acc3\n" + + " value 1000\n" + + " build\n" + + "\n" + + "transaction_build tx02\n" + + " sender acc2\n" + + " receiver acc3\n" + + " value 1000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01 tx02\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "assert_balance acc3 2000\n" + + "\n", new short[]{ 2 }); + } + + // 3. A and B transfer value to the same smart contract + @Test + void sameContractRecipient() throws DslProcessorException { + this.compareStateRootPreAndPostRSKIP144AndTestEdges(createThreeAccounts + + createContractInBlock01 + + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " value 1000\n" + + " gas 100000\n" + + " build\n" + + "\n" + + "transaction_build tx03\n" + + " sender acc2\n" + + " contract tx01\n" + + " value 1000\n" + + " gas 100000\n" + + " build\n" + + "\n" + + buildBlockWithTwoTxs + validateTxs, new short[]{ 2 }); + } + + // 4. B reads a smart contract variable that A writes + @Test + void readWrite() throws DslProcessorException { + this.createContractAndTestCall(writeTen, readData, new short[]{ 2 }); + } + + @Test + void readWriteWithRevert() throws DslProcessorException { + this.createContractAndTestCallWithRevert(writeTen, readDataWithRevert, new short[]{ 2 }); + } + + @Test + void readWriteWithContract() throws DslProcessorException { + this.createContractAndTestCallWithContract(writeTen, readData, new short[]{ 2 }); + } + + // 5. B reads a smart contract variable that A updates (i.e., +=) + @Test + void readUpdate() throws DslProcessorException { + this.createContractAndTestCall(updateByTen, readData, new short[]{ 2 }); + } + + @Test + void readUpdateWithRevert() throws DslProcessorException { + this.createContractAndTestCallWithRevert(updateByTen, readDataWithRevert, new short[]{ 2 }); + } + + @Test + void readUpdateWithContract() throws DslProcessorException { + this.createContractAndTestCallWithContract(updateByTen, readData, new short[]{ 2 }); + } + + //6. B writes a smart contract variable that A writes + @Test + void writeWrite() throws DslProcessorException { + this.createContractAndTestCall(writeTen, writeTen, new short[]{ 2 }); + } + + @Test + void writeWriteWithRevert() throws DslProcessorException { + this.createContractAndTestCallWithRevert(writeTen, writeTenWithRevert, new short[]{ 2 }); + } + + @Test + void writeWriteWithContract() throws DslProcessorException { + this.createContractAndTestCallWithContract(writeTen, writeTen, new short[]{ 2 }); + } + + // 7. B writes a smart contract variable that A updates + @Test + void writeUpdate() throws DslProcessorException { + this.createContractAndTestCall(updateByTen, writeTen, new short[]{ 2 }); + } + + @Test + void writeUpdateWithRevert() throws DslProcessorException { + this.createContractAndTestCallWithRevert(updateByTen, writeTenWithRevert, new short[]{ 2 }); + } + + @Test + void writeUpdateWithContract() throws DslProcessorException { + this.createContractAndTestCallWithContract(updateByTen, writeTen, new short[]{ 2 }); + } + + // 8. B writes a smart contract variable that A reads + @Test + void writeRead() throws DslProcessorException { + this.createContractAndTestCall(readData, writeTen, new short[]{ 2 }); + } + + @Test + void writeReadWithRevert() throws DslProcessorException { + this.createContractAndTestCallWithRevert(readData, writeTenWithRevert, new short[]{ 2 }); + } + + @Test + void writeReadWithContract() throws DslProcessorException { + this.createContractAndTestCallWithContract(readData, writeTen, new short[]{ 2 }); + } + + // 9. B updates a smart contract variable that A writes + @Test + void updateWrite() throws DslProcessorException { + this.createContractAndTestCall(writeTen, updateByTen, new short[]{ 2 }); + } + + @Test + void updateWriteWithRevert() throws DslProcessorException { + this.createContractAndTestCallWithRevert(writeTen, updateByTenWithRevert, new short[]{ 2 }); + } + + @Test + void updateWriteWithContract() throws DslProcessorException { + this.createContractAndTestCallWithContract(writeTen, updateByTen, new short[]{ 2 }); + } + + // 10. B updates a smart contract variable that A reads + @Test + void updateRead() throws DslProcessorException { + this.createContractAndTestCall(readData, updateByTen, new short[]{ 2 }); + } + + // 11. B updates a smart contract variable that A updates + @Test + void updateUpdate() throws DslProcessorException { + this.createContractAndTestCall(updateByTen, updateByTen, new short[]{ 2 }); + } + + // 12. B calls a smart contract that A creates + @Test + void callCreatedContract() throws DslProcessorException { + this.compareStateRootPreAndPostRSKIP144AndTestEdges("account_new acc1 10000000\n" + + "account_new acc2 10000000\n" + + "\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress 00\n" + + " data " + creationData + "\n" + + " gas 1200000\n" + + " build\n" + + "\n" + + "transaction_build tx02\n" + + " sender acc2\n" + + " contract tx01\n" + + " data " + writeTen + "\n" + + " gas 100000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01 tx02\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "\n", new short[]{ 2 }); + } + + // 13. B transfers value to a smart contract that A creates + @Test + void sendToCreatedContract() throws DslProcessorException { + this.compareStateRootPreAndPostRSKIP144AndTestEdges("account_new acc1 10000000\n" + + "account_new acc2 10000000\n" + + "\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress 00\n" + + " data " + creationData + "\n" + + " gas 1200000\n" + + " build\n" + + "\n" + + "transaction_build tx02\n" + + " sender acc2\n" + + " contract tx01\n" + + " value 10000\n" + + " gas 100000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01 tx02\n" + + " build\n" + + "\n" + + "block_connect b01\n" + "assert_tx_success tx01\n" + - "\n"); + "assert_tx_success tx02\n" + + "assert_balance tx01 10000\n" + + "\n", new short[]{ 2 }); + } + + // 2. A is in a parallel sublist without enough gas available: B is placed in the sequential sublist + @Test + void useSequentialForGas() throws DslProcessorException { + World parallel = this.createWorldAndProcess(skeleton("transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + wasteTwoMillionGas + "\n" + + " gas 2700000\n" + + " build\n" + + "\n" + + "transaction_build tx03\n" + + " sender acc2\n" + + " contract tx01\n" + + " data " + wasteTwoMillionGas + "\n" + + " gas 2500000\n" + + " build\n" + + "\n", true), 0); + + Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[] { 1 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + // 3. A is in the sequential sublist: B is placed in the sequential sublist + @Test + void useSequentialForCollisionWithSequential() throws DslProcessorException { + World parallel = this.createWorldAndProcess(createThreeAccounts + + createContractInBlock01 + + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + wasteTwoMillionGas + "\n" + + " gas 2500000\n" + + " nonce 0\n" + + " build\n" + + "\n" + + "transaction_build tx03\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + wasteTwoMillionGas + "\n" + + " gas 2500000\n" + + " nonce 1\n" + + " build\n" + + "\n" + // goes to sequential + "transaction_build tx04\n" + + " sender acc2\n" + + " contract tx01\n" + + " data " + wasteHundredThousandGas + "\n" + + " gas 200000\n" + + " build\n" + + "\n" + + "block_build b02\n" + + " parent b01\n" + + " transactions tx02 tx03 tx04\n" + + " build\n" + + "\n" + + "block_connect b02\n" + + "\n" + + "assert_best b02\n" + + "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "assert_tx_success tx03\n" + + "assert_tx_success tx04\n" + + "\n", 0); + + Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(3, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[] { 1 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + // 1. A and B are in different parallel sublists with enough gas: C is placed in the sequential sublist + @Test + void useSequentialForCollisionWithTwoParallel() throws DslProcessorException { + World parallel = this.createWorldAndProcess(createThreeAccounts + + "account_new acc4 10000000\n" + + createContractInBlock01 + + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + writeToX + "\n" + + " gas 200000\n" + + " build\n" + + "\n" + + "transaction_build tx03\n" + + " sender acc2\n" + + " contract tx01\n" + + " data " + writeToY + "\n" + + " gas 200000\n" + + " build\n" + + "\n" + + "transaction_build tx04\n" + + " sender acc4\n" + + " contract tx01\n" + + " data " + writeToXAndY + "\n" + + " gas 200000\n" + + " build\n" + + "\n" + + "block_build b02\n" + + " parent b01\n" + + " transactions tx02 tx03 tx04\n" + + " build\n" + + "\n" + + "block_connect b02\n" + + "\n" + + "assert_best b02\n" + + "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "assert_tx_success tx03\n" + + "assert_tx_success tx04\n" + + "\n", 0); + + Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(3, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[] { 1, 2 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + // 2. A and B are in different parallel sublists without enough gas: C is placed in the sequential sublist + @Test + void useSequentialForCollisionWithTwoParallelWithoutGas() throws DslProcessorException { + World parallel = this.createWorldAndProcess(createThreeAccounts + + "account_new acc4 10000000\n" + + createContractInBlock01 + + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + writeToXWastingGas + "\n" + + " gas 2500000\n" + + " build\n" + + "\n" + + "transaction_build tx03\n" + + " sender acc2\n" + + " contract tx01\n" + + " data " + writeToYWastingGas + "\n" + + " gas 2500000\n" + + " build\n" + + "\n" + // goes to sequential + "transaction_build tx04\n" + + " sender acc4\n" + + " contract tx01\n" + + " data " + writeToXAndYWastingGas + "\n" + + " gas 2500000\n" + + " build\n" + + "\n" + + "block_build b02\n" + + " parent b01\n" + + " transactions tx02 tx03 tx04\n" + + " build\n" + + "\n" + + "block_connect b02\n" + + "\n" + + "assert_best b02\n" + + "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "assert_tx_success tx03\n" + + "assert_tx_success tx04\n" + + "\n", 0); + + Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(3, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[] { 1, 2 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + // 3. A is in a parallel sublist and B is in the sequential sublist: C is placed in the sequential sublist + @Test + void useSequentialForCollisionWithSequentialAndParallel() throws DslProcessorException { + World parallel = this.createWorldAndProcess(createThreeAccounts + + "account_new acc4 10000000\n" + + createContractInBlock01 + + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + writeToXWastingGas + "\n" + + " gas 2500000\n" + + " build\n" + + "\n" + + "transaction_build tx03\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + writeToXWastingGas + "\n" + + " gas 2500000\n" + + " nonce 1\n" + + " build\n" + + "\n" + // goes to sequential + "transaction_build tx04\n" + + " sender acc2\n" + + " contract tx01\n" + + " data " + writeToY + "\n" + + " gas 200000\n" + + " build\n" + + "\n" + + "transaction_build tx05\n" + + " sender acc4\n" + + " contract tx01\n" + + " data " + writeToXAndY + "\n" + + " gas 200000\n" + + " build\n" + + "\n" + + "block_build b02\n" + + " parent b01\n" + + " transactions tx02 tx03 tx04 tx05\n" + + " build\n" + + "\n" + + "block_connect b02\n" + + "\n" + + "assert_best b02\n" + + "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "assert_tx_success tx03\n" + + "assert_tx_success tx04\n" + + "assert_tx_success tx05\n" + + "\n", 0); + + Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(4, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[] { 1, 2 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } } \ No newline at end of file From 2c9a6e8d9560a449102db0b763b3ce0ac20b1fc7 Mon Sep 17 00:00:00 2001 From: Ilan <36084092+ilanolkies@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:01:23 -0300 Subject: [PATCH 27/89] RSKIP 351 updates (#1938) * RSKIP 351 updates Make logs bloom field shortest as possible, add version to RLP encoding, express compressed on encode/decode, rename methods and variables, make header v0 read from extensionData * Make compressed header rlp list size remain the same as old header size * Remove unused import * Fix decoding for header with merged mining fields and no tx edges * Refactor canBeDecoded * Refactor BlockHeaderTest variables * Rename logsBloomField in BlockFactory * Make compressed constructor parameter of BlockHeaderV1 * Fix code smells --- .../messages/BlockHeadersResponseMessage.java | 2 +- .../java/co/rsk/net/messages/MessageType.java | 4 +- .../java/org/ethereum/core/BlockFactory.java | 92 +++-- .../java/org/ethereum/core/BlockHeader.java | 23 +- .../org/ethereum/core/BlockHeaderBuilder.java | 4 +- .../ethereum/core/BlockHeaderExtension.java | 2 +- .../ethereum/core/BlockHeaderExtensionV1.java | 31 +- .../java/org/ethereum/core/BlockHeaderV0.java | 25 +- .../java/org/ethereum/core/BlockHeaderV1.java | 55 +-- .../java/co/rsk/core/BlockFactoryTest.java | 321 ++++++++++++++++-- .../co/rsk/core/BlockHeaderExtensionTest.java | 5 +- .../rsk/core/BlockHeaderExtensionV1Test.java | 7 - .../java/co/rsk/core/BlockHeaderTest.java | 155 +++++---- .../java/co/rsk/core/BlockHeaderV0Test.java | 4 +- .../java/co/rsk/core/BlockHeaderV1Test.java | 26 +- .../ethereum/core/BlockHeaderBuilderTest.java | 14 +- 16 files changed, 564 insertions(+), 206 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java b/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java index bfc7ec472cd..d38324cb303 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java @@ -32,7 +32,7 @@ public BlockHeadersResponseMessage(long id, List headers) { @Override protected byte[] getEncodedMessageWithoutId() { byte[][] rlpHeaders = this.blockHeaders.stream() - .map(BlockHeader::getEncodedForHeaderMessage) + .map(BlockHeader::getEncodedCompressed) .toArray(byte[][]::new); return RLP.encodeList(RLP.encodeList(rlpHeaders)); diff --git a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java index e7e602d43d6..17d81035c4a 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java @@ -143,7 +143,7 @@ public Message createMessage(BlockFactory blockFactory, RLPList list) { for (int k = 0; k < rlpHeaders.size(); k++) { RLPElement element = rlpHeaders.get(k); - BlockHeader header = blockFactory.decodeHeader(element.getRLPData()); + BlockHeader header = blockFactory.decodeHeader(element.getRLPData(), true); headers.add(header); } @@ -229,7 +229,7 @@ public Message createMessage(BlockFactory blockFactory, RLPList list) { for (int j = 0; j < rlpUncles.size(); j++) { RLPElement element = rlpUncles.get(j); - uncles.add(blockFactory.decodeHeader(element.getRLPData())); + uncles.add(blockFactory.decodeHeader(element.getRLPData(), false)); } BlockHeaderExtension blockHeaderExtension = message.size() == 3 diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java index d7c8352d011..cd58a3d7f28 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockFactory.java @@ -39,8 +39,8 @@ import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH; public class BlockFactory { - private static final int RLP_HEADER_SIZE = 18; - private static final int RLP_HEADER_SIZE_WITH_MERGED_MINING = 21; + private static final int RLP_HEADER_SIZE = 19; + private static final int RLP_HEADER_SIZE_WITH_MERGED_MINING = 22; private final ActivationConfig activationConfig; @@ -67,7 +67,7 @@ private Block decodeBlock(byte[] rawData, boolean sealed) { } RLPList rlpHeader = (RLPList) block.get(0); - BlockHeader header = decodeHeader(rlpHeader, sealed); + BlockHeader header = decodeHeader(rlpHeader, false, sealed); List transactionList = parseTxs((RLPList) block.get(1)); @@ -77,7 +77,7 @@ private Block decodeBlock(byte[] rawData, boolean sealed) { for (int k = 0; k < uncleHeadersRlp.size(); k++) { RLPElement element = uncleHeadersRlp.get(k); - BlockHeader uncleHeader = decodeHeader((RLPList)element, sealed); + BlockHeader uncleHeader = decodeHeader((RLPList)element, false, sealed); uncleList.add(uncleHeader); } @@ -93,11 +93,11 @@ public Block newBlock(BlockHeader header, List transactionList, Lis return new Block(header, transactionList, uncleList, isRskip126Enabled, sealed); } - public BlockHeader decodeHeader(byte[] encoded) { - return decodeHeader(RLP.decodeList(encoded), true); + public BlockHeader decodeHeader(byte[] encoded, boolean compressed) { + return decodeHeader(RLP.decodeList(encoded), compressed, true); } - private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { + private BlockHeader decodeHeader(RLPList rlpHeader, boolean compressed, boolean sealed) { byte[] parentHash = rlpHeader.get(0).getRLPData(); byte[] unclesHash = rlpHeader.get(1).getRLPData(); byte[] coinBaseBytes = rlpHeader.get(2).getRLPData(); @@ -117,7 +117,7 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { receiptTrieRoot = EMPTY_TRIE_HASH; } - byte[] logsBloom = rlpHeader.get(6).getRLPData(); + byte[] extensionData = rlpHeader.get(6).getRLPData(); // rskip351: logs bloom when decoding extended, list(version, hash(extension)) when compressed byte[] difficultyBytes = rlpHeader.get(7).getRLPData(); BlockDifficulty difficulty = RLP.parseBlockDifficulty(difficultyBytes); @@ -137,7 +137,7 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { byte[] minimumGasPriceBytes = rlpHeader.get(14).getRLPData(); Coin minimumGasPrice = RLP.parseSignedCoinNonNullZero(minimumGasPriceBytes); - if (!canBeDecoded(rlpHeader, blockNumber)) { + if (!canBeDecoded(rlpHeader, blockNumber, compressed)) { throw new IllegalArgumentException(String.format( "Invalid block header size: %d", rlpHeader.size() @@ -154,8 +154,18 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { ummRoot = rlpHeader.get(r++).getRLPRawData(); } + byte version = 0x0; + + if (activationConfig.isActive(ConsensusRule.RSKIP351, blockNumber)) { + version = compressed + ? RLP.decodeList(extensionData).get(0).getRLPData()[0] + : rlpHeader.get(r++).getRLPData()[0]; + } + short[] txExecutionSublistsEdges = null; - if (rlpHeader.size() > r && activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)) { + + if ((!activationConfig.isActive(ConsensusRule.RSKIP351, blockNumber) || !compressed) && + (rlpHeader.size() > r && activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber))) { txExecutionSublistsEdges = ByteUtil.rlpToShorts(rlpHeader.get(r++).getRLPRawData()); } @@ -172,11 +182,25 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { boolean includeForkDetectionData = activationConfig.isActive(ConsensusRule.RSKIP110, blockNumber) && blockNumber >= MiningConfig.REQUIRED_NUMBER_OF_BLOCKS_FOR_FORK_DETECTION_CALCULATION; + return createBlockHeader(compressed, sealed, parentHash, unclesHash, + coinBaseBytes, coinbase, stateRoot, txTrieRoot, receiptTrieRoot, extensionData, + difficultyBytes, difficulty, glBytes, blockNumber, gasUsed, timestamp, extraData, + paidFees, minimumGasPriceBytes, minimumGasPrice, uncleCount, ummRoot, version, txExecutionSublistsEdges, + bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, + useRskip92Encoding, includeForkDetectionData); + } + + private BlockHeader createBlockHeader(boolean compressed, boolean sealed, byte[] parentHash, byte[] unclesHash, + byte[] coinBaseBytes, RskAddress coinbase, byte[] stateRoot, byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] extensionData, + byte[] difficultyBytes, BlockDifficulty difficulty, byte[] glBytes, long blockNumber, long gasUsed, long timestamp, byte[] extraData, + Coin paidFees, byte[] minimumGasPriceBytes, Coin minimumGasPrice, int uncleCount, byte[] ummRoot, byte version, short[] txExecutionSublistsEdges, + byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, + boolean useRskip92Encoding, boolean includeForkDetectionData) { if (blockNumber == Genesis.NUMBER) { return new GenesisHeader( parentHash, unclesHash, - logsBloom, + extensionData, difficultyBytes, blockNumber, glBytes, @@ -192,19 +216,21 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { stateRoot); } - if (activationConfig.getHeaderVersion(blockNumber) == 1) return new BlockHeaderV1( - parentHash, unclesHash, coinbase, stateRoot, - txTrieRoot, receiptTrieRoot, logsBloom, difficulty, - blockNumber, glBytes, gasUsed, timestamp, extraData, - paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, - bitcoinMergedMiningCoinbaseTransaction, new byte[0], - minimumGasPrice, uncleCount, sealed, useRskip92Encoding, includeForkDetectionData, - ummRoot, txExecutionSublistsEdges - ); + if (version == 1) { + return new BlockHeaderV1( + parentHash, unclesHash, coinbase, stateRoot, + txTrieRoot, receiptTrieRoot, extensionData, difficulty, + blockNumber, glBytes, gasUsed, timestamp, extraData, + paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, + bitcoinMergedMiningCoinbaseTransaction, new byte[0], + minimumGasPrice, uncleCount, sealed, useRskip92Encoding, includeForkDetectionData, + ummRoot, txExecutionSublistsEdges, compressed + ); + } return new BlockHeaderV0( parentHash, unclesHash, coinbase, stateRoot, - txTrieRoot, receiptTrieRoot, logsBloom, difficulty, + txTrieRoot, receiptTrieRoot, extensionData, difficulty, blockNumber, glBytes, gasUsed, timestamp, extraData, paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, new byte[0], @@ -213,17 +239,27 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean sealed) { ); } - private boolean canBeDecoded(RLPList rlpHeader, long blockNumber) { + private boolean canBeDecoded(RLPList rlpHeader, long blockNumber, boolean compressed) { int preUmmHeaderSizeAdjustment = activationConfig.isActive(ConsensusRule.RSKIPUMM, blockNumber) ? 0 : 1; int preParallelSizeAdjustment = activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber) ? 0 : 1; - int expectedSize = RLP_HEADER_SIZE - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment; - int expectedSizeMM = RLP_HEADER_SIZE_WITH_MERGED_MINING - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment; + int preRSKIP351SizeAdjustment = getRSKIP351SizeAdjustment(blockNumber, compressed, preParallelSizeAdjustment); + + int expectedSize = RLP_HEADER_SIZE - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment - preRSKIP351SizeAdjustment; + int expectedSizeMM = RLP_HEADER_SIZE_WITH_MERGED_MINING - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment - preRSKIP351SizeAdjustment; - int preHeaderExtensionAdjustment = activationConfig.isActive(ConsensusRule.RSKIP351, blockNumber) ? 1 : 0; + return rlpHeader.size() == expectedSize || rlpHeader.size() == expectedSizeMM; + } + + private int getRSKIP351SizeAdjustment(long blockNumber, boolean compressed, int preParallelSizeAdjustment) { + if (!activationConfig.isActive(ConsensusRule.RSKIP351, blockNumber)) { + return 1; // remove version + } + + if (compressed) { + return 2 - preParallelSizeAdjustment; // remove version and edges if existent + } - return rlpHeader.size() == expectedSize || rlpHeader.size() == expectedSizeMM || - rlpHeader.size() == expectedSize - preHeaderExtensionAdjustment || - rlpHeader.size() == expectedSizeMM - preHeaderExtensionAdjustment; + return 0; } private static BigInteger parseBigInteger(byte[] bytes) { diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index bea5356d9fa..f2b6ca90407 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -50,15 +50,17 @@ public abstract class BlockHeader { public abstract BlockHeaderExtension getExtension(); public abstract void setExtension(BlockHeaderExtension extension); + // contains the logs bloom or the hash of the extension depending on version + public byte[] getExtensionData() { return this.extensionData; } + // fields from block header extension public abstract byte[] getLogsBloom(); public abstract void setLogsBloom(byte[] logsBloom); public abstract short[] getTxExecutionSublistsEdges(); // Edges of the transaction execution lists public abstract void setTxExecutionSublistsEdges(short[] edges); - // encoding to use in logs bloom field on header response message - public abstract byte[] getLogsBloomFieldEncoded(); - public abstract void addExtraFieldsToEncoded(boolean useExtensionEncoding, List fieldsToEncode); + // called after encoding the header, used to add elements at the end + public abstract void addExtraFieldsToEncodedHeader(boolean usingCompressedEncoding, List fieldsToEncode); private static final int HASH_FOR_MERGED_MINING_PREFIX_LENGTH = 20; private static final int FORK_DETECTION_DATA_LENGTH = 12; @@ -118,6 +120,8 @@ public abstract class BlockHeader { private final byte[] ummRoot; + protected byte[] extensionData; + /** * The mgp for a tx to be included in the block. */ @@ -137,7 +141,7 @@ public abstract class BlockHeader { private final boolean includeForkDetectionData; protected BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, - byte[] txTrieRoot, byte[] receiptTrieRoot, BlockDifficulty difficulty, + byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] extensionData, BlockDifficulty difficulty, long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, @@ -148,6 +152,7 @@ protected BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, this.coinbase = coinbase; this.stateRoot = stateRoot; this.txTrieRoot = txTrieRoot; + this.extensionData = extensionData; this.receiptTrieRoot = receiptTrieRoot; this.difficulty = difficulty; this.number = number; @@ -318,7 +323,7 @@ public Keccak256 getHash() { public byte[] getFullEncoded() { return this.getEncoded(true, true, false); } // the encoded block header used for calculating block hashes including RSKIP92 public byte[] getEncoded() { return this.getEncoded(true, !useRskip92Encoding, false); } - public byte[] getEncodedForHeaderMessage() { return this.getEncoded(true, true, true); } + public byte[] getEncodedCompressed() { return this.getEncoded(true, true, true); } public byte[] getEncodedForHash() { return this.getEncoded(true, !useRskip92Encoding, true); } @Nullable @@ -326,7 +331,7 @@ public Coin getMinimumGasPrice() { return this.minimumGasPrice; } - public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProofAndCoinbase, boolean useExtensionEncoding) { + public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProofAndCoinbase, boolean compressed) { byte[] parentHash = RLP.encodeElement(this.parentHash); byte[] unclesHash = RLP.encodeElement(this.unclesHash); @@ -346,7 +351,7 @@ public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProof byte[] receiptTrieRoot = RLP.encodeElement(this.receiptTrieRoot); - byte[] logsBloom = useExtensionEncoding ? this.getLogsBloomFieldEncoded() : RLP.encodeElement(this.getLogsBloom()); + byte[] logsBloomField = RLP.encodeElement(compressed ? this.getExtensionData() : this.getLogsBloom()); byte[] difficulty = encodeBlockDifficulty(this.difficulty); byte[] number = RLP.encodeBigInteger(BigInteger.valueOf(this.number)); byte[] gasLimit = RLP.encodeElement(this.gasLimit); @@ -356,7 +361,7 @@ public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProof byte[] paidFees = RLP.encodeCoin(this.paidFees); byte[] mgp = RLP.encodeSignedCoinNonNullZero(this.minimumGasPrice); List fieldToEncodeList = Lists.newArrayList(parentHash, unclesHash, coinbase, - stateRoot, txTrieRoot, receiptTrieRoot, logsBloom, difficulty, number, + stateRoot, txTrieRoot, receiptTrieRoot, logsBloomField, difficulty, number, gasLimit, gasUsed, timestamp, extraData, paidFees, mgp); byte[] uncleCount = RLP.encodeBigInteger(BigInteger.valueOf(this.uncleCount)); @@ -366,7 +371,7 @@ public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProof fieldToEncodeList.add(RLP.encodeElement(this.ummRoot)); } - this.addExtraFieldsToEncoded(useExtensionEncoding, fieldToEncodeList); + this.addExtraFieldsToEncodedHeader(compressed, fieldToEncodeList); if (withMergedMiningFields && hasMiningFields()) { byte[] bitcoinMergedMiningHeader = RLP.encodeElement(this.bitcoinMergedMiningHeader); diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java index 8af5b55def8..51496403adf 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java @@ -333,7 +333,7 @@ public BlockHeader build() { } } - if (createParallelCompliantHeader && txExecutionSublistsEdges == null) { + if (activationConfig.isActive(ConsensusRule.RSKIP144, number) && createParallelCompliantHeader && txExecutionSublistsEdges == null) { txExecutionSublistsEdges = new short[0]; } @@ -348,7 +348,7 @@ public BlockHeader build() { mergedMiningForkDetectionData, minimumGasPrice, uncleCount, false, useRskip92Encoding, - includeForkDetectionData, ummRoot, txExecutionSublistsEdges + includeForkDetectionData, ummRoot, txExecutionSublistsEdges, false ); return new BlockHeaderV0( diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java index db154461919..3ef30bb9af9 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java @@ -3,8 +3,8 @@ import org.ethereum.util.RLPList; public interface BlockHeaderExtension { - byte getHeaderVersion(); byte[] getEncoded(); + byte[] getHash(); static BlockHeaderExtension fromEncoded(RLPList encoded) { byte version = encoded.get(0).getRLPData()[0]; diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java index 7cce383f03f..8706156338a 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java @@ -10,9 +10,6 @@ import java.util.List; public class BlockHeaderExtensionV1 implements BlockHeaderExtension { - @Override - public byte getHeaderVersion() { return 0x1; } - private byte[] logsBloom; private short[] txExecutionSublistsEdges; @@ -20,35 +17,43 @@ public class BlockHeaderExtensionV1 implements BlockHeaderExtension { public void setLogsBloom(byte[] logsBloom) { this.logsBloom = logsBloom; } public short[] getTxExecutionSublistsEdges() { return this.txExecutionSublistsEdges != null ? Arrays.copyOf(this.txExecutionSublistsEdges, this.txExecutionSublistsEdges.length) : null; } - - public void setTxExecutionSublistsEdges(short[] edges) { - this.txExecutionSublistsEdges = edges != null? Arrays.copyOf(edges, edges.length) : null; - } + public void setTxExecutionSublistsEdges(short[] edges) { this.txExecutionSublistsEdges = edges != null? Arrays.copyOf(edges, edges.length) : null; } public BlockHeaderExtensionV1(byte[] logsBloom, short[] edges) { this.logsBloom = logsBloom; this.txExecutionSublistsEdges = edges != null ? Arrays.copyOf(edges, edges.length) : null; } + @Override public byte[] getHash() { - return HashUtil.keccak256(this.getEncoded()); + return HashUtil.keccak256(this.getEncodedForHash()); } - @Override - public byte[] getEncoded() { - List fieldToEncodeList = Lists.newArrayList(RLP.encodeByte(this.getHeaderVersion()), RLP.encodeElement(this.getLogsBloom())); + private void addEdgesEncoded(List fieldToEncodeList) { short[] txExecutionSublistsEdges = this.getTxExecutionSublistsEdges(); if (txExecutionSublistsEdges != null) { fieldToEncodeList.add(ByteUtil.shortsToRLP(this.getTxExecutionSublistsEdges())); } + } + + private byte[] getEncodedForHash() { + List fieldToEncodeList = Lists.newArrayList(RLP.encodeElement(HashUtil.keccak256(this.getLogsBloom()))); + this.addEdgesEncoded(fieldToEncodeList); + return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); + } + + @Override + public byte[] getEncoded() { + List fieldToEncodeList = Lists.newArrayList(RLP.encodeElement(this.getLogsBloom())); + this.addEdgesEncoded(fieldToEncodeList); return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); } public static BlockHeaderExtensionV1 fromEncoded(byte[] encoded) { RLPList rlpExtension = RLP.decodeList(encoded); return new BlockHeaderExtensionV1( - rlpExtension.get(1).getRLPData(), - rlpExtension.size() == 3 ? ByteUtil.rlpToShorts(rlpExtension.get(2).getRLPData()): null + rlpExtension.get(0).getRLPData(), + rlpExtension.size() == 2 ? ByteUtil.rlpToShorts(rlpExtension.get(1).getRLPData()): null ); } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java index d172b632b7a..54e1c2e9105 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java @@ -37,7 +37,6 @@ public void setExtension(BlockHeaderExtension extension) { // block header v0 has no extension } - private byte[] logsBloom; private short[] txExecutionSublistsEdges; public BlockHeaderV0(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, @@ -48,41 +47,43 @@ public BlockHeaderV0(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, Coin minimumGasPrice, int uncleCount, boolean sealed, boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot, short[] txExecutionSublistsEdges) { super(parentHash,unclesHash, coinbase, stateRoot, - txTrieRoot, receiptTrieRoot, difficulty, + txTrieRoot, receiptTrieRoot, logsBloom, difficulty, number, gasLimit, gasUsed, timestamp, extraData, paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, mergedMiningForkDetectionData, minimumGasPrice, uncleCount, sealed, useRskip92Encoding, includeForkDetectionData, ummRoot); - this.logsBloom = logsBloom; this.txExecutionSublistsEdges = txExecutionSublistsEdges != null ? Arrays.copyOf(txExecutionSublistsEdges, txExecutionSublistsEdges.length) : null; } + // logs bloom is stored in the extension data + @Override + public byte[] getLogsBloom() { return extensionData; } @Override - public byte[] getLogsBloom() { return logsBloom; } - public void setLogsBloom(byte[] logsBloom) { if (this.sealed) { throw new SealedBlockHeaderException("trying to alter logs bloom"); } this.hash = null; - this.logsBloom = logsBloom; + this.extensionData = logsBloom; } + @Override public short[] getTxExecutionSublistsEdges() { return this.txExecutionSublistsEdges != null ? Arrays.copyOf(this.txExecutionSublistsEdges, this.txExecutionSublistsEdges.length) : null; } + @Override public void setTxExecutionSublistsEdges(short[] edges) { this.txExecutionSublistsEdges = edges != null? Arrays.copyOf(edges, edges.length) : null; } @Override - public byte[] getLogsBloomFieldEncoded() { - return RLP.encodeElement(this.logsBloom); - } - - @Override - public void addExtraFieldsToEncoded(boolean useExtensionEncoding, List fieldsToEncode) { + public void addExtraFieldsToEncodedHeader(boolean usingCompressedEncoding, List fieldsToEncode) { + // adding edges to + // 1. keep RSKIP 351 and RSKIP 144 independent + // either can be activated at any height independently + // 2. keep compressed encoding the same as uncompressed + // since this difference should not exist on v0 this.addTxExecutionSublistsEdgesIfAny(fieldsToEncode); } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java index 408006ddba5..2f395bfbc06 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java @@ -3,6 +3,7 @@ import co.rsk.core.BlockDifficulty; import co.rsk.core.Coin; import co.rsk.core.RskAddress; +import com.google.common.annotations.VisibleForTesting; import org.ethereum.util.RLP; import java.util.Arrays; @@ -13,40 +14,58 @@ public class BlockHeaderV1 extends BlockHeader { public byte getVersion() { return 0x1; } private BlockHeaderExtensionV1 extension; + @Override public BlockHeaderExtensionV1 getExtension() { return this.extension; } + @Override public void setExtension(BlockHeaderExtension extension) { this.extension = (BlockHeaderExtensionV1) extension; } public BlockHeaderV1(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, - byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] logsBloom, BlockDifficulty difficulty, + byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] extensionData, BlockDifficulty difficulty, long number, byte[] gasLimit, long gasUsed, long timestamp, byte[] extraData, Coin paidFees, byte[] bitcoinMergedMiningHeader, byte[] bitcoinMergedMiningMerkleProof, byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] mergedMiningForkDetectionData, Coin minimumGasPrice, int uncleCount, boolean sealed, - boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot, short[] txExecutionSublistsEdges) { + boolean useRskip92Encoding, boolean includeForkDetectionData, byte[] ummRoot, short[] txExecutionSublistsEdges, boolean compressed) { super(parentHash,unclesHash, coinbase, stateRoot, - txTrieRoot, receiptTrieRoot, difficulty, + txTrieRoot, receiptTrieRoot, compressed ? extensionData : null, difficulty, number, gasLimit, gasUsed, timestamp, extraData, paidFees, bitcoinMergedMiningHeader, bitcoinMergedMiningMerkleProof, bitcoinMergedMiningCoinbaseTransaction, mergedMiningForkDetectionData, minimumGasPrice, uncleCount, sealed, useRskip92Encoding, includeForkDetectionData, ummRoot); + this.extension = compressed + ? new BlockHeaderExtensionV1(null, null) + : new BlockHeaderExtensionV1(extensionData, txExecutionSublistsEdges); + if(!compressed) { + this.updateExtensionData(); // update after calculating + } - this.extension = new BlockHeaderExtensionV1(logsBloom, txExecutionSublistsEdges); } - @Override - public byte[] getLogsBloom() { - return this.extension.getLogsBloom(); + @VisibleForTesting + public static byte[] createExtensionData(byte[] extensionHash) { + return RLP.encodeList( + RLP.encodeByte((byte) 0x1), + RLP.encodeElement(extensionHash) + ); } + private void updateExtensionData() { + this.extensionData = BlockHeaderV1.createExtensionData(this.extension.getHash()); + } + + @Override + public byte[] getLogsBloom() { return this.extension.getLogsBloom(); } + + @Override public void setLogsBloom(byte[] logsBloom) { - /* A sealed block header is immutable, cannot be changed */ if (this.sealed) { throw new SealedBlockHeaderException("trying to alter logs bloom"); } this.hash = null; this.extension.setLogsBloom(logsBloom); + this.updateExtensionData(); } @Override @@ -54,30 +73,20 @@ public void setLogsBloom(byte[] logsBloom) { @Override public void setTxExecutionSublistsEdges(short[] edges) { - /* A sealed block header is immutable, cannot be changed */ if (this.sealed) { - throw new SealedBlockHeaderException("trying to alter logs bloom"); + throw new SealedBlockHeaderException("trying to alter edges"); } this.hash = null; this.extension.setTxExecutionSublistsEdges(edges != null ? Arrays.copyOf(edges, edges.length) : null); + this.updateExtensionData(); } @Override - public byte[] getLogsBloomFieldEncoded() { - byte[] ecnoded = new byte[Bloom.BLOOM_BYTES]; - ecnoded[0] = 0x1; - byte[] hash = this.extension.getHash(); - System.arraycopy(hash, 0, ecnoded, 1, hash.length); - return RLP.encodeElement(ecnoded); - } - - @Override - public void addExtraFieldsToEncoded(boolean useExtensionEncoding, List fieldsToEncode) { - if (!useExtensionEncoding) { + public void addExtraFieldsToEncodedHeader(boolean usingCompressedEncoding, List fieldsToEncode) { + if (!usingCompressedEncoding) { + fieldsToEncode.add(RLP.encodeByte(this.getVersion())); this.addTxExecutionSublistsEdgesIfAny(fieldsToEncode); } } - - } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java index 00adc54fc44..5d128d7df09 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java @@ -26,6 +26,8 @@ import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.BlockFactory; import org.ethereum.core.BlockHeader; +import org.ethereum.core.BlockHeaderV1; +import org.ethereum.core.Bloom; import org.ethereum.crypto.HashUtil; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; @@ -86,7 +88,7 @@ void decodeBlockPriorToHeight449AndRskip110On() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(19)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getMiningForkDetectionData(), is(decodedHeader.getMiningForkDetectionData())); } @@ -102,7 +104,7 @@ void decodeBlockPriorToHeight449AndRskip110Off() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(19)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getMiningForkDetectionData(), is(decodedHeader.getMiningForkDetectionData())); } @@ -118,7 +120,7 @@ void decodeBlockAfterHeight449AndRskip110OFF() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(19)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getMiningForkDetectionData(), is(decodedHeader.getMiningForkDetectionData())); } @@ -131,7 +133,8 @@ void decodeBlockAfterHeight449AndRskip110On() { BlockHeader header = createBlockHeaderWithMergedMiningFields(number, forkDetectionData, null, null); - byte[] encodedBlock = header.getEncoded(false, false, false); + boolean compressed = false; + byte[] encodedBlock = header.getEncoded(false, false, compressed); byte[] hashForMergedMining = Arrays.copyOfRange(HashUtil.keccak256(encodedBlock), 0, 20); byte[] coinbase = org.bouncycastle.util.Arrays.concatenate(hashForMergedMining, forkDetectionData); coinbase = org.bouncycastle.util.Arrays.concatenate(RskMiningConstants.RSK_TAG, coinbase); @@ -142,7 +145,7 @@ void decodeBlockAfterHeight449AndRskip110On() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(19)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, compressed); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getMiningForkDetectionData(), is(decodedHeader.getMiningForkDetectionData())); } @@ -158,11 +161,12 @@ void decodeWithNoMergedMiningDataAndRskip110OffAndNoForkDetectionData() { BlockHeader header = createBlockHeader(number, new byte[0], null, null); - byte[] encodedHeader = header.getEncoded(false, false, false); + boolean compressed = false; + byte[] encodedHeader = header.getEncoded(false, false, compressed); RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(16)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, compressed); assertThat(header.getHash(), is(decodedHeader.getHash())); } @@ -179,11 +183,12 @@ void decodeWithNoMergedMiningDataAndRskip110OffAndForkDetectionData() { byte[] forkDetectionData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; BlockHeader header = createBlockHeader(number, forkDetectionData, null, null); - byte[] encodedHeader = header.getEncoded(false, false, false); + boolean compressed = false; + byte[] encodedHeader = header.getEncoded(false, false, compressed); RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(16)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, compressed); assertThat(header.getHash(), is(decodedHeader.getHash())); } @@ -199,7 +204,7 @@ void decodeBlockRskip110OffRskipUMMOnAndNoMergedMiningFieldsValidUMMRoot() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(17)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getUmmRoot(), is(decodedHeader.getUmmRoot())); @@ -216,7 +221,7 @@ void decodeBlockRskip110OffRskipUMMOnAndNoMergedMiningFieldsEmptyUMMRoot() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(17)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getUmmRoot(), is(decodedHeader.getUmmRoot())); @@ -235,7 +240,7 @@ void decodeBlockRskip110OffRskipUMMOnAndNoMergedMiningFieldsNullUMMRoot() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(16)); - Assertions.assertThrows(IllegalArgumentException.class, () -> factory.decodeHeader(encodedHeader)); + Assertions.assertThrows(IllegalArgumentException.class, () -> factory.decodeHeader(encodedHeader, false)); } @Test @@ -250,7 +255,7 @@ void decodeBlockRskip110OffRskipUMMOnAndMergedMiningFieldsValidUMMRoot() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(20)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getUmmRoot(), is(decodedHeader.getUmmRoot())); @@ -267,7 +272,7 @@ void decodeBlockRskip110OffRskipUMMOnAndMergedMiningFieldsEmptyUmmRoot() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(20)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getUmmRoot(), is(decodedHeader.getUmmRoot())); @@ -286,16 +291,16 @@ void decodeBlockRskip110OffRskipUMMOnAndMergedMiningFieldsNullUmmRoot() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(19)); - Assertions.assertThrows(IllegalArgumentException.class, () -> factory.decodeHeader(encodedHeader)); + Assertions.assertThrows(IllegalArgumentException.class, () -> factory.decodeHeader(encodedHeader, false)); } @Test - public void genesisHasVersion0() { + void genesisHasVersion0() { Assertions.assertEquals((byte) 0x0, factory.decodeBlock(genesisRaw()).getHeader().getVersion()); } @Test - public void headerIsVersion0BeforeActivation () { + void headerIsVersion0Before351Activation () { long number = 20L; enableRskip351At(number); BlockHeader header = factory.getBlockHeaderBuilder().setNumber(number - 1).build(); @@ -303,15 +308,268 @@ public void headerIsVersion0BeforeActivation () { } @Test - public void headerIsVersion1AfterActivation () { + void headerIsVersion1After351Activation () { long number = 20L; enableRskip351At(number); BlockHeader header = factory.getBlockHeaderBuilder().setNumber(number).build(); Assertions.assertEquals(1, header.getVersion()); } + private BlockHeader testRSKIP351FullHeaderEncoding(byte[] encoded, byte expectedVersion, byte[] expectedLogsBloom, short[] expectedEdges) { + return testRSKIP351CompressedHeaderEncoding(encoded, expectedVersion, expectedLogsBloom, expectedEdges, false); + } + + private BlockHeader testRSKIP351CompressedHeaderEncoding(byte[] encoded, byte expectedVersion, byte[] expectedLogsBloom, short[] expectedEdges) { + return testRSKIP351CompressedHeaderEncoding(encoded, expectedVersion, expectedLogsBloom, expectedEdges, true); + } + + private BlockHeader testRSKIP351CompressedHeaderEncoding(byte[] encoded, byte expectedVersion, byte[] expectedLogsBloom, short[] expectedEdges, boolean compressed) { + BlockHeader decodedHeader = factory.decodeHeader(encoded, compressed); + + Assertions.assertEquals(expectedVersion, decodedHeader.getVersion()); + Assertions.assertArrayEquals(expectedLogsBloom, decodedHeader.getLogsBloom()); + Assertions.assertArrayEquals(expectedEdges, decodedHeader.getTxExecutionSublistsEdges()); + + return decodedHeader; + } + + @Test + void decodeCompressedBefore351() { + long number = 20L; + enableRulesAt(number, RSKIP144); + enableRskip351At(number); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 1; + logsBloom[1] = 1; + logsBloom[2] = 1; + logsBloom[3] = 1; + + BlockHeader header = factory.getBlockHeaderBuilder() + .setLogsBloom(logsBloom) + .setNumber(number - 1) + .build(); + + byte[] encoded = header.getEncodedCompressed(); + + BlockHeader decodedHeader = testRSKIP351CompressedHeaderEncoding(encoded, (byte) 0, logsBloom, null); + Assertions.assertArrayEquals(logsBloom, decodedHeader.getExtensionData()); + } + + @Test + void decodeCompressedBefore351WithEdges() { + long number = 20L; + long blockNumber = number - 1; + enableRulesAt(blockNumber, RSKIP144); + enableRskip351At(number); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 1; + logsBloom[1] = 1; + logsBloom[2] = 1; + logsBloom[3] = 1; + + short[] edges = { 1, 2, 3, 4 }; + + BlockHeader header = factory.getBlockHeaderBuilder() + .setLogsBloom(logsBloom) + .setTxExecutionSublistsEdges(edges) + .setNumber(blockNumber) + .build(); + + byte[] encoded = header.getEncodedCompressed(); + + BlockHeader decodedHeader = testRSKIP351CompressedHeaderEncoding(encoded, (byte) 0, logsBloom, edges); + Assertions.assertArrayEquals(logsBloom, decodedHeader.getExtensionData()); + } + + @Test + void decodeCompressedWithNoEdgesAndMergedMiningFields() { + long blockNumber = 20L; + enableRulesAt(blockNumber, RSKIP144, RSKIP92, RSKIPUMM); + enableRskip351At(blockNumber); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 1; + logsBloom[1] = 1; + logsBloom[2] = 1; + logsBloom[3] = 1; + + BlockHeader header = createBlockHeaderWithMergedMiningFields(blockNumber, new byte[0], new byte[0], null, logsBloom); + + byte[] encoded = header.getEncodedCompressed(); + + BlockHeader decodedHeader = testRSKIP351CompressedHeaderEncoding(encoded, (byte) 1, null, null); + + Assertions.assertArrayEquals(header.getExtensionData(), decodedHeader.getExtensionData()); + assertThat(header.getHash(), is(decodedHeader.getHash())); + assertThat(header.getUmmRoot(), is(decodedHeader.getUmmRoot())); + } + /** + * note on decodeCompressedOfExtendedBefore351 & + * decodeCompressedOfExtendedBefore351WithEdges: + * while nodes activate hf, the new nodes will decode + * blocks headers v0 with compressed=true when old nodes send + * block headers encoded in the old fashion. in consequence, + * decode(compressed=true) needs to handle encoded(compress=false) + * for blocks v0 + */ + + @Test + void decodeCompressedOfExtendedBefore351() { + long number = 20L; + enableRulesAt(number, RSKIP144); + enableRskip351At(number); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 1; + logsBloom[1] = 1; + logsBloom[2] = 1; + logsBloom[3] = 1; + + BlockHeader header = factory.getBlockHeaderBuilder() + .setLogsBloom(logsBloom) + .setNumber(number - 1) + .build(); + + byte[] encoded = header.getFullEncoded(); // used before hf + + BlockHeader decodedHeader = testRSKIP351CompressedHeaderEncoding(encoded, (byte) 0, logsBloom, null); + Assertions.assertArrayEquals(logsBloom, decodedHeader.getExtensionData()); + } + + @Test + void decodeCompressedOfExtendedBefore351WithEdges() { + long number = 20L; + long blockNumber = number - 1; + enableRulesAt(blockNumber, RSKIP144); + enableRskip351At(number); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 1; + logsBloom[1] = 1; + logsBloom[2] = 1; + logsBloom[3] = 1; + + short[] edges = { 1, 2, 3, 4 }; + + BlockHeader header = factory.getBlockHeaderBuilder() + .setLogsBloom(logsBloom) + .setTxExecutionSublistsEdges(edges) + .setNumber(blockNumber) + .build(); + + byte[] encoded = header.getFullEncoded(); // used before hf + + BlockHeader decodedHeader = testRSKIP351CompressedHeaderEncoding(encoded, (byte) 0, logsBloom, edges); + Assertions.assertArrayEquals(logsBloom, decodedHeader.getExtensionData()); + } + + @Test + void decodeCompressedAfter351WithEdges() { + long blockNumber = 20L; + enableRulesAt(blockNumber, RSKIP144); + enableRskip351At(blockNumber); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 1; + logsBloom[1] = 1; + logsBloom[2] = 1; + logsBloom[3] = 1; + + short[] edges = { 1, 2, 3, 4 }; + + BlockHeader header = factory.getBlockHeaderBuilder() + .setLogsBloom(logsBloom) + .setTxExecutionSublistsEdges(edges) + .setNumber(blockNumber) + .build(); + + byte[] encoded = header.getEncodedCompressed(); + + BlockHeader decodedHeader = testRSKIP351CompressedHeaderEncoding(encoded, (byte) 1, null, null); + Assertions.assertArrayEquals(BlockHeaderV1.createExtensionData(header.getExtension().getHash()), decodedHeader.getExtensionData()); + } + + @Test + void decodeFullBefore351And144() { + long number = 20L; + long blockNumber = number - 1; + enableRulesAt(number, RSKIP144); + enableRskip351At(number); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 1; + logsBloom[1] = 1; + logsBloom[2] = 1; + logsBloom[3] = 1; + + BlockHeader header = factory.getBlockHeaderBuilder() + .setLogsBloom(logsBloom) + .setNumber(blockNumber) + .build(); + + byte[] encoded = header.getFullEncoded(); + + BlockHeader decodedHeader = testRSKIP351FullHeaderEncoding(encoded, (byte) 0, logsBloom, null); + Assertions.assertArrayEquals(header.getLogsBloom(), decodedHeader.getExtensionData()); + } + @Test - public void decodeBlockRskip144OnRskipUMMOnAndMergedMiningFields() { + void decodeFullBefore351WithEdges() { + long number = 20L; + long blockNumber = number - 1; + enableRulesAt(blockNumber, RSKIP144); + enableRskip351At(number); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 1; + logsBloom[1] = 1; + logsBloom[2] = 1; + logsBloom[3] = 1; + + short[] edges = { 1, 2, 3, 4 }; + + BlockHeader header = factory.getBlockHeaderBuilder() + .setLogsBloom(logsBloom) + .setTxExecutionSublistsEdges(edges) + .setNumber(blockNumber) + .build(); + + byte[] encoded = header.getFullEncoded(); + + BlockHeader decodedHeader = testRSKIP351FullHeaderEncoding(encoded, (byte) 0, logsBloom, edges); + Assertions.assertArrayEquals(header.getLogsBloom(), decodedHeader.getExtensionData()); + } + + @Test + void decodeFullAfter351WithEdges() { + long blockNumber = 20L; + enableRulesAt(blockNumber, RSKIP144); + enableRskip351At(blockNumber); + + byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + logsBloom[0] = 1; + logsBloom[1] = 1; + logsBloom[2] = 1; + logsBloom[3] = 1; + + short[] edges = { 1, 2, 3, 4 }; + + BlockHeader header = factory.getBlockHeaderBuilder() + .setLogsBloom(logsBloom) + .setTxExecutionSublistsEdges(edges) + .setNumber(blockNumber) + .build(); + + byte[] encoded = header.getFullEncoded(); + + BlockHeader decodedHeader = testRSKIP351FullHeaderEncoding(encoded, (byte) 1, logsBloom, edges); + Assertions.assertArrayEquals(BlockHeaderV1.createExtensionData(header.getExtension().getHash()), decodedHeader.getExtensionData()); + } + + @Test + void decodeBlockRskip144OnRskipUMMOnAndMergedMiningFields() { long number = 500L; enableRulesAt(number, RSKIPUMM, RSKIP144); short[] edges = TestUtils.randomShortArray(4); @@ -322,14 +580,14 @@ public void decodeBlockRskip144OnRskipUMMOnAndMergedMiningFields() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(21)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getTxExecutionSublistsEdges(), is(edges)); } @Test - public void decodeBlockRskip144OnRskipUMMOnAndNoMergedMiningFields() { + void decodeBlockRskip144OnRskipUMMOnAndNoMergedMiningFields() { long number = 500L; enableRulesAt(number, RSKIPUMM, RSKIP144); short[] edges = TestUtils.randomShortArray(4); @@ -340,14 +598,14 @@ public void decodeBlockRskip144OnRskipUMMOnAndNoMergedMiningFields() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(18)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getTxExecutionSublistsEdges(), is(edges)); } @Test - public void decodeBlockRskip144OnRskipUMMOffAndMergedMiningFields() { + void decodeBlockRskip144OnRskipUMMOffAndMergedMiningFields() { long number = 500L; enableRulesAt(number, RSKIP144); short[] edges = TestUtils.randomShortArray(4); @@ -358,14 +616,14 @@ public void decodeBlockRskip144OnRskipUMMOffAndMergedMiningFields() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(20)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getTxExecutionSublistsEdges(), is(edges)); } @Test - public void decodeBlockRskip144OnRskipUMMOffAndNoMergedMiningFields() { + void decodeBlockRskip144OnRskipUMMOffAndNoMergedMiningFields() { long number = 500L; enableRulesAt(number, RSKIP144); short[] edges = TestUtils.randomShortArray(4); @@ -376,7 +634,7 @@ public void decodeBlockRskip144OnRskipUMMOffAndNoMergedMiningFields() { RLPList headerRLP = RLP.decodeList(encodedHeader); assertThat(headerRLP.size(), is(17)); - BlockHeader decodedHeader = factory.decodeHeader(encodedHeader); + BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false); assertThat(header.getHash(), is(decodedHeader.getHash())); assertThat(header.getTxExecutionSublistsEdges(), is(edges)); @@ -399,6 +657,15 @@ private BlockHeader createBlockHeaderWithMergedMiningFields( byte[] forkDetectionData, byte[] ummRoot, short[] edges) { + return createBlockHeaderWithMergedMiningFields(number, forkDetectionData, ummRoot, edges, null); + } + + private BlockHeader createBlockHeaderWithMergedMiningFields( + long number, + byte[] forkDetectionData, + byte[] ummRoot, + short[] edges, + byte[] logsBloom) { byte[] difficulty = BigInteger.ONE.toByteArray(); byte[] gasLimit = BigInteger.valueOf(6800000).toByteArray(); long timestamp = 7731067; // Friday, 10 May 2019 6:04:05 @@ -426,6 +693,7 @@ private BlockHeader createBlockHeaderWithMergedMiningFields( .setCreateUmmCompliantHeader(ummRoot != null) .setUmmRoot(ummRoot) .setTxExecutionSublistsEdges(edges) + .setLogsBloom(logsBloom) .build(); } @@ -461,7 +729,6 @@ private BlockHeader createBlockHeader( .build(); } - private static byte[] genesisRawHash() { return Hex.decode("cabb7fbe88cd6d922042a32ffc08ce8b1fbb37d650b9d4e7dbfe2a7469adfa42"); } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java index 3454ccb5fbe..a2bb623b188 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java @@ -10,8 +10,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.List; - public class BlockHeaderExtensionTest { @Test public void decodeV1() { @@ -29,8 +27,7 @@ public void decodeV1() { RLP.decodeList(extension.getEncoded()) ); - Assertions.assertEquals(extension.getHeaderVersion(), decoded.getHeaderVersion()); - Assertions.assertArrayEquals(extension.getHash(), extension.getHash()); + Assertions.assertArrayEquals(extension.getHash(), decoded.getHash()); } @Test diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java index 835503781ed..c4d3b8c7055 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java @@ -1,7 +1,5 @@ package co.rsk.core; -import org.ethereum.TestUtils; -import org.ethereum.core.BlockHeaderExtension; import org.ethereum.core.BlockHeaderExtensionV1; import org.ethereum.core.Bloom; import org.junit.jupiter.api.Assertions; @@ -11,11 +9,6 @@ public class BlockHeaderExtensionV1Test { private static short[] EDGES = new short[] { 1, 2, 3, 4 }; - @Test - public void hasVersion1 () { - BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(TestUtils.randomBytes(256), EDGES); - Assertions.assertEquals(1, extension.getHeaderVersion()); - } @Test public void createWithLogsBloomAndEdges() { diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java index 38a57d0c6cd..a03c9930f13 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderTest.java @@ -29,6 +29,7 @@ import org.ethereum.util.RLPList; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; @@ -396,30 +397,50 @@ private void testHeaderVersion(byte version) { } @Test - public void getVersion0() { this.testHeaderVersion((byte) 0x0); } + void getVersion0() { this.testHeaderVersion((byte) 0x0); } @Test - public void getVersion1() { + void getVersion1() { this.testHeaderVersion((byte) 0x1); } - @Test - public void encodeForLogsBloomField() { - byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + private static byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; + private static short[] edges = new short[]{ 1, 2, 3, 4 }; + + @BeforeAll + static void setupLogsBloom() { logsBloom[0] = 0x01; logsBloom[1] = 0x02; logsBloom[2] = 0x03; logsBloom[3] = 0x04; + } + @Test + void encodeForLogsBloomField() { BlockHeaderV1 header = (BlockHeaderV1) createBlockHeaderWithVersion((byte) 0x1); header.setLogsBloom(logsBloom); + header.setTxExecutionSublistsEdges(edges); - byte[] logsBloomField = RLP.decode2(header.getLogsBloomFieldEncoded()).get(0).getRLPData(); + RLPList extensionDataRLP = RLP.decodeList(header.getExtensionData()); + byte version = extensionDataRLP.get(0).getRLPData()[0]; + byte[] extensionHash = extensionDataRLP.get(1).getRLPData(); - Assertions.assertEquals(0x1, logsBloomField[0]); - Assertions.assertArrayEquals(header.getExtension().getHash(), Arrays.copyOfRange(logsBloomField, 1, 33)); - for (byte b:Arrays.copyOfRange(logsBloomField, 33, Bloom.BLOOM_BYTES)) Assertions.assertEquals(0x0, b); - Assertions.assertEquals(Bloom.BLOOM_BYTES, logsBloomField.length); + Assertions.assertEquals((byte) 0x1, version); + Assertions.assertArrayEquals(header.getExtension().getHash(), extensionHash); + } + + @Test + void encodeForLogsBloomFieldIsNotLogsBloomSize() { + // this test is added to assert the blockchain is still serializable + // it is still possible to serialize wether logs bloom field is the + // actual logs bloom or the extension data by reading its length != 256 + BlockHeaderV1 header = (BlockHeaderV1) createBlockHeaderWithVersion((byte) 0x1); + header.setLogsBloom(logsBloom); + header.setTxExecutionSublistsEdges(edges); + + byte[] extensionData = header.getExtensionData(); + + Assertions.assertNotEquals(Bloom.BLOOM_BYTES, extensionData.length); } private BlockHeaderV1 createV1FromV0(BlockHeaderV0 headerV0) { @@ -430,90 +451,95 @@ private BlockHeaderV1 createV1FromV0(BlockHeaderV0 headerV0) { headerV0.getPaidFees(), headerV0.getBitcoinMergedMiningHeader(), headerV0.getBitcoinMergedMiningMerkleProof(), headerV0.getBitcoinMergedMiningCoinbaseTransaction(), headerV0.getMiningForkDetectionData(), headerV0.getMinimumGasPrice(), headerV0.getUncleCount(), headerV0.isSealed(), - false, false, headerV0.getUmmRoot(), headerV0.getTxExecutionSublistsEdges() + false, false, headerV0.getUmmRoot(), headerV0.getTxExecutionSublistsEdges(), false ); } - @Test - public void encodedV0IsTheSameForV0andV1 () { - byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; - logsBloom[0] = 0x01; - logsBloom[1] = 0x02; - logsBloom[2] = 0x03; - logsBloom[3] = 0x04; + private void testEncodingButVersion(BlockHeaderV0 headerV0, BlockHeaderV1 headerV1) { + RLPList rlpHeaderV0 = RLP.decodeList(headerV0.getEncoded()); + RLPList rlpHeaderV1 = RLP.decodeList(headerV1.getEncoded()); + + for (int i = 0; i < rlpHeaderV0.size(); i++) { + // jump version field + Assertions.assertArrayEquals(rlpHeaderV0.get(i).getRLPData(), rlpHeaderV1.get(i > 15 ? i + 1 : i).getRLPData()); + } + } + @Test + void encodedV0IsTheSameForV0andV1 () { BlockHeaderV0 headerV0 = (BlockHeaderV0) createBlockHeaderWithVersion((byte) 0x0); headerV0.setLogsBloom(logsBloom); BlockHeaderV1 headerV1 = createV1FromV0(headerV0); - Assertions.assertArrayEquals(headerV0.getEncoded(), headerV1.getEncoded()); + testEncodingButVersion(headerV0, headerV1); } @Test - public void fullEncodedV0IsTheSameForV0andV1 () { - byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; - logsBloom[0] = 0x01; - logsBloom[1] = 0x02; - logsBloom[2] = 0x03; - logsBloom[3] = 0x04; - + void fullEncodedV0IsTheSameForV0andV1 () { BlockHeaderV0 headerV0 = (BlockHeaderV0) createBlockHeaderWithVersion((byte) 0x0); headerV0.setLogsBloom(logsBloom); BlockHeaderV1 headerV1 = createV1FromV0(headerV0); - Assertions.assertArrayEquals(headerV0.getFullEncoded(), headerV1.getFullEncoded()); + testEncodingButVersion(headerV0, headerV1); } @Test - public void fullEncodedV0IsTheSameAsEncodedForHeaderMessage () { - byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; - logsBloom[0] = 0x01; - logsBloom[1] = 0x02; - logsBloom[2] = 0x03; - logsBloom[3] = 0x04; - + void fullEncodedV0IsTheSameAsEncodedForHeaderMessage () { BlockHeaderV0 headerV0 = (BlockHeaderV0) createBlockHeaderWithVersion((byte) 0x0); headerV0.setLogsBloom(logsBloom); - Assertions.assertArrayEquals(headerV0.getFullEncoded(), headerV0.getEncodedForHeaderMessage()); + Assertions.assertArrayEquals(headerV0.getFullEncoded(), headerV0.getEncodedCompressed()); } @Test - public void fullEncodedV1IsTheSameAsEncodedForHeaderMessageButLogsBloomAndEdges () { - byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; - logsBloom[0] = 0x01; - logsBloom[1] = 0x02; - logsBloom[2] = 0x03; - logsBloom[3] = 0x04; - + void fullEncodedV1IsTheSameAsCompressedButLogsBloomEdgesAndVersion () { + // this test is added to assert that there were no changes in the rest of the elements BlockHeaderV1 headerV1 = (BlockHeaderV1) createBlockHeaderWithVersion((byte) 0x1); headerV1.setLogsBloom(logsBloom); RLPList fullEncoded = RLP.decodeList(headerV1.getFullEncoded()); - RLPList encodedForHeaderMessage = RLP.decodeList(headerV1.getEncodedForHeaderMessage()); + RLPList encodedCompressed = RLP.decodeList(headerV1.getEncodedCompressed()); + + // extension data takes the element of logs bloom + // version element is not existent, is inside the rlp list od the extension data + // edges is not existent + int sizeDifference = 2; + Assertions.assertEquals(fullEncoded.size() - sizeDifference, encodedCompressed.size()); + + for (int i = 0; i < encodedCompressed.size(); i++) { + int j = i < 16 ? i : i + sizeDifference; // keep comparing elements jumping version and edges + if (i != 6) { + // logs bloom field + Assertions.assertArrayEquals(fullEncoded.get(j).getRLPData(), encodedCompressed.get(i).getRLPData()); + } + } + } - Assertions.assertEquals(fullEncoded.size() - 1, encodedForHeaderMessage.size()); + @Test + void compressedEncodingV1HasSameRLPSizeAsFullEncodedV0WithoutEdges () { + // this test is added to assert that the rlp header size does not change + // in the hard fork, assuming both RSKIP 351 and RSKIP 144 are activated + // together + BlockHeaderV0 headerV0 = (BlockHeaderV0) createBlockHeaderWithVersion((byte) 0x0); + headerV0.setLogsBloom(logsBloom); + headerV0.setTxExecutionSublistsEdges(null); - for (int i = 0; i < encodedForHeaderMessage.size(); i++) { - int j = i < 16 ? i : i + 1; //padding if extension has edges - if (i != 6) // logs bloom field - Assertions.assertArrayEquals(fullEncoded.get(j).getRLPData(), encodedForHeaderMessage.get(i).getRLPData()); - } + BlockHeaderV1 headerV1 = (BlockHeaderV1) createBlockHeaderWithVersion((byte) 0x1); + headerV1.setLogsBloom(logsBloom); + headerV1.setTxExecutionSublistsEdges(null); + + RLPList compressedEncodingV1 = RLP.decodeList(headerV1.getEncodedCompressed()); + RLPList fullEncodedV0 = RLP.decodeList(headerV0.getFullEncoded()); - Assertions.assertFalse(Arrays.equals(fullEncoded.get(6).getRLPData(), encodedForHeaderMessage.get(6).getRLPData())); + Assertions.assertEquals(fullEncodedV0.size(), compressedEncodingV1.size()); } @Test - public void hashOfV1IncludesLogsBloom() { + void hashOfV1IncludesLogsBloom() { BlockHeaderV1 headerV1 = (BlockHeaderV1) createBlockHeaderWithVersion((byte) 0x1); - byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; - logsBloom[0] = 0x01; - logsBloom[1] = 0x02; - logsBloom[2] = 0x03; - logsBloom[3] = 0x04; headerV1.setLogsBloom(logsBloom); byte[] hash = headerV1.getHash().getBytes(); @@ -527,6 +553,20 @@ public void hashOfV1IncludesLogsBloom() { Assertions.assertFalse(Arrays.equals(hash, headerV1.getHash().getBytes())); } + @Test + void hashOfV1IncludesEdges() { + BlockHeaderV1 headerV1 = (BlockHeaderV1) createBlockHeaderWithVersion((byte) 0x1); + + headerV1.setTxExecutionSublistsEdges(edges); + byte[] hash = headerV1.getHash().getBytes(); + + + short[] otherEdges = new short[]{ 1, 2, 3, 5}; + headerV1.setTxExecutionSublistsEdges(otherEdges); + + Assertions.assertFalse(Arrays.equals(hash, headerV1.getHash().getBytes())); + } + private BlockHeader createBlockHeaderWithMergedMiningFields( byte[] forkDetectionData, boolean includeForkDetectionData, byte[] ummRoot, short[] edges){ @@ -607,7 +647,8 @@ private BlockHeader createBlockHeader(byte version, useRskip92Encoding, includeForkDetectionData, ummRoot, - edges); + edges, + false); return new BlockHeaderV0( PegTestUtils.createHash3().getBytes(), diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java index 94c61d78ffa..bbca89dc786 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java @@ -4,8 +4,6 @@ import org.ethereum.TestUtils; import org.ethereum.core.BlockHeaderExtensionV1; import org.ethereum.core.BlockHeaderV0; -import org.ethereum.core.BlockHeaderV1; -import org.ethereum.core.Bloom; import org.ethereum.crypto.HashUtil; import org.ethereum.util.RLP; import org.junit.jupiter.api.Assertions; @@ -67,7 +65,7 @@ void logsBloomFieldEncoded() { byte[] bloom = TestUtils.randomBytes(256); short[] edges = new short[]{ 1, 2, 3, 4 }; BlockHeaderV0 header = createBlockHeader(bloom, edges); - byte[] field = RLP.decode2(header.getLogsBloomFieldEncoded()).get(0).getRLPData(); + byte[] field = header.getExtensionData(); Assertions.assertArrayEquals(bloom, field); } } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java index b27cb9f0256..fd503921940 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java @@ -4,9 +4,9 @@ import org.ethereum.TestUtils; import org.ethereum.core.BlockHeaderExtensionV1; import org.ethereum.core.BlockHeaderV1; -import org.ethereum.core.Bloom; import org.ethereum.crypto.HashUtil; import org.ethereum.util.RLP; +import org.ethereum.util.RLPList; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -41,7 +41,8 @@ private BlockHeaderV1 createBlockHeader(byte[] logsBloom) { false, false, null, - new short[0] + new short[0], + false ); } @@ -74,20 +75,19 @@ void setsLogsBloomToExtension() { void logsBloomFieldEncoded() { byte[] bloom = TestUtils.randomBytes(256); BlockHeaderV1 header = createBlockHeader(bloom); - byte[] field = RLP.decode2(header.getLogsBloomFieldEncoded()).get(0).getRLPData(); - Assertions.assertEquals((byte) 0x1, field[0]); - for (int i = 33; i < 256; i++) Assertions.assertEquals((byte) 0x0, field[i]); - Assertions.assertEquals(Bloom.BLOOM_BYTES, field.length); + RLPList extensionDataRLP = RLP.decodeList(header.getExtensionData()); + + byte version = extensionDataRLP.get(0).getRLPData()[0]; + byte[] extensionHash = extensionDataRLP.get(1).getRLPData(); + + Assertions.assertEquals((byte) 0x1, version); + Assertions.assertArrayEquals(header.getExtension().getHash(), extensionHash); } BlockHeaderV1 encodedHeaderWithRandomLogsBloom() { return createBlockHeader(TestUtils.randomBytes(256)); } - byte[] getLogsBloomFieldHashPart(byte[] encodedHeader) { - return Arrays.copyOfRange(encodedHeader, 1, 33); - } - @Test void logsBloomFieldEncodedIncludesExtensionHash() { BlockHeaderV1 header = encodedHeaderWithRandomLogsBloom(); @@ -95,13 +95,13 @@ void logsBloomFieldEncodedIncludesExtensionHash() { byte[] hash = TestUtils.randomBytes(32); Mockito.when(extension.getHash()).thenReturn(hash); header.setExtension(extension); - byte[] encoded = header.getLogsBloomFieldEncoded(); + BlockHeaderV1 otherHeader = encodedHeaderWithRandomLogsBloom(); BlockHeaderExtensionV1 otherExtension = Mockito.mock(BlockHeaderExtensionV1.class); byte[] otherHash = TestUtils.randomBytes(32); Mockito.when(otherExtension.getHash()).thenReturn(otherHash); - header.setExtension(otherExtension); + otherHeader.setExtension(otherExtension); - Assertions.assertFalse(Arrays.equals(encoded, header.getLogsBloomFieldEncoded())); + Assertions.assertFalse(Arrays.equals(header.getExtensionData(), otherHeader.getExtensionData())); } } diff --git a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java index 1c49446746e..81418b68209 100644 --- a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java @@ -261,7 +261,10 @@ void createsHeaderWithEmptyMergedMiningFields() { @ParameterizedTest(name = "createHeader: when createConsensusCompliantHeader {0} and useRskip92Encoding {1} then expectedSize {2}") @ArgumentsSource(CreateHeaderArgumentsProvider.class) - void createsHeaderWith(boolean createConsensusCompliantHeader, boolean useRskip92Encoding, int expectedSize) { + void createsHeaderWith(boolean createConsensusCompliantHeader, boolean useRskip92Encoding, boolean useRSKIP351, int expectedSize) { + BlockHeaderBuilder blockHeaderBuilder = useRSKIP351 + ? this.blockHeaderBuilder + : new BlockHeaderBuilder(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)); byte[] btcCoinbase = TestUtils.randomBytes(128); byte[] btcHeader = TestUtils.randomBytes(80); byte[] merkleProof = TestUtils.randomBytes(32); @@ -502,9 +505,12 @@ private static class CreateHeaderArgumentsProvider implements ArgumentsProvider @Override public Stream provideArguments(ExtensionContext context) { return Stream.of( - Arguments.of(false, false, 21), - Arguments.of(false, true, 19), - Arguments.of(true, false, 19) + Arguments.of(false, false, false, 21), + Arguments.of(false, true, false, 19), + Arguments.of(true, false, false, 19), + Arguments.of(false, false, true, 22), + Arguments.of(false, true, true, 20), + Arguments.of(true, false, true, 20) ); } } From 735ead401e7698bacb1f2b82edd64edee6e6b216 Mon Sep 17 00:00:00 2001 From: Ilan <36084092+ilanolkies@users.noreply.github.com> Date: Mon, 13 Mar 2023 12:40:02 -0300 Subject: [PATCH 28/89] Remove unused import --- rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java | 1 - 1 file changed, 1 deletion(-) diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java index 54e1c2e9105..ea80deb6ee1 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV0.java @@ -21,7 +21,6 @@ import co.rsk.core.BlockDifficulty; import co.rsk.core.Coin; import co.rsk.core.RskAddress; -import org.ethereum.util.RLP; import java.util.Arrays; import java.util.List; From ac3884fd67fc1615d68aa6a779f8a8f4b5d072a7 Mon Sep 17 00:00:00 2001 From: julianlen Date: Mon, 10 Apr 2023 11:32:16 -0300 Subject: [PATCH 29/89] Apart from testing, when RSKIP144 is deactivated, the txExecutionSublistsEdges is null, otherwise is initially an empty array or it has the correct content (#2005) Co-authored-by: Julian Len --- .../src/main/java/co/rsk/RskContext.java | 2 +- .../java/co/rsk/core/bc/BlockExecutor.java | 7 +--- .../ValidTxExecutionSublistsEdgesRule.java | 14 ++++++- .../ValidTxExecutionSublistsEdgesTest.java | 38 ++++++++++++------- 4 files changed, 40 insertions(+), 21 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/RskContext.java b/rskj-core/src/main/java/co/rsk/RskContext.java index 5885fc0d716..205c38984ac 100644 --- a/rskj-core/src/main/java/co/rsk/RskContext.java +++ b/rskj-core/src/main/java/co/rsk/RskContext.java @@ -1108,7 +1108,7 @@ public synchronized BlockValidationRule getBlockValidationRule() { new GasLimitRule(commonConstants.getMinGasLimit()), new ExtraDataRule(commonConstants.getMaximumExtraDataSize()), getForkDetectionDataRule(), - new ValidTxExecutionSublistsEdgesRule() + new ValidTxExecutionSublistsEdgesRule(getRskSystemProperties().getActivationConfig()) ); } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 79b225de708..53016dd4bdc 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -259,8 +259,7 @@ private boolean validateLogsBloom(BlockHeader header, BlockResult result) { } public BlockResult executeForMining(Block block, BlockHeader parent, boolean discardInvalidTxs, boolean ignoreReadyToExecute, boolean saveState) { - boolean rskip144Active = activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber()); - if (rskip144Active || (block.getHeader().getTxExecutionSublistsEdges() != null)) { + if (activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber())) { return executeForMiningAfterRSKIP144(block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); } else { return executeInternal(null, 0, block, parent, discardInvalidTxs, ignoreReadyToExecute, saveState); @@ -291,9 +290,7 @@ public BlockResult execute( boolean acceptInvalidTransactions, boolean saveState ) { - boolean rskip144Active = activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber()); - - if (rskip144Active && block.getHeader().getTxExecutionSublistsEdges() != null) { + if (activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber())) { return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); } else { return executeInternal(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); diff --git a/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java index 637b5b71c04..63e6cbb3e43 100644 --- a/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java +++ b/rskj-core/src/main/java/co/rsk/validators/ValidTxExecutionSublistsEdgesRule.java @@ -1,6 +1,8 @@ package co.rsk.validators; import org.ethereum.config.Constants; +import org.ethereum.config.blockchain.upgrades.ActivationConfig; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.Block; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,12 +17,22 @@ public class ValidTxExecutionSublistsEdgesRule implements BlockValidationRule { private static final Logger logger = LoggerFactory.getLogger("blockvalidator"); + private final ActivationConfig activationConfig; + + public ValidTxExecutionSublistsEdgesRule(ActivationConfig activationConfig) { + this.activationConfig = activationConfig; + } + @Override public boolean isValid(Block block) { + if (!activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber())) { + return true; + } + short[] edges = block.getHeader().getTxExecutionSublistsEdges(); - if (edges == null || edges.length == 0) { + if (edges.length == 0) { return true; } diff --git a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java index 9731540bebb..1f63abc4bb1 100644 --- a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java +++ b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java @@ -1,5 +1,7 @@ package co.rsk.validators; +import org.ethereum.config.blockchain.upgrades.ActivationConfig; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.Block; import org.ethereum.core.BlockHeader; import org.junit.jupiter.api.Assertions; @@ -10,47 +12,55 @@ import java.util.LinkedList; import java.util.List; +import static org.mockito.AdditionalMatchers.geq; +import static org.mockito.ArgumentMatchers.any; + public class ValidTxExecutionSublistsEdgesTest { private BlockHeader blockHeader; private Block block; private List txList; private ValidTxExecutionSublistsEdgesRule rule; + private ActivationConfig activationConfig; + private long blockNumber = 1L; @BeforeEach public void setUp() { blockHeader = Mockito.mock(org.ethereum.core.BlockHeader.class); block = Mockito.mock(Block.class); txList = Mockito.mock(LinkedList.class); + activationConfig = Mockito.mock(ActivationConfig.class); Mockito.when(block.getHeader()).thenReturn(blockHeader); Mockito.when(block.getTransactionsList()).thenReturn(txList); + Mockito.when(blockHeader.getNumber()).thenReturn(blockNumber); Mockito.when(txList.size()).thenReturn(10); - rule = new ValidTxExecutionSublistsEdgesRule(); + rule = new ValidTxExecutionSublistsEdgesRule(activationConfig); } - private void mockGetTxExecutionListsEdges (short[] edges) { + private void mockGetTxExecutionListsEdges (short[] edges, boolean rskip144Activated) { Mockito.when(blockHeader.getTxExecutionSublistsEdges()).thenReturn(edges); + Mockito.when(activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)).thenReturn(rskip144Activated); } // valid cases @Test - void blockWithValidEdges() { - mockGetTxExecutionListsEdges(new short[]{2, 5, 6}); + void blockWithRSKIP144Deactivated() { + mockGetTxExecutionListsEdges(null, false); Assertions.assertTrue(rule.isValid(block)); } @Test - void blockWithNullEdges() { - mockGetTxExecutionListsEdges(null); + void blockWithValidEdges() { + mockGetTxExecutionListsEdges(new short[]{2, 5, 6}, true); Assertions.assertTrue(rule.isValid(block)); } @Test void blockWithEmptyEdges() { - mockGetTxExecutionListsEdges(new short[0]); + mockGetTxExecutionListsEdges(new short[0], true); Assertions.assertTrue(rule.isValid(block)); } @@ -58,7 +68,7 @@ void blockWithEmptyEdges() { // invalid cases @Test void blockWithTooManyEdges() { - mockGetTxExecutionListsEdges(new short[]{1, 2, 3, 4, 5}); + mockGetTxExecutionListsEdges(new short[]{1, 2, 3, 4, 5}, true); Assertions.assertFalse(rule.isValid(block)); } @@ -68,42 +78,42 @@ void blockWithOutOfBoundsEdgesBecauseOfRemascTx() { // include the last tx in a parallelized thread // shouldn't be valid because the last transaction // is the remasc transaction and cannot be parallelized - mockGetTxExecutionListsEdges(new short[]{10}); + mockGetTxExecutionListsEdges(new short[]{10}, true); Assertions.assertFalse(rule.isValid(block)); } @Test void blockWithOutOfBoundsEdges() { - mockGetTxExecutionListsEdges(new short[]{12}); + mockGetTxExecutionListsEdges(new short[]{12}, true); Assertions.assertFalse(rule.isValid(block)); } @Test void blockWithNegativeEdge() { - mockGetTxExecutionListsEdges(new short[]{-2}); + mockGetTxExecutionListsEdges(new short[]{-2}, true); Assertions.assertFalse(rule.isValid(block)); } @Test void blockWithEdgeZero() { - mockGetTxExecutionListsEdges(new short[]{0, 2}); + mockGetTxExecutionListsEdges(new short[]{0, 2}, true); Assertions.assertFalse(rule.isValid(block)); } @Test void blockWithRepeatedEdge() { - mockGetTxExecutionListsEdges(new short[]{2, 2}); + mockGetTxExecutionListsEdges(new short[]{2, 2}, true); Assertions.assertFalse(rule.isValid(block)); } @Test void blockWithEdgesNotInOrder() { - mockGetTxExecutionListsEdges(new short[]{2, 4, 3}); + mockGetTxExecutionListsEdges(new short[]{2, 4, 3}, true); Assertions.assertFalse(rule.isValid(block)); } From b45f75b686eccb59a1afed322b26f9e535957b54 Mon Sep 17 00:00:00 2001 From: julianlen Date: Mon, 10 Apr 2023 12:07:32 -0300 Subject: [PATCH 30/89] Rskip351+rskip144+precompiled contracts (#1994) From now on, whenever a tx execution touches a Precompiled contract, it goes to the sequential sublist. To do that, I added a new flag called PrecompiledHasBeenCalled in both the Program and the TransactionExecutor classes. Furthermore, I tested the flag in each class and added some DSL tests for a more complete testing. --- .../java/co/rsk/core/bc/BlockExecutor.java | 19 +- .../bc/ParallelizeTransactionHandler.java | 17 + .../ethereum/core/TransactionExecutor.java | 11 + .../java/org/ethereum/vm/program/Program.java | 9 +- .../bc/ParallelizeTransactionHandlerTest.java | 117 +++-- .../PrecompiledContractHasBeenCalledTest.java | 455 ++++++++++++++++++ .../TransactionExecutorTest.java | 26 +- .../parallel/ParallelExecutionStateTest.java | 263 +++++++++- .../src/test/java/org/ethereum/vm/VMTest.java | 44 ++ 9 files changed, 916 insertions(+), 45 deletions(-) create mode 100644 rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java rename rskj-core/src/test/java/{org/ethereum/core => co/rsk/core/bc/transactionexecutor}/TransactionExecutorTest.java (95%) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 53016dd4bdc..2def3269ea8 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -622,12 +622,13 @@ private BlockResult executeForMiningAfterRSKIP144( continue; } - Optional sublistGasAccumulated = calculateSublistGasAccumulated( + Optional sublistGasAccumulated = addTxToSublistAndGetAccumulatedGas( readWrittenKeysTracker, parallelizeTransactionHandler, tx, tx.isRemascTransaction(txindex, transactionsList.size()), - txExecutor); + txExecutor.getGasUsed(), + txExecutor.precompiledContractHasBeenCalled()); if (!acceptInvalidTransactions && !sublistGasAccumulated.isPresent()) { if (!discardInvalidTxs) { @@ -672,7 +673,6 @@ private BlockResult executeForMiningAfterRSKIP144( saveOrCommitTrackState(saveState, track); - List executedTransactions = parallelizeTransactionHandler.getTransactionsInOrder(); short[] sublistOrder = parallelizeTransactionHandler.getTransactionsPerSublistInOrder(); List receipts = getTransactionReceipts(receiptsByTx, executedTransactions); @@ -732,12 +732,17 @@ private List getTransactionReceipts(Map calculateSublistGasAccumulated(IReadWrittenKeysTracker readWrittenKeysTracker, ParallelizeTransactionHandler parallelizeTransactionHandler, Transaction tx, boolean isRemascTransaction, TransactionExecutor txExecutor) { + private Optional addTxToSublistAndGetAccumulatedGas(IReadWrittenKeysTracker readWrittenKeysTracker, ParallelizeTransactionHandler parallelizeTransactionHandler, Transaction tx, boolean isRemascTransaction, long gasUsed, boolean precompiledContractHasBeenCalled) { Optional sublistGasAccumulated; - if (isRemascTransaction) { - sublistGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, txExecutor.getGasUsed()); + + if (precompiledContractHasBeenCalled) { + if (isRemascTransaction) { + sublistGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, gasUsed); + } else { + sublistGasAccumulated = parallelizeTransactionHandler.addTxSentToPrecompiledContract(tx, gasUsed); + } } else { - sublistGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getThisThreadReadKeys(), readWrittenKeysTracker.getThisThreadWrittenKeys(), txExecutor.getGasUsed()); + sublistGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getThisThreadReadKeys(), readWrittenKeysTracker.getThisThreadWrittenKeys(), gasUsed); } return sublistGasAccumulated; } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index 7c41569f8f8..f15941af371 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -71,6 +71,23 @@ public Optional addRemascTransaction(Transaction tx, long gasUsedByTx) { return Optional.of(sequentialSublist.getGasUsed()); } + // Unlike the smart contracts execution, Precompiled contracts persist the changes in the repository when they + // complete the execution. So, there is no way for the ParallelTransactionHandler to track the keys if the execution + // fails since the Precompiled contract rolls back the repository. + // Therefore, the tx is added to the sequential sublist to avoid possible race conditions. + public Optional addTxSentToPrecompiledContract(Transaction tx, long gasUsedByTx) { + TransactionSublist sequentialSublist = getSequentialSublist(); + + if (!sublistHasAvailableGas(tx, sequentialSublist)) { + return Optional.empty(); + } + + sequentialSublist.addTransaction(tx, gasUsedByTx); + addNewKeysToMaps(tx.getSender(), sequentialSublist, new HashSet<>(), new HashSet<>()); + + return Optional.of(sequentialSublist.getGasUsed()); + } + public long getGasUsedIn(Short sublistId) { if (sublistId < 0 || sublistId >= sublists.size()) { diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index aa85ce9aa64..72b96959583 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -99,6 +99,7 @@ public class TransactionExecutor { private final SignatureCache signatureCache; private boolean localCall = false; + private boolean precompiledContractHasBeenCalledFlag = false; private boolean postponeFeePayment; @@ -322,6 +323,7 @@ private void call() { this.subtraces = new ArrayList<>(); if (precompiledContract != null) { + this.precompiledContractHasBeenCalledFlag = true; Metric metric = profiler.start(Profiler.PROFILING_TYPE.PRECOMPILED_CONTRACT_INIT); precompiledContract.init(tx, executionBlock, track, blockStore, receiptStore, result.getLogInfoList()); profiler.stop(metric); @@ -439,6 +441,11 @@ private void go() { vm.play(program); + // This line checks whether the invoked smart contract calls a Precompiled contract. + // This flag is then taken by the Parallel transaction handler, if the tx calls a precompiled contract, + // it should be executed sequentially. + precompiledContractHasBeenCalledFlag |= program.precompiledContractHasBeenCalled(); + result = program.getResult(); gasLeftover = GasCost.subtract(GasCost.toGas(tx.getGasLimit()), program.getResult().getGasUsed()); @@ -690,4 +697,8 @@ public long getGasUsed() { } public Coin getPaidFees() { return paidFees; } + + public boolean precompiledContractHasBeenCalled() { + return this.precompiledContractHasBeenCalledFlag; + } } diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java index 9a2ec121b15..6e0c027e7a2 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -112,6 +112,7 @@ public class Program { private final Set deletedAccountsInBlock; private final SignatureCache signatureCache; + private boolean precompiledContractHasBeenCalled = false; public Program( VmConfig config, @@ -150,7 +151,6 @@ public Program( this.storage = setupProgramListener(new Storage(programInvoke)); this.deletedAccountsInBlock = new HashSet<>(deletedAccounts); this.signatureCache = signatureCache; - precompile(); traceListener = new ProgramTraceListener(config); } @@ -835,6 +835,7 @@ private boolean executeCode( getTrace().merge(program.getTrace()); getResult().merge(childResult); + this.precompiledContractHasBeenCalled |= program.precompiledContractHasBeenCalled; boolean childCallSuccessful = true; @@ -1332,7 +1333,7 @@ public int verifyJumpDest(DataWord nextPC) { } public void callToPrecompiledAddress(MessageCall msg, PrecompiledContract contract) { - + this.precompiledContractHasBeenCalled = true; if (getCallDeep() == getMaxDepth()) { stackPushZero(); this.refundGas(msg.getGas().longValue(), " call deep limit reach"); @@ -1487,6 +1488,10 @@ private boolean byTestingSuite() { return invoke.byTestingSuite(); } + public boolean precompiledContractHasBeenCalled() { + return this.precompiledContractHasBeenCalled; + } + public interface ProgramOutListener { void output(String out); } diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index fd08a57ef77..add8373ced6 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -40,7 +40,7 @@ public class ParallelizeTransactionHandlerTest { private Transaction tx2; private Transaction tx3; private ByteArrayWrapper aWrappedKey; - private ByteArrayWrapper aDifferentWrapperKey; + private ByteArrayWrapper aDifferentWrappedKey; private Transaction bigTx; private Transaction bigTx2; private short sequentialSublistNumber; @@ -62,12 +62,12 @@ public void setup() { sublists = 2; sequentialSublistNumber = sublists; handler = new ParallelizeTransactionHandler(sublists, blockGasLimit); - tx = new TransactionBuilder().nonce(1).sender(sender).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); - tx2 = new TransactionBuilder().nonce(1).sender(sender2).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); - tx3 = new TransactionBuilder().nonce(1).sender(sender3).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); + tx = new TransactionBuilder().nonce(1).sender(sender).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx+1)).build(); + tx2 = new TransactionBuilder().nonce(1).sender(sender2).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx+2)).build(); + tx3 = new TransactionBuilder().nonce(1).sender(sender3).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx+3)).build(); bigTx = new TransactionBuilder().nonce(1).sender(sender4).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInSublists)).value(BigInteger.valueOf(1)).build(); bigTx2 = new TransactionBuilder().nonce(1).sender(sender5).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInSublists)).value(BigInteger.valueOf(1)).build(); - aDifferentWrapperKey = new ByteArrayWrapper(aDifferentKey); + aDifferentWrappedKey = new ByteArrayWrapper(aDifferentKey); } @Test @@ -131,7 +131,7 @@ void addTwoTransactionsWithDifferentReadKeysShouldBeAddedInADifferentSublist() { short[] expectedTransactionEdgeList = new short[]{1, 2}; HashSet readKeys = createASetAndAddKeys(aWrappedKey); - HashSet readKeys2 = createASetAndAddKeys(aDifferentWrapperKey); + HashSet readKeys2 = createASetAndAddKeys(aDifferentWrappedKey); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); @@ -147,7 +147,7 @@ void addTwoTransactionsWithDifferentReadKeysShouldBeAddedInADifferentSublist() { } @Test - void addTwoTransactionsWithSameWrittenKeysShouldBeAddedInTheSameBucket() { + void addTwoTransactionsWithSameWrittenKeysShouldBeAddedInTheSameSublist() { short[] expectedTransactionEdgeList = new short[]{2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); @@ -169,7 +169,7 @@ void addTwoTransactionsWithDifferentWrittenKeysShouldBeAddedInDifferentBuckets() short[] expectedTransactionEdgeList = new short[]{1, 2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrapperKey); + HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrappedKey); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); @@ -225,11 +225,11 @@ void addTwoTransactionsWithTheSameReadWrittenKeyShouldBeAddedInTheSameSublist() } @Test - void addTwoTransactionsWithDifferentReadWrittenKeysShouldBeAddedInDifferentBuckets() { + void addTwoTransactionsWithDifferentReadWrittenKeysShouldBeAddedInDifferentSublists() { short[] expectedTransactionEdgeList = new short[]{1,2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - HashSet readKeys = createASetAndAddKeys(aDifferentWrapperKey); + HashSet readKeys = createASetAndAddKeys(aDifferentWrappedKey); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); @@ -249,7 +249,7 @@ void addTwoTransactionWithDifferentWrittenReadKeyShouldBeAddedInDifferentSublist short[] expectedTransactionEdgeList = new short[]{1, 2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - HashSet readKeys = createASetAndAddKeys(aDifferentWrapperKey); + HashSet readKeys = createASetAndAddKeys(aDifferentWrappedKey); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); @@ -269,7 +269,7 @@ void addTwoIndependentTxsAndAThirdOneCollidingWithBothAndShouldBeAddedInTheSeque short[] expectedTransactionEdgeList = new short[]{1, 2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - HashSet differentWrittenKeys = createASetAndAddKeys(aDifferentWrapperKey); + HashSet differentWrittenKeys = createASetAndAddKeys(aDifferentWrappedKey); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); @@ -430,8 +430,8 @@ void ifATxHasTheSameSenderThatAnotherAlreadyAddedIntoTheSequentialShouldGoToTheS short[] expectedTransactionEdgeList = new short[]{1,2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrapperKey); - HashSet readKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); + HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrappedKey); + HashSet readKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrappedKey); Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); @@ -450,17 +450,18 @@ void ifATxHasTheSameSenderThatAnotherAlreadyAddedIntoTheSequentialShouldGoToTheS } @Test - void ifATxReadTwoDifferentWrittenKeysShouldGoToSequential() { + void ifATxReadTwoDifferentKeysWrittenByDifferentTxsInDifferentSublistsShouldGoToSequential() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{1,2}; HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); - HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrapperKey); - HashSet readKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); + HashSet writtenKeys2 = createASetAndAddKeys(aDifferentWrappedKey); + HashSet readKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrappedKey); Optional sublistGasUsed = handler.addTransaction(tx, new HashSet<>(), writtenKeys, gasUsedByTx); - Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx); + Optional sublistGasUsed2 = handler.addTransaction(tx2, new HashSet<>(), writtenKeys2, gasUsedByTx2); Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); @@ -474,7 +475,7 @@ void ifATxReadTwoDifferentWrittenKeysShouldGoToSequential() { } @Test - void ifATxWritesAKeyAlreadyReadByTwoTxsPlacedInDifferentSublistsShouldGoToTheSequential() { + void ifATxWritesAKeyAlreadyReadByTwoTxsInDifferentSublistsShouldGoToTheSequential() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{1,2}; @@ -497,14 +498,14 @@ void ifATxWritesAKeyAlreadyReadByTwoTxsPlacedInDifferentSublistsShouldGoToTheSeq Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @Test - void ifATxReadTwoKeysThatAreInDifferentSublistsShouldGoToTheSequential() { + void ifATxReadTwoKeysThatAreWereWrittenByTxsInDifferentSublistsShouldGoToTheSequential() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx3 = GasCost.toGas(tx3.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{1,2}; HashSet readKeys = createASetAndAddKeys(aWrappedKey); - HashSet readKeys2 = createASetAndAddKeys(aDifferentWrapperKey); - HashSet writtenKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); + HashSet readKeys2 = createASetAndAddKeys(aDifferentWrappedKey); + HashSet writtenKeys = createASetAndAddKeys(aWrappedKey, aDifferentWrappedKey); Optional sublistGasUsed = handler.addTransaction(tx, readKeys, new HashSet<>(), gasUsedByTx); Optional sublistGasUsed2 = handler.addTransaction(tx2, readKeys2, new HashSet<>(), gasUsedByTx); @@ -537,7 +538,7 @@ void ifATxCollidesWithAnotherOneThatAlsoHasTheSameSenderShouldGoIntoTheSameSubli } @Test - void ifATransactionHasAnAlreadyAddedSenderButCollidesWithAnotherTxShouldBeAddedIntoTheSequential() { + void ifATxCollidesByTheSenderAndAKeyWithTwoTxsShouldBeAddedIntoTheSequential() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); long gasUsedByTx2 = GasCost.toGas(tx2.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{1,2}; @@ -676,7 +677,7 @@ void ifAllTheSublistsAreFullTheNewDependentTxShouldNotBeIncluded() { @Test void aRemascTxAddedShouldBeInTheSequentialSublist() { List expectedListOfTxs = Collections.singletonList(tx); - long gasUsedByTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); Optional sequentialSublistGasUsed = handler.addRemascTransaction(tx, gasUsedByTx); @@ -687,6 +688,62 @@ void aRemascTxAddedShouldBeInTheSequentialSublist() { Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); } + @Test + void aTxDirectedToAPrecompiledContractAddedShouldBeInTheSequentialSublist() { + List expectedListOfTxs = Collections.singletonList(bigTx); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + + Optional sequentialSublistGasUsedAfterBigTx = handler.addTxSentToPrecompiledContract(bigTx, gasUsedByBigTx); + Assertions.assertTrue(sequentialSublistGasUsedAfterBigTx.isPresent()); + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByBigTx, (long) sequentialSublistGasUsedAfterBigTx.get()); + + Optional sequentialSublistGasUsedAfterTx = handler.addTxSentToPrecompiledContract(tx, gasUsedByTx); + Assertions.assertFalse(sequentialSublistGasUsedAfterTx.isPresent()); + + Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + } + + @Test + void aTxDirectedToAPrecompiledContractAddedWithSequentialSublistFullShouldNotBeAdded() { + List expectedListOfTxs = Collections.singletonList(tx); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + Optional sequentialSublistGasUsed = handler.addTxSentToPrecompiledContract(tx, gasUsedByTx); + + Assertions.assertTrue(sequentialSublistGasUsed.isPresent()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByTx, (long) sequentialSublistGasUsed.get()); + Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); + } + + @Test + void whenATxCallsAPrecompiledAnotherWithTheSameSenderShouldGoToSequential() { + List expectedListOfTxsPreSecondTx = Collections.singletonList(tx); + List expectedListOfTxsPostSecondTx = Arrays.asList(tx, tx); + long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); + + Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); + Optional sequentialSublistGasUsed = handler.addTxSentToPrecompiledContract(tx, gasUsedByTx); + + Assertions.assertTrue(sequentialSublistGasUsed.isPresent()); + Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByTx, (long) sequentialSublistGasUsed.get()); + Assertions.assertEquals(expectedListOfTxsPreSecondTx, handler.getTransactionsInOrder()); + + Optional sequentialSublistGasUsedByTx2 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); + + Assertions.assertTrue(sequentialSublistGasUsedByTx2.isPresent()); + Assertions.assertEquals(gasUsedByTx*2, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByTx*2, (long) sequentialSublistGasUsedByTx2.get()); + Assertions.assertEquals(expectedListOfTxsPostSecondTx, handler.getTransactionsInOrder()); + } + @Test void ifItsSequentialTheEdgesListShouldHaveSizeZero() { handler.addRemascTransaction(tx, GasCost.toGas(bigTx.getGasLimit())); @@ -718,8 +775,8 @@ void callGetGasUsedInWithAnInvalidSublistShouldThrowAnError2() { @Test void senderWritesAKeyAndReadsAnotherThatIsWrittenShouldGoToSequential() { HashSet writeKeyX = createASetAndAddKeys(aWrappedKey); - HashSet writeKeyY = createASetAndAddKeys(aDifferentWrapperKey); - HashSet readKeyY = createASetAndAddKeys(aDifferentWrapperKey); + HashSet writeKeyY = createASetAndAddKeys(aDifferentWrappedKey); + HashSet readKeyY = createASetAndAddKeys(aDifferentWrappedKey); Account senderA = new AccountBuilder().name("sender1").build(); Account senderB = new AccountBuilder().name("sender2").build(); @@ -739,8 +796,8 @@ void senderWritesAKeyAndReadsAnotherThatIsWrittenShouldGoToSequential() { void senderWritesAKeyAndReadsAnotherThatIsWrittenShouldGoToSequentialIfReadingOtherKeys() { ByteArrayWrapper anotherKey = new ByteArrayWrapper(new byte[]{ 7, 7, 7 }); HashSet writeKeyX = createASetAndAddKeys(aWrappedKey); - HashSet writeKeyYAndAnother = createASetAndAddKeys(anotherKey, aDifferentWrapperKey); - HashSet readKeyYAndAnother = createASetAndAddKeys(anotherKey, aDifferentWrapperKey); + HashSet writeKeyYAndAnother = createASetAndAddKeys(anotherKey, aDifferentWrappedKey); + HashSet readKeyYAndAnother = createASetAndAddKeys(anotherKey, aDifferentWrappedKey); Account senderA = new AccountBuilder().name("sender1").build(); Account senderB = new AccountBuilder().name("sender2").build(); @@ -759,8 +816,8 @@ void senderWritesAKeyAndReadsAnotherThatIsWrittenShouldGoToSequentialIfReadingOt @Test void writeSequentialAfterTwoParallelReadsAndAWriteShouldGoToSequential() { HashSet setWithX = createASetAndAddKeys(aWrappedKey); - HashSet setWithY = createASetAndAddKeys(aDifferentWrapperKey); - HashSet setWithXAndY = createASetAndAddKeys(aWrappedKey, aDifferentWrapperKey); + HashSet setWithY = createASetAndAddKeys(aDifferentWrappedKey); + HashSet setWithXAndY = createASetAndAddKeys(aWrappedKey, aDifferentWrappedKey); AccountBuilder accountBuilder = new AccountBuilder(); diff --git a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java new file mode 100644 index 00000000000..68022d28908 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java @@ -0,0 +1,455 @@ +package co.rsk.core.bc.transactionexecutor; + +import co.rsk.blockchain.utils.BlockGenerator; +import co.rsk.config.TestSystemProperties; +import co.rsk.core.Coin; +import co.rsk.core.RskAddress; +import co.rsk.core.TransactionExecutorFactory; +import co.rsk.db.MutableTrieImpl; +import co.rsk.peg.BridgeSupportFactory; +import co.rsk.peg.BtcBlockStoreWithCache; +import co.rsk.peg.RepositoryBtcBlockStoreWithCache; +import co.rsk.trie.Trie; +import co.rsk.trie.TrieStore; +import co.rsk.trie.TrieStoreImpl; +import org.bouncycastle.util.encoders.Hex; +import org.ethereum.config.Constants; +import org.ethereum.core.*; +import org.ethereum.crypto.HashUtil; +import org.ethereum.datasource.HashMapDB; +import org.ethereum.db.BlockStoreDummy; +import org.ethereum.db.MutableRepository; +import org.ethereum.vm.PrecompiledContracts; +import org.ethereum.vm.program.ProgramResult; +import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.util.*; + +import static co.rsk.core.bc.BlockExecutorTest.createAccount; +import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH; + +public class PrecompiledContractHasBeenCalledTest { + public static final String SMART_CONTRACT_BYTECODE = "6080604052600160005534801561001557600080fd5b50610115806100256000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80633bccbbc9146037578063853255cc14603f575b600080fd5b603d6047565b005b6045604c565b005b600080fd5b600080815480929190605c90609c565b9190505550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000819050919050565b600060a5826092565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820360d45760d36063565b5b60018201905091905056fea2646970667358221220d0c973fb2823cb683c85b25b597842bf24f5d10e34b28298cd260a17597d433264736f6c63430008120033"; + public static final String PROXY_SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516104eb3803806104eb833981810160405281019061003291906100db565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610108565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b6100b88161009d565b81146100c357600080fd5b50565b6000815190506100d5816100af565b92915050565b6000602082840312156100f1576100f0610078565b5b60006100ff848285016100c6565b91505092915050565b6103d4806101176000396000f3fe60806040526004361061001e5760003560e01c8063688c62c514610023575b600080fd5b61003d600480360381019061003891906101a1565b610054565b60405161004b929190610299565b60405180910390f35b6000606060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163485856040516100a0929190610308565b60006040518083038185875af1925050503d80600081146100dd576040519150601f19603f3d011682016040523d82523d6000602084013e6100e2565b606091505b5080925081935050508161012b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101229061037e565b60405180910390fd5b9250929050565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126101615761016061013c565b5b8235905067ffffffffffffffff81111561017e5761017d610141565b5b60208301915083600182028301111561019a57610199610146565b5b9250929050565b600080602083850312156101b8576101b7610132565b5b600083013567ffffffffffffffff8111156101d6576101d5610137565b5b6101e28582860161014b565b92509250509250929050565b60008115159050919050565b610203816101ee565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610243578082015181840152602081019050610228565b60008484015250505050565b6000601f19601f8301169050919050565b600061026b82610209565b6102758185610214565b9350610285818560208601610225565b61028e8161024f565b840191505092915050565b60006040820190506102ae60008301856101fa565b81810360208301526102c08184610260565b90509392505050565b600081905092915050565b82818337600083830152505050565b60006102ef83856102c9565b93506102fc8385846102d4565b82840190509392505050565b60006103158284866102e3565b91508190509392505050565b600082825260208201905092915050565b7f4661696c656420746f2063616c6c000000000000000000000000000000000000600082015250565b6000610368600e83610321565b915061037382610332565b602082019050919050565b600060208201905081810360008301526103978161035b565b905091905056fea2646970667358221220ad54ae56c44b1857061914a1dde2177d2f7158fde81816cdacb47d11b605a9cd64736f6c63430008120033000000000000000000000000"; + public static final String PROXY_TO_PROXY_SMART_CONTRACT_BYTECODE = "608060405234801561001057600080fd5b506040516106d03803806106d0833981810160405281019061003291906100ed565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061011a565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100a88261007d565b9050919050565b60006100ba8261009d565b9050919050565b6100ca816100af565b81146100d557600080fd5b50565b6000815190506100e7816100c1565b92915050565b60006020828403121561010357610102610078565b5b6000610111848285016100d8565b91505092915050565b6105a7806101296000396000f3fe60806040526004361061001e5760003560e01c8063688c62c514610023575b600080fd5b61003d600480360381019061003891906101c3565b610054565b60405161004b9291906102bb565b60405180910390f35b6000606060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663688c62c585856040518363ffffffff1660e01b81526004016100b3929190610327565b6000604051808303816000875af11580156100d2573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052508101906100fb9190610498565b809250819350505081610143576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161013a90610551565b60405180910390fd5b9250929050565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b600080fd5b60008083601f8401126101835761018261015e565b5b8235905067ffffffffffffffff8111156101a05761019f610163565b5b6020830191508360018202830111156101bc576101bb610168565b5b9250929050565b600080602083850312156101da576101d9610154565b5b600083013567ffffffffffffffff8111156101f8576101f7610159565b5b6102048582860161016d565b92509250509250929050565b60008115159050919050565b61022581610210565b82525050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561026557808201518184015260208101905061024a565b60008484015250505050565b6000601f19601f8301169050919050565b600061028d8261022b565b6102978185610236565b93506102a7818560208601610247565b6102b081610271565b840191505092915050565b60006040820190506102d0600083018561021c565b81810360208301526102e28184610282565b90509392505050565b82818337600083830152505050565b60006103068385610236565b93506103138385846102eb565b61031c83610271565b840190509392505050565b600060208201905081810360008301526103428184866102fa565b90509392505050565b61035481610210565b811461035f57600080fd5b50565b6000815190506103718161034b565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6103b482610271565b810181811067ffffffffffffffff821117156103d3576103d261037c565b5b80604052505050565b60006103e661014a565b90506103f282826103ab565b919050565b600067ffffffffffffffff8211156104125761041161037c565b5b61041b82610271565b9050602081019050919050565b600061043b610436846103f7565b6103dc565b90508281526020810184848401111561045757610456610377565b5b610462848285610247565b509392505050565b600082601f83011261047f5761047e61015e565b5b815161048f848260208601610428565b91505092915050565b600080604083850312156104af576104ae610154565b5b60006104bd85828601610362565b925050602083015167ffffffffffffffff8111156104de576104dd610159565b5b6104ea8582860161046a565b9150509250929050565b600082825260208201905092915050565b7f4661696c656420746f2063616c6c000000000000000000000000000000000000600082015250565b600061053b600e836104f4565b915061054682610505565b602082019050919050565b6000602082019050818103600083015261056a8161052e565b905091905056fea2646970667358221220465f552de541a4d2292de2fff8f37af0cf3d5b3995b7cadfd358afef3c19f61664736f6c63430008120033000000000000000000000000"; + public static final RskAddress NULL_ADDRESS = RskAddress.nullAddress(); + private TestSystemProperties config; + private TransactionExecutorFactory transactionExecutorFactory; + private Repository track; + private int txIndex; + private Account sender; + + private Account receiver; + + private int difficulty; + private ArrayList uncles; + private BigInteger minGasPrice; + + @BeforeEach + public void setUp() { + config = new TestSystemProperties(); + transactionExecutorFactory = getTransactionExecutorFactory(config); + track = getRepository(); + txIndex = 0; + sender = createAccount("acctest1", track, Coin.valueOf(6000000)); + receiver = createAccount("acctest2", track, Coin.valueOf(6000000)); + initTrackAndAssertIsNotEmpty(track); + difficulty = 1; + uncles = new ArrayList<>(); + minGasPrice = null; + } + + @Test + void whenATxCallsAPrecompiledContractPrecompiledContractHasBeenCalledFlagShouldBeTrue() { + byte[] arbitraryData = Hex.decode("00112233"); + long value = 0L; + boolean precompiledHasBeenCalled = true; + boolean hasRevert = false; + boolean threwAnException = false; + RskAddress destination = PrecompiledContracts.IDENTITY_ADDR; + executeATransactionAndAssert(getABlockWithATx(track, config, arbitraryData, value, destination, sender), txIndex, precompiledHasBeenCalled, hasRevert, threwAnException); + } + + + @Test + void whenATxSendsValueToAPrecompiledContractPrecompiledContractHasBeenCalledFlagShouldBeTrue() { + int value = 1; + boolean precompiledHasBeenCalled = true; + boolean hasRevert = false; + boolean threwAnException = false; + RskAddress destination = PrecompiledContracts.IDENTITY_ADDR; + executeATransactionAndAssert(getABlockWithATx(track, config, null, value, destination, sender), txIndex, precompiledHasBeenCalled, hasRevert, threwAnException); + } + + @Test + void whenATxCallsAPrecompiledContractAndThrowsAnExceptionPrecompiledContractHasBeenCalledFlagShouldBeTrue() { + byte[] dataThatThrowsAnException = Hex.decode("e674f5e80000000000000000000000000000000000000000000000000000000001000006"); + int value = 0; + boolean precompiledHasBeenCalled = true; + boolean hasRevert = false; + boolean threwAnException = true; + RskAddress destination = PrecompiledContracts.BRIDGE_ADDR; + executeATransactionAndAssert(getABlockWithATx(track, config, dataThatThrowsAnException, value, destination, sender), txIndex, precompiledHasBeenCalled, hasRevert, threwAnException); + } + + @Test + void ifAnAccountSendsValueToAnAccountPrecompiledContractHasBeenCalledFlagShouldBeFalse() { + Coin balance_before = track.getBalance(receiver.getAddress()); + executeATransactionAndAssert(getABlockWithATx(track, config, null, 1, receiver.getAddress(), sender), txIndex, false, false, false); + Assertions.assertEquals(balance_before.add(Coin.valueOf(1)), track.getBalance(receiver.getAddress()));; + } + + @Test + void whenATxCallsANonPrecompiledContractPrecompiledContractHasBeenCalledFlagShouldBeFalse() { + List txs = new LinkedList<>(); + + //Deploy a Smart Contract + BigInteger nonce = track.getNonce(sender.getAddress()); + Transaction tx = buildTransaction(config, Hex.decode(SMART_CONTRACT_BYTECODE), 0, NULL_ADDRESS, sender, nonce); + txs.add(tx); + + //Call the Smart Contract + RskAddress smartContractAddress = getContractAddress(sender, nonce); + byte[] dataToCallTheSmartContract = Hex.decode("853255cc"); + BigInteger nonce2 = incrementNonce(nonce); + Transaction tx2 = buildTransaction(config, dataToCallTheSmartContract, 0, smartContractAddress, sender, nonce2); + txs.add(tx2); + + Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, false, false, false); + } + + @Test + void whenATxCallsANonPrecompiledContractAndRevertsPrecompiledContractHasBeenCalledFlagShouldBeFalse() { + List txs = new LinkedList<>(); + + //Deploy a Smart Contract + BigInteger nonce = track.getNonce(sender.getAddress()); + Transaction tx = buildTransaction(config, Hex.decode(SMART_CONTRACT_BYTECODE), 0, NULL_ADDRESS, sender, nonce); + txs.add(tx); + + //Call the Smart Contract + RskAddress smartContractAddress = getContractAddress(sender, nonce); + byte[] dataToCallTheSmartContract = Hex.decode("3bccbbc9"); //reverts + BigInteger nonce2 = incrementNonce(nonce); + Transaction tx2 = buildTransaction(config, dataToCallTheSmartContract, 0, smartContractAddress, sender, nonce2); + txs.add(tx2); + + Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, false, true, false); + } + + @Test + void whenATxCallsAContractThatCallsAPrecompiledContractPrecompiledContractHasBeenCalledFlagShouldBeTrue() { + List txs = new LinkedList<>(); + + //Deploy Proxy to Identity precompiled contract + BigInteger nonce = track.getNonce(sender.getAddress()); + Transaction tx = buildTransaction(config, getDeployDataWithAddressAsParameterToProxyConstructor(PROXY_SMART_CONTRACT_BYTECODE, PrecompiledContracts.IDENTITY_ADDR), 0, NULL_ADDRESS, sender, nonce); + txs.add(tx); + + //Call Proxy + RskAddress proxyContract = getContractAddress(sender, nonce); + byte[] dataToCallAPrecompiledThroughTheProxy = Hex.decode("688c62c5000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000145b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000"); + BigInteger nonce2 = incrementNonce(nonce); + Transaction tx2 = buildTransaction(config, dataToCallAPrecompiledThroughTheProxy, 0, proxyContract, sender, nonce2); + txs.add(tx2); + + Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, true, false, false); + } + + @Test + void whenATxCallsAContractThatCallsAPrecompiledContractAndThrowsExceptionPrecompiledContractHasBeenCalledFlagShouldBeTrue() { + List txs = new LinkedList<>(); + + BigInteger nonce = track.getNonce(sender.getAddress()); + Transaction tx = buildTransaction(config, getDeployDataWithAddressAsParameterToProxyConstructor(PROXY_SMART_CONTRACT_BYTECODE, PrecompiledContracts.BRIDGE_ADDR), 0, NULL_ADDRESS, sender, nonce); + txs.add(tx); + + //Call Proxy + RskAddress proxyContract = getContractAddress(sender, nonce); + byte[] dataToCallAPrecompiledThroughTheProxy = Hex.decode("688c62c500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024e674f5e8000000000000000000000000000000000000000000000000000000000100000600000000000000000000000000000000000000000000000000000000"); + BigInteger nonce1 = incrementNonce(nonce); + Transaction tx1 = buildTransaction(config, dataToCallAPrecompiledThroughTheProxy, 0, proxyContract, sender, nonce1); + txs.add(tx1); + + Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + // As the Precompiled throws an exception, the contract hits the require, and thus reverts. + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, true, true, false); + } + + @Test + void whenAnInternalTxCallsANonPrecompiledContractPrecompiledContractHasBeenCalledFlagShouldBeFalse() { + List txs = new LinkedList<>(); + + //Deploy a Smart Contract + BigInteger nonce = track.getNonce(sender.getAddress()); + Transaction tx = buildTransaction(config, Hex.decode(SMART_CONTRACT_BYTECODE), 0, NULL_ADDRESS, sender, nonce); + RskAddress smartContractAddress = getContractAddress(sender, nonce); + txs.add(tx); + + //Deploy Proxy to Smart Contract + BigInteger nonce1 = incrementNonce(nonce); + Transaction tx1 = buildTransaction(config, getDeployDataWithAddressAsParameterToProxyConstructor(PROXY_SMART_CONTRACT_BYTECODE, smartContractAddress), 0, NULL_ADDRESS, sender, nonce1); + txs.add(tx1); + + //Call the Proxy + byte[] dataForProxyToCallTheSmartContract = Hex.decode("688c62c500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000004853255cc00000000000000000000000000000000000000000000000000000000"); //reverts + BigInteger nonce2 = incrementNonce(nonce1); + RskAddress proxyAddress = getContractAddress(sender, nonce1); + Transaction tx2 = buildTransaction(config, dataForProxyToCallTheSmartContract, 0, proxyAddress, sender, nonce2); + txs.add(tx2); + + Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, false, false, false); + } + + @Test + void whenAnInternalTxCallsANonPrecompiledContractAndRevertsPrecompiledContractHasBeenCalledFlagShouldBeFalse() { + List txs = new LinkedList<>(); + + //Deploy a Smart Contract + BigInteger nonce = track.getNonce(sender.getAddress()); + Transaction tx = buildTransaction(config, Hex.decode(SMART_CONTRACT_BYTECODE), 0, NULL_ADDRESS, sender, nonce); + RskAddress smartContractAddress = getContractAddress(sender, nonce); + txs.add(tx); + + //Deploy Proxy to Smart Contract + BigInteger nonce1 = incrementNonce(nonce); + Transaction tx1 = buildTransaction(config, getDeployDataWithAddressAsParameterToProxyConstructor(PROXY_SMART_CONTRACT_BYTECODE, smartContractAddress), 0, NULL_ADDRESS, sender, nonce1); + RskAddress proxyAddress = getContractAddress(sender, nonce1); + txs.add(tx1); + + //Call the Proxy + byte[] dataForProxyToCallTheSmartContract = Hex.decode("688c62c5000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000043bccbbc900000000000000000000000000000000000000000000000000000000"); //reverts + BigInteger nonce2 = incrementNonce(nonce1); + Transaction tx2 = buildTransaction(config, dataForProxyToCallTheSmartContract, 0, proxyAddress, sender, nonce2); + txs.add(tx2); + + Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, false, true, false); + } + + @Test + void whenAnInternalTxCallsAPrecompiledContractPrecompiledContractHasBeenCalledFlagShouldBeTrue() { + List txs = new LinkedList<>(); + + //Deploy Proxy to Identity precompiled contract + BigInteger nonce = track.getNonce(sender.getAddress()); + RskAddress proxyAddress = getContractAddress(sender, nonce); + Transaction tx = buildTransaction(config, getDeployDataWithAddressAsParameterToProxyConstructor(PROXY_SMART_CONTRACT_BYTECODE, PrecompiledContracts.IDENTITY_ADDR), 0, NULL_ADDRESS, sender, nonce); + txs.add(tx); + + //Deploy Proxy to Proxy + BigInteger nonce2 = incrementNonce(nonce); + RskAddress proxyToProxy = getContractAddress(sender, nonce2); + Transaction tx1 = buildTransaction(config, getDeployDataWithAddressAsParameterToProxyConstructor(PROXY_TO_PROXY_SMART_CONTRACT_BYTECODE, proxyAddress), 0, NULL_ADDRESS, sender, nonce2); + txs.add(tx1); + + //Call Proxy to Proxy + BigInteger nonce3 = incrementNonce(nonce2); + byte[] dataToCallProxyToProxyContract = Hex.decode("688c62c5000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000145fd6eb55d12e759a21c09ef703fe0cba1dc9d88d000000000000000000000000"); + Transaction tx2 = buildTransaction(config, dataToCallProxyToProxyContract, 0, proxyToProxy, sender, nonce3); + txs.add(tx2); + + Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); + + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, true, false, false); + } + + @Test + void whenAnInternalTxCallsAPrecompiledContractAndThrowsAnExceptionPrecompiledContractHasBeenCalledFlagShouldBeTrue(){ + List txs = new LinkedList<>(); + + //Deploy Proxy to Bridge precompiled contract + BigInteger nonce = track.getNonce(sender.getAddress()); + RskAddress proxyAddress = getContractAddress(sender, nonce);; + Transaction tx = buildTransaction(config, getDeployDataWithAddressAsParameterToProxyConstructor(PROXY_SMART_CONTRACT_BYTECODE, PrecompiledContracts.BRIDGE_ADDR), 0, NULL_ADDRESS, sender, nonce); + txs.add(tx); + + //Deploy Proxy to Proxy + BigInteger nonce2 = incrementNonce(nonce); + RskAddress proxyToProxy = getContractAddress(sender, nonce2); + Transaction tx1 = buildTransaction(config, getDeployDataWithAddressAsParameterToProxyConstructor(PROXY_TO_PROXY_SMART_CONTRACT_BYTECODE, proxyAddress), 0, NULL_ADDRESS, sender, nonce2); + txs.add(tx1); + + //Call Proxy to Proxy with data triggers an exception + BigInteger nonce3 = incrementNonce(nonce2); + byte[] dataToCallProxyToProxyContractThatReverts = Hex.decode("688c62c500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024e674f5e8000000000000000000000000000000000000000000000000000000000100000600000000000000000000000000000000000000000000000000000000"); + Transaction tx2 = buildTransaction(config, dataToCallProxyToProxyContractThatReverts, 0, proxyToProxy, sender, nonce3); + txs.add(tx2); + + Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, true, true, false); + } + + private static TransactionExecutorFactory getTransactionExecutorFactory(TestSystemProperties config) { + BlockTxSignatureCache blockTxSignatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); + BtcBlockStoreWithCache.Factory btcBlockStoreFactory = new RepositoryBtcBlockStoreWithCache.Factory( + config.getNetworkConstants().getBridgeConstants().getBtcParams()); + BridgeSupportFactory bridgeSupportFactory = new BridgeSupportFactory( + btcBlockStoreFactory, config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig(), blockTxSignatureCache); + return new TransactionExecutorFactory( + config, + new BlockStoreDummy(), + null, + new BlockFactory(config.getActivationConfig()), + new ProgramInvokeFactoryImpl(), + new PrecompiledContracts(config, bridgeSupportFactory, blockTxSignatureCache), + blockTxSignatureCache + ); + } + + private void executeATransactionAndAssert(Block aBlock, int transactionIndex, boolean precompiledHasBeenCalled, boolean hasRevert, boolean threwAnException) { + TransactionExecutor transactionExecutor = transactionExecutorFactory.newInstance(aBlock.getTransactionsList().get(transactionIndex), transactionIndex, aBlock.getCoinbase(), + track, aBlock, 0L); + + boolean txExecuted = transactionExecutor.executeTransaction(); + assertExecutionFlagAndIfRevertedOrThrewAnException(transactionExecutor, txExecuted, precompiledHasBeenCalled, hasRevert, threwAnException); + } + + private static BigInteger incrementNonce(BigInteger nonce) { + return nonce.add(BigInteger.ONE); + } + + private Block getGenesisBlock(TestSystemProperties config, Repository track) { + BlockGenerator blockGenerator = new BlockGenerator(Constants.regtest(), config.getActivationConfig()); + Block genesis = blockGenerator.getGenesisBlock(); + genesis.setStateRoot(track.getRoot()); + return genesis; + } + + public RskAddress getContractAddress(Account aSender, BigInteger nonce) { + return new RskAddress(HashUtil.calcNewAddr(aSender.getAddress().getBytes(), nonce.toByteArray())); + } + + private static void assertExecutionFlagAndIfRevertedOrThrewAnException(TransactionExecutor txExecutor, boolean txExecuted, boolean precompiledHasBeenCalled, boolean hasRevert, boolean threwAnException) { + Assertions.assertTrue(txExecuted); + ProgramResult transactionResult = txExecutor.getResult(); + Assertions.assertEquals(precompiledHasBeenCalled, txExecutor.precompiledContractHasBeenCalled()); + Exception exception = transactionResult.getException(); + if (threwAnException) { + Assertions.assertNotNull(exception); + } else { + Assertions.assertNull(exception); + } + Assertions.assertEquals(hasRevert, transactionResult.isRevert()); + } + + public byte[] getDeployDataWithAddressAsParameterToProxyConstructor(String data, RskAddress address) { + return Hex.decode(data + address); + } + +/* +- calls a precomp, flag should be true and tx go to seq X +- calls a precomp and revert, same. X +- sends value to a precomp flag should be true. X +- Internal tx calls a precomp so it should go to sequential X +- Internal tx calls a precomp and reverts so it should go to sequential. X +NEGATIVE CASES, IF A REGULAR CONTRACT IS CALLED, FLAG SHOULD BE FALSE, SAME FOR SEND VALUE TO SC AND EOA. + */ + + private Block getABlockWithATx(Repository track, TestSystemProperties config, byte[] data, long value, RskAddress destination, Account account) { + Transaction tx = buildTransaction(config, data, value, destination, account, track.getNonce(account.getAddress())); + List txs = Collections.singletonList( + tx + ); + + Block genesis = getGenesisBlock(config, track); + return new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(genesis, txs, new ArrayList<>(), 1, null); + } + + private static Transaction buildTransaction(TestSystemProperties config, byte[] data, long value, RskAddress destination, Account account, BigInteger nonce) { + Transaction tx = Transaction + .builder() + .nonce(nonce) + .gasPrice(BigInteger.ONE) + .gasLimit(BigInteger.valueOf(2000000)) + .destination(destination) + .chainId(config.getNetworkConstants().getChainId()) + .value(Coin.valueOf(value)) + .data(data) + .build(); + tx.sign(account.getEcKey().getPrivKeyBytes()); + return tx; + } + + private static void initTrackAndAssertIsNotEmpty(Repository track) { + track.commit(); + Assertions.assertFalse(Arrays.equals(EMPTY_TRIE_HASH, track.getRoot())); + } + + private static Repository getRepository() { + TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); + Repository aRepository = new MutableRepository(new MutableTrieImpl(trieStore, new Trie(trieStore))); + return aRepository.startTracking(); + } +} + + +/* Smart contract whose bytecode is PROXY_SMART_CONTRACT_BYTECODE +* +*pragma solidity ^0.8.9; +*contract Proxy { +* +* address precompiledContract; +* +* constructor(address _precompiled) { +* precompiledContract = _precompiled; +* } +* +* function callPrecompiledContract(bytes calldata _data) public payable returns (bool sent, bytes memory ret) { +* (sent, ret) = precompiledContract.call{value: msg.value}(_data); +* require(sent, "Failed to call"); +* } +*} +* */ + +/* Smart contract whose bytecode is PROXY_TO_PROXY_SMART_CONTRACT_BYTECODE +* +* contract ProxyToProxy { +* +* Proxy precompiledContract; +* +* constructor(Proxy _precompiled) { +* precompiledContract = Proxy(_precompiled); +* } +* +* function callPrecompiledContract(bytes calldata _data) public payable returns (bool sent, bytes memory ret) { +* (sent, ret) = precompiledContract.callPrecompiledContract(_data); +* require(sent, "Failed to call"); +* } +*} +* +* +* +* contract Counter { +* +* uint counter = 1; +* +* function sum() public { +* counter++; +* } +* +* function reverts() public { +* reverts(); +* } +*} +* */ \ No newline at end of file diff --git a/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java similarity index 95% rename from rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java rename to rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java index da2f1414d7c..657faab7c0c 100644 --- a/rskj-core/src/test/java/org/ethereum/core/TransactionExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java @@ -15,30 +15,47 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ -package org.ethereum.core; +package co.rsk.core.bc.transactionexecutor; +import co.rsk.blockchain.utils.BlockGenerator; +import co.rsk.config.TestSystemProperties; import co.rsk.config.VmConfig; import co.rsk.core.Coin; import co.rsk.core.RskAddress; +import co.rsk.core.TransactionExecutorFactory; import co.rsk.crypto.Keccak256; +import co.rsk.db.MutableTrieImpl; +import co.rsk.peg.BridgeSupportFactory; +import co.rsk.peg.BtcBlockStoreWithCache; +import co.rsk.peg.RepositoryBtcBlockStoreWithCache; +import co.rsk.trie.Trie; +import co.rsk.trie.TrieStore; +import co.rsk.trie.TrieStoreImpl; +import org.bouncycastle.util.encoders.Hex; import org.ethereum.TestUtils; import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; +import org.ethereum.core.*; import org.ethereum.crypto.HashUtil; +import org.ethereum.datasource.HashMapDB; import org.ethereum.db.BlockStore; +import org.ethereum.db.BlockStoreDummy; import org.ethereum.db.MutableRepository; import org.ethereum.db.ReceiptStore; import org.ethereum.vm.DataWord; import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.program.invoke.ProgramInvokeFactory; +import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigInteger; -import java.util.HashSet; -import java.util.Set; +import java.util.*; +import static co.rsk.core.bc.BlockExecutorTest.createAccount; +import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -66,7 +83,6 @@ void setUp() { // paperwork: mock a whole nice transaction executor txIndex = 1; gasUsedInTheBlock = 0; - activationConfig = ActivationConfigsForTest.all(); constants = mock(Constants.class); rskAddress = mock(RskAddress.class); @@ -299,6 +315,8 @@ void firstTxIsRemovedWhenTheCacheLimitSizeIsExceeded() { assertNotNull(blockTxSignatureCache.getSender(transaction)); } + + private boolean executeValidTransaction(Transaction transaction, BlockTxSignatureCache blockTxSignatureCache) { when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); diff --git a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java index 862934e4375..c5f01a51bc1 100644 --- a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java +++ b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java @@ -25,17 +25,27 @@ import co.rsk.test.dsl.DslProcessorException; import co.rsk.test.dsl.WorldDslProcessor; import com.typesafe.config.ConfigValueFactory; +import org.ethereum.crypto.HashUtil; import org.ethereum.datasource.HashMapDB; import org.ethereum.db.ReceiptStore; import org.ethereum.db.ReceiptStoreImpl; import org.ethereum.core.Transaction; import org.ethereum.vm.GasCost; +import org.ethereum.vm.PrecompiledContracts; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.math.BigInteger; import java.util.List; +import static co.rsk.core.bc.transactionexecutor.PrecompiledContractHasBeenCalledTest.PROXY_SMART_CONTRACT_BYTECODE; +import static co.rsk.core.bc.transactionexecutor.PrecompiledContractHasBeenCalledTest.PROXY_TO_PROXY_SMART_CONTRACT_BYTECODE; +import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; + class ParallelExecutionStateTest { + + public static final byte[] FAILED_STATUS = EMPTY_BYTE_ARRAY; + private World createWorld(int rskip144) { ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); TestSystemProperties config = new TestSystemProperties(rawConfig -> @@ -46,8 +56,8 @@ private World createWorld(int rskip144) { return world; } - private World createWorldAndProcess(String dsl, int rskip144) throws DslProcessorException { - World world = createWorld(rskip144); + private World createWorldAndProcess(String dsl, int rskip144ActivationHeight) throws DslProcessorException { + World world = createWorld(rskip144ActivationHeight); DslParser parser = new DslParser(dsl); WorldDslProcessor processor = new WorldDslProcessor(world); @@ -818,4 +828,253 @@ void useSequentialForCollisionWithSequentialAndParallel() throws DslProcessorExc Assertions.assertEquals(4, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[] { 1, 2 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } + + @Test + void whenATxCallsAPrecompiledItShouldGoToSequential() throws DslProcessorException { + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress " + PrecompiledContracts.IDENTITY_ADDR +"\n"+ + " data " + "1234" + "\n" + + " gas 2500000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "\n", 0); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenATxSendsValueToAPrecompiledItShouldGoToSequential() throws DslProcessorException { + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress " + PrecompiledContracts.IDENTITY_ADDR +"\n"+ + " value 1000\n" + + " gas 2500000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "\n", 0); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenATxCallsAPrecompiledThatThrowsAnExceptionShouldGoToSequential() throws DslProcessorException { + // TX01 throws an exception, though, there is no way to check from out of the execution if the precompiled contract call has failed. + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress " + PrecompiledContracts.BRIDGE_ADDR +"\n"+ + " data " + "e674f5e80000000000000000000000000000000000000000000000000000000001000006" + "\n" + + " gas 2500000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "\n", 0); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenATxCallsAContractThatCallsAPrecompiledShouldGoToSequential() throws DslProcessorException { + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress 00\n" + + " data " + PROXY_SMART_CONTRACT_BYTECODE + PrecompiledContracts.IDENTITY_ADDR + "\n" + + " gas 1200000\n" + + " build\n" + + "\n" + + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + "688c62c5000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000145b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000" + "\n" + + " nonce 1\n" + + " gas 2500000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01 tx02\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "\n", 0); + + Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequential() throws DslProcessorException { + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress 00\n" + + " data " + PROXY_SMART_CONTRACT_BYTECODE + PrecompiledContracts.BRIDGE_ADDR + "\n" + + " gas 1200000\n" + + " build\n" + + "\n" + + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + "688c62c500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024e674f5e8000000000000000000000000000000000000000000000000000000000100000600000000000000000000000000000000000000000000000000000000" + "\n" + + " nonce 1\n" + + " gas 2500000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01 tx02\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "\n", 0); + + Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(FAILED_STATUS, parallel.getTransactionReceiptByName("tx02").getStatus()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenAnInternalTxCallsAContractThatCallsAPrecompiledShouldGoToSequential() throws DslProcessorException { + World parallel = createWorld(0); + WorldDslProcessor processor = new WorldDslProcessor(parallel); + String dsl = deployProxyTo(PrecompiledContracts.IDENTITY_ADDR); + DslParser parser = new DslParser(dsl); + processor.processCommands(parser); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + + RskAddress proxyContract = new RskAddress(HashUtil.calcNewAddr(parallel.getAccountByName("acc1").getAddress().getBytes(), BigInteger.ZERO.toByteArray())); + String dataInputToCallProxyToProxyThatReverts = "688c62c5000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000145fd6eb55d12e759a21c09ef703fe0cba1dc9d88d000000000000000000000000"; + String transactionAssertions = "assert_tx_success tx02\n" + + "assert_tx_success tx03\n" + + "\n"; + String dslNext = + deployProxyToProxyCallAContractAndAssert(proxyContract, dataInputToCallProxyToProxyThatReverts, transactionAssertions); + + DslParser parserNext = new DslParser(dslNext); + processor.processCommands(parserNext); + Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenAnInternalTxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequential() throws DslProcessorException { + World parallel = createWorld(0); + WorldDslProcessor processor = new WorldDslProcessor(parallel); + String dsl = deployProxyTo(PrecompiledContracts.BRIDGE_ADDR); + DslParser parser = new DslParser(dsl); + processor.processCommands(parser); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + + RskAddress proxyContract = new RskAddress(HashUtil.calcNewAddr(parallel.getAccountByName("acc1").getAddress().getBytes(), BigInteger.ZERO.toByteArray())); + String dataInputToCallProxyToProxy = "688c62c500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024e674f5e8000000000000000000000000000000000000000000000000000000000100000600000000000000000000000000000000000000000000000000000000"; + String transactionAssertions = "assert_tx_success tx02\n" + + "\n"; + + String dslNext = + deployProxyToProxyCallAContractAndAssert(proxyContract, dataInputToCallProxyToProxy, transactionAssertions); + + DslParser parserNext = new DslParser(dslNext); + processor.processCommands(parserNext); + Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(FAILED_STATUS, parallel.getTransactionReceiptByName("tx03").getStatus()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + private static String deployProxyToProxyCallAContractAndAssert(RskAddress proxyContract, String dataInputToCallProxyToProxyThatReverts, String transactionAssertions) { + return "transaction_build tx02\n" + + " sender acc1\n" + + " receiverAddress 00\n" + + " data " + PROXY_TO_PROXY_SMART_CONTRACT_BYTECODE + proxyContract + "\n" + + " nonce 1\n" + + " gas 1200000\n" + + " build\n" + + "\n" + + "transaction_build tx03\n" + + " sender acc1\n" + + " contract tx02\n" + + " data " + dataInputToCallProxyToProxyThatReverts + "\n" + + " nonce 2\n" + + " gas 2500000\n" + + " build\n" + + "\n" + + "block_build b02\n" + + " parent b01\n" + + " transactions tx02 tx03\n" + + " build\n" + + "\n" + + "block_connect b02\n" + + "\n" + + "assert_best b02\n" + + transactionAssertions; + } + + private static String deployProxyTo(RskAddress address) { + return "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress 00\n" + + " data " + PROXY_SMART_CONTRACT_BYTECODE + address + "\n" + + " gas 1200000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " transactions tx01\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "\n"; + } } \ No newline at end of file diff --git a/rskj-core/src/test/java/org/ethereum/vm/VMTest.java b/rskj-core/src/test/java/org/ethereum/vm/VMTest.java index 3a1af7de87d..a718de0840e 100644 --- a/rskj-core/src/test/java/org/ethereum/vm/VMTest.java +++ b/rskj-core/src/test/java/org/ethereum/vm/VMTest.java @@ -3356,6 +3356,50 @@ void testScriptVersion3() { } } + @Test + void whenProgramIsInitializedPrecompiledCalledShouldBeFalse() { + Program program = getProgram(new byte[]{}); + Assertions.assertFalse(program.precompiledContractHasBeenCalled()); + } + + @Test + void ifATxCallsAPrecompiledContractPrecompiledContractHasBeenCalledShouldBeTrue() { + program = getProgram(compile("PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH1 0x01" + + " PUSH1 0x00" + + " PUSH1 0x01" + + " PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH20 0x" + PrecompiledContracts.IDENTITY_ADDR_STR + + " PUSH4 0x005B8D80" + + " CALL" + )); + vm.steps(program, Long.MAX_VALUE); + Assertions.assertTrue(program.precompiledContractHasBeenCalled()); + Assertions.assertFalse(program.getResult().isRevert()); + } + + @Test + void ifATxCallsANonPrecompiledContractPrecompiledContractHasBeenCalledShouldBeFalse() { + invoke = new ProgramInvokeMockImpl(compile("PUSH1 0x01 PUSH1 0x02 SUB"), null); + + program = getProgram(compile("PUSH1 0x00" + + " PUSH1 0x00" + + " PUSH1 0x01" + //out size + " PUSH1 0x00" + //out off + " PUSH1 0x01" + //in size + " PUSH1 0x00" + //in off + " PUSH1 0x00" + + " PUSH20 0x" + invoke.getContractAddress() + + " PUSH4 0x005B8D80" + + " CALL" + )); + vm.steps(program, Long.MAX_VALUE); + Assertions.assertFalse(program.precompiledContractHasBeenCalled()); + Assertions.assertFalse(program.getResult().isRevert()); + } + private VM getSubject() { return new VM(vmConfig, precompiledContracts); } From 03535320299bb825616be38410460ad18dfd7867 Mon Sep 17 00:00:00 2001 From: julianlen Date: Wed, 12 Apr 2023 10:39:47 -0300 Subject: [PATCH 31/89] The gas limit provided to the TransactionHandler is now divided into two parameters: sequentialSublistGasLimit and parallelSublistGasLimit. To implement this change, the BlockExecutor now passes blockGasLimit/2 as parameters when creating the TransactionHandler. However, this caused some tests to fail, as they were using more gas than necessary. To address this issue, the tests were modified to use a more appropriate amount of gas. (#2010) --- .../java/co/rsk/core/bc/BlockExecutor.java | 3 +- .../bc/ParallelizeTransactionHandler.java | 6 +- .../co/rsk/core/bc/BlockExecutorTest.java | 62 +++++++++---------- .../bc/ParallelizeTransactionHandlerTest.java | 14 ++--- .../parallel/ParallelExecutionStateTest.java | 54 ++++++++-------- .../resources/dsl/blake2b/eip_152_example.txt | 4 +- .../resources/dsl/codeSizeAfterSuicide.txt | 2 +- .../contract_nested_abi_calls.txt | 6 +- .../contract_nested_interface_calls.txt | 6 +- .../src/test/resources/dsl/contracts07.txt | 2 +- rskj-core/src/test/resources/dsl/create02.txt | 2 +- .../test/resources/dsl/createAfterSuicide.txt | 2 +- .../eth_module/estimateGas/callWithValue.txt | 4 +- .../callWithValuePlusSstoreRefund.txt | 4 +- .../dsl/eth_module/estimateGas/gasCap.txt | 2 +- .../estimateGas/nestedCallsWithValue.txt | 12 ++-- .../nestedCallsWithValueAndStorageRefund.txt | 12 ++-- ...CallsWithValueStorageRefundAndFixedGas.txt | 12 ++-- .../subsequentCallWithValueCase1.txt | 14 ++--- .../subsequentCallWithValueCase2.txt | 18 +++--- .../eth_module/estimateGas/updateStorage.txt | 6 +- .../dsl/eth_module/revert_reason.txt | 3 +- .../extcodehash_address_does_not_exist.txt | 6 +- ...dehash_address_exists_but_not_contract.txt | 6 +- .../extcodehash_with_cache_empty_deploy.txt | 8 +-- ...extcodehash_without_cache_empty_deploy.txt | 10 +-- rskj-core/src/test/resources/dsl/logs01.txt | 2 +- .../src/test/resources/dsl/recursive01.txt | 4 +- .../src/test/resources/dsl/recursive02.txt | 4 +- 29 files changed, 146 insertions(+), 144 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 2def3269ea8..88dc4d763c0 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -593,7 +593,8 @@ private BlockResult executeForMiningAfterRSKIP144( int txindex = 0; - ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) Constants.getTransactionExecutionThreads(), GasCost.toGas(block.getGasLimit())); + long sublistGasLimit = GasCost.toGas(block.getGasLimit()) / 2; + ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) Constants.getTransactionExecutionThreads(), sublistGasLimit, sublistGasLimit); for (Transaction tx : transactionsList) { loggingApplyBlockToTx(block, i); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index f15941af371..898c06f9ba8 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -31,15 +31,15 @@ public class ParallelizeTransactionHandler { private final Map sublistOfSender; private final ArrayList sublists; - public ParallelizeTransactionHandler(short numberOfSublists, long sublistGasLimit) { + public ParallelizeTransactionHandler(short numberOfSublists, long parallelSublistGasLimit, long sequentialSublistGasLimit) { this.sublistOfSender = new HashMap<>(); this.sublistsHavingWrittenToKey = new HashMap<>(); this.sublistsHavingReadFromKey = new HashMap<>(); this.sublists = new ArrayList<>(); for (short i = 0; i < numberOfSublists; i++){ - this.sublists.add(new TransactionSublist(sublistGasLimit, false)); + this.sublists.add(new TransactionSublist(parallelSublistGasLimit, false)); } - this.sublists.add(new TransactionSublist(sublistGasLimit, true)); + this.sublists.add(new TransactionSublist(sequentialSublistGasLimit, true)); } public Optional addTransaction(Transaction tx, Set newReadKeys, Set newWrittenKeys, long gasUsedByTx) { diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index 9dde6ae7a3e..aaba1bf948e 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -489,17 +489,15 @@ void executeSequentiallyATransactionAndGasShouldBeSubtractedCorrectly(boolean ac doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); - short[] expectedEdges = new short[]{1}; Block parent = blockchain.getBestBlock(); long expectedAccumulatedGas = 21000L; Block block = getBlockWithNIndependentTransactions(1, BigInteger.valueOf(expectedAccumulatedGas), false); - List txs = block.getTransactionsList(); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); - Assertions.assertEquals(txs, blockResult.getExecutedTransactions()); + Assertions.assertEquals(block.getTransactionsList(), blockResult.getExecutedTransactions()); Assertions.assertEquals(expectedAccumulatedGas, blockResult.getGasUsed()); - Assertions.assertArrayEquals(expectedEdges, blockResult.getTxEdges()); + Assertions.assertArrayEquals(new short[]{1}, blockResult.getTxEdges()); List transactionReceipts = blockResult.getTransactionReceipts(); for (TransactionReceipt receipt: transactionReceipts) { @@ -515,31 +513,32 @@ void executeSequentiallyTenIndependentTxsAndThemShouldGoInBothSublists(boolean a } doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); - long expectedGasUsed = 0L; - long expectedAccumulatedGas = 21000L; - int txNumber = 12; + long txGasLimit = 21000L; short[] expectedEdges = new short[]{3, 6, 9, 12}; Block parent = blockchain.getBestBlock(); - Block block = getBlockWithNIndependentTransactions(txNumber, BigInteger.valueOf(expectedAccumulatedGas), false); + int numberOfTxs = 12; + + Block block = getBlockWithNIndependentTransactions(numberOfTxs, BigInteger.valueOf(txGasLimit), false); List txs = block.getTransactionsList(); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); Assertions.assertEquals(txs.size(), blockResult.getExecutedTransactions().size()); Assertions.assertTrue(txs.containsAll(blockResult.getExecutedTransactions())); Assertions.assertArrayEquals(expectedEdges, blockResult.getTxEdges()); - Assertions.assertEquals(expectedAccumulatedGas*txNumber, blockResult.getGasUsed()); + Assertions.assertEquals(txGasLimit * numberOfTxs, blockResult.getGasUsed()); List transactionReceipts = blockResult.getTransactionReceipts(); long accumulatedGasUsed = 0L; short i = 0; short edgeIndex = 0; for (TransactionReceipt receipt: transactionReceipts) { - if ((edgeIndex < expectedEdges.length) && (i == expectedEdges[edgeIndex])) { + boolean isFromADifferentSublist = (edgeIndex < expectedEdges.length) && (i == expectedEdges[edgeIndex]); + if (isFromADifferentSublist) { edgeIndex++; - accumulatedGasUsed = expectedGasUsed; + accumulatedGasUsed = 0L; } - accumulatedGasUsed += expectedAccumulatedGas; + accumulatedGasUsed += txGasLimit; Assertions.assertEquals(accumulatedGasUsed, GasCost.toGas(receipt.getCumulativeGas())); i++; } @@ -549,19 +548,18 @@ void executeSequentiallyTenIndependentTxsAndThemShouldGoInBothSublists(boolean a @ParameterizedTest @ValueSource(booleans = {true, false}) - void executeBigIndependentTxsSequentiallyTheLastOneShouldGoToSequential(boolean activeRskip144) { + void whenParallelSublistsAreFullTheLastTxShouldGoToSequential(boolean activeRskip144) { if (!activeRskip144) { return; } doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); - long blockGasLimit = GasCost.toGas(parent.getGasLimit()); int gasLimit = 21000; - int transactionNumber = (int) (blockGasLimit/gasLimit); - short[] expectedEdges = new short[]{(short) transactionNumber, (short) (transactionNumber*2), (short) (transactionNumber*3), (short) (transactionNumber*4)}; + int numberOfTransactions = (int) (getSublistGaslimit(parent)/gasLimit); + short[] expectedEdges = new short[]{(short) numberOfTransactions, (short) (numberOfTransactions*2), (short) (numberOfTransactions*3), (short) (numberOfTransactions*4)}; int transactionsInSequential = 1; - Block block = getBlockWithNIndependentTransactions(transactionNumber * Constants.getTransactionExecutionThreads() + transactionsInSequential, BigInteger.valueOf(gasLimit), false); + Block block = getBlockWithNIndependentTransactions(numberOfTransactions * Constants.getTransactionExecutionThreads() + transactionsInSequential, BigInteger.valueOf(gasLimit), false); List transactionsList = block.getTransactionsList(); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); @@ -596,15 +594,14 @@ void executeATxInSequentialAndBlockResultShouldTrackTheGasUsedInTheBlock(boolean doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); - long blockGasLimit = GasCost.toGas(parent.getGasLimit()); int gasLimit = 21000; - int transactionNumberToFillParallelSublist = (int) (blockGasLimit / gasLimit); + int transactionNumberToFillParallelSublist = (int) (getSublistGaslimit(parent) / gasLimit); int transactionsInSequential = 1; int totalTxsNumber = transactionNumberToFillParallelSublist * Constants.getTransactionExecutionThreads() + transactionsInSequential; Block block = getBlockWithNIndependentTransactions(totalTxsNumber, BigInteger.valueOf(gasLimit), false); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); - Assertions.assertEquals(gasLimit*totalTxsNumber, blockResult.getGasUsed()); + Assertions.assertEquals(gasLimit * totalTxsNumber, blockResult.getGasUsed()); } @ParameterizedTest @@ -616,14 +613,13 @@ void withTheSublistsFullTheLastTransactionShouldNotFit(boolean activeRskip144) { doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); - long blockGasLimit = GasCost.toGas(parent.getGasLimit()); int gasLimit = 21000; - int transactionNumberToFillParallelSublist = (int) (blockGasLimit / gasLimit); + int transactionNumberToFillParallelSublist = (int) (getSublistGaslimit(parent) / gasLimit); int totalNumberOfSublists = Constants.getTransactionExecutionThreads() + 1; int totalTxs = (transactionNumberToFillParallelSublist) * totalNumberOfSublists + 1; Block block = getBlockWithNIndependentTransactions(totalTxs, BigInteger.valueOf(gasLimit), false); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); - Assertions.assertEquals(totalTxs, blockResult.getExecutedTransactions().size() + 1); + Assertions.assertEquals(totalTxs - 1, blockResult.getExecutedTransactions().size()); } @ParameterizedTest @@ -636,12 +632,12 @@ void withSequentialSublistFullRemascTxShouldFit(boolean activeRskip144) { doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); - long blockGasLimit = GasCost.toGas(parent.getGasLimit()); int gasLimit = 21000; - int transactionNumberToFillASublist = (int) (blockGasLimit / gasLimit); + int transactionNumberToFillASublist = (int) (getSublistGaslimit(parent) / gasLimit); int totalNumberOfSublists = Constants.getTransactionExecutionThreads() + 1; - int expectedNumberOfTx = transactionNumberToFillASublist* totalNumberOfSublists + 1; - Block block = getBlockWithNIndependentTransactions(transactionNumberToFillASublist * totalNumberOfSublists, BigInteger.valueOf(gasLimit), true); + int expectedNumberOfTx = transactionNumberToFillASublist * totalNumberOfSublists + 1; + + Block block = getBlockWithNIndependentTransactions(expectedNumberOfTx, BigInteger.valueOf(gasLimit), true); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); Assertions.assertEquals(expectedNumberOfTx, blockResult.getExecutedTransactions().size()); } @@ -944,6 +940,10 @@ void invalidBlockBadLogsBloom(boolean activeRskip144) { Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); } + private static long getSublistGaslimit(Block parent) { + return GasCost.toGas(parent.getGasLimit()) / 2; + } + private static TestObjects generateBlockWithOneTransaction(Boolean activeRskip144, boolean rskip126IsActive) { TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Repository repository = new MutableRepository(trieStore, new Trie(trieStore)); @@ -1152,8 +1152,8 @@ private Block getBlockWithTenTransactions(short[] edges) { ); } - private Block getBlockWithNIndependentTransactions(int txNumber, BigInteger txGasLimit, boolean withRemasc) { - int nAccounts = txNumber * 2; + private Block getBlockWithNIndependentTransactions(int numberOfTxs, BigInteger txGasLimit, boolean withRemasc) { + int nAccounts = numberOfTxs * 2; Repository track = repository.startTracking(); List accounts = new LinkedList<>(); @@ -1166,12 +1166,12 @@ private Block getBlockWithNIndependentTransactions(int txNumber, BigInteger txGa List txs = new LinkedList<>(); - for (int i = 0; i < txNumber; i++) { + for (int i = 0; i < numberOfTxs; i++) { Transaction tx = Transaction.builder() .nonce(BigInteger.ZERO) .gasPrice(BigInteger.ONE) .gasLimit(txGasLimit) - .destination(accounts.get(i + txNumber).getAddress()) + .destination(accounts.get(i + numberOfTxs).getAddress()) .chainId(CONFIG.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index add8373ced6..fb3cc3ccdbc 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -54,17 +54,17 @@ public void setup() { Account sender5 = new AccountBuilder().name("sender5").build(); byte[] aKey = {1, 2, 3}; byte[] aDifferentKey = {1, 2, 3, 4}; - int blockGasLimit = 6800000; + int sublistGasLimit = 3400000; long gasUsedByTx = 16000; - long biggestGasLimitPossibleInSublists = blockGasLimit - 1; + long biggestGasLimitPossibleInSublists = sublistGasLimit - 1; aWrappedKey = new ByteArrayWrapper(aKey); sublists = 2; sequentialSublistNumber = sublists; - handler = new ParallelizeTransactionHandler(sublists, blockGasLimit); - tx = new TransactionBuilder().nonce(1).sender(sender).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx+1)).build(); - tx2 = new TransactionBuilder().nonce(1).sender(sender2).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx+2)).build(); - tx3 = new TransactionBuilder().nonce(1).sender(sender3).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx+3)).build(); + handler = new ParallelizeTransactionHandler(sublists, sublistGasLimit, sublistGasLimit); + tx = new TransactionBuilder().nonce(1).sender(sender).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); + tx2 = new TransactionBuilder().nonce(1).sender(sender2).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); + tx3 = new TransactionBuilder().nonce(1).sender(sender3).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); bigTx = new TransactionBuilder().nonce(1).sender(sender4).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInSublists)).value(BigInteger.valueOf(1)).build(); bigTx2 = new TransactionBuilder().nonce(1).sender(sender5).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInSublists)).value(BigInteger.valueOf(1)).build(); aDifferentWrappedKey = new ByteArrayWrapper(aDifferentKey); @@ -859,7 +859,7 @@ void writeAfterWriteToSequentialForOutOfGasShouldGoToSequential() { AccountBuilder accountBuilder = new AccountBuilder(); - ParallelizeTransactionHandler handler = new ParallelizeTransactionHandler((short) 2, 1000); + ParallelizeTransactionHandler handler = new ParallelizeTransactionHandler((short) 2, 1000, 1000); // write X with 800 handler.addTransaction( diff --git a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java index c5f01a51bc1..d1e5239307e 100644 --- a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java +++ b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java @@ -155,8 +155,8 @@ private String getProxyCreationCode (String address) { private final String readDataWithRevert = "e2033a13000000000000000000000000000000000000000000000000000000000000000a"; // updateWithRevert(10) private final String updateByTenWithRevert = "1b892f87000000000000000000000000000000000000000000000000000000000000000a"; - // wasteGas(2000000, 0, 0) - private final String wasteTwoMillionGas = "34b0919300000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + // wasteGas(1000000, 0, 0) + private final String wasteOneMillionGas = "34b0919300000000000000000000000000000000000000000000000000000000000f4d4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; // wasteGas(100000, 0, 0) private final String wasteHundredThousandGas = "34b0919300000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; // wasteGas(100000, 1, 1) @@ -165,12 +165,12 @@ private String getProxyCreationCode (String address) { private final String writeToY = "34b0919300000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002"; // wasteGas(100000, 1, 2) private final String writeToXAndY = "34b0919300000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"; - // wasteGas(2000000, 1, 1) - private final String writeToXWastingGas = "34b0919300000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"; - // wasteGas(2000000, 2, 2) - private final String writeToYWastingGas = "34b0919300000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002"; - // wasteGas(2000000, 1, 2) - private final String writeToXAndYWastingGas = "34b0919300000000000000000000000000000000000000000000000000000000001e848000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"; + // wasteGas(1000000, 1, 1) + private final String writeToXWastingGas = "34b0919300000000000000000000000000000000000000000000000000000000000f4d4000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"; + // wasteGas(1000000, 2, 2) + private final String writeToYWastingGas = "34b0919300000000000000000000000000000000000000000000000000000000000f4d4000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002"; + // wasteGas(1000000, 1, 2) + private final String writeToXAndYWastingGas = "34b0919300000000000000000000000000000000000000000000000000000000000f4d4000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"; // substrings for dsl text private final String createThreeAccounts = "account_new acc1 10000000\n" + @@ -618,15 +618,15 @@ void useSequentialForGas() throws DslProcessorException { World parallel = this.createWorldAndProcess(skeleton("transaction_build tx02\n" + " sender acc1\n" + " contract tx01\n" + - " data " + wasteTwoMillionGas + "\n" + - " gas 2700000\n" + + " data " + wasteOneMillionGas + "\n" + + " gas 1500000\n" + " build\n" + "\n" + "transaction_build tx03\n" + " sender acc2\n" + " contract tx01\n" + - " data " + wasteTwoMillionGas + "\n" + - " gas 2500000\n" + + " data " + wasteOneMillionGas + "\n" + + " gas 1500000\n" + " build\n" + "\n", true), 0); @@ -643,16 +643,16 @@ void useSequentialForCollisionWithSequential() throws DslProcessorException { "transaction_build tx02\n" + " sender acc1\n" + " contract tx01\n" + - " data " + wasteTwoMillionGas + "\n" + - " gas 2500000\n" + + " data " + wasteOneMillionGas + "\n" + + " gas 1500000\n" + " nonce 0\n" + " build\n" + "\n" + "transaction_build tx03\n" + " sender acc1\n" + " contract tx01\n" + - " data " + wasteTwoMillionGas + "\n" + - " gas 2500000\n" + + " data " + wasteOneMillionGas + "\n" + + " gas 1500000\n" + " nonce 1\n" + " build\n" + "\n" + // goes to sequential @@ -738,21 +738,21 @@ void useSequentialForCollisionWithTwoParallelWithoutGas() throws DslProcessorExc " sender acc1\n" + " contract tx01\n" + " data " + writeToXWastingGas + "\n" + - " gas 2500000\n" + + " gas 1500000\n" + " build\n" + "\n" + "transaction_build tx03\n" + " sender acc2\n" + " contract tx01\n" + " data " + writeToYWastingGas + "\n" + - " gas 2500000\n" + + " gas 1500000\n" + " build\n" + "\n" + // goes to sequential "transaction_build tx04\n" + " sender acc4\n" + " contract tx01\n" + " data " + writeToXAndYWastingGas + "\n" + - " gas 2500000\n" + + " gas 1500000\n" + " build\n" + "\n" + "block_build b02\n" + @@ -784,14 +784,14 @@ void useSequentialForCollisionWithSequentialAndParallel() throws DslProcessorExc " sender acc1\n" + " contract tx01\n" + " data " + writeToXWastingGas + "\n" + - " gas 2500000\n" + + " gas 1500000\n" + " build\n" + "\n" + "transaction_build tx03\n" + " sender acc1\n" + " contract tx01\n" + " data " + writeToXWastingGas + "\n" + - " gas 2500000\n" + + " gas 1500000\n" + " nonce 1\n" + " build\n" + "\n" + // goes to sequential @@ -837,7 +837,7 @@ void whenATxCallsAPrecompiledItShouldGoToSequential() throws DslProcessorExcepti " sender acc1\n" + " receiverAddress " + PrecompiledContracts.IDENTITY_ADDR +"\n"+ " data " + "1234" + "\n" + - " gas 2500000\n" + + " gas 1000000\n" + " build\n" + "\n" + "block_build b01\n" + @@ -863,7 +863,7 @@ void whenATxSendsValueToAPrecompiledItShouldGoToSequential() throws DslProcessor " sender acc1\n" + " receiverAddress " + PrecompiledContracts.IDENTITY_ADDR +"\n"+ " value 1000\n" + - " gas 2500000\n" + + " gas 1000000\n" + " build\n" + "\n" + "block_build b01\n" + @@ -890,7 +890,7 @@ void whenATxCallsAPrecompiledThatThrowsAnExceptionShouldGoToSequential() throws " sender acc1\n" + " receiverAddress " + PrecompiledContracts.BRIDGE_ADDR +"\n"+ " data " + "e674f5e80000000000000000000000000000000000000000000000000000000001000006" + "\n" + - " gas 2500000\n" + + " gas 1000000\n" + " build\n" + "\n" + "block_build b01\n" + @@ -924,7 +924,7 @@ void whenATxCallsAContractThatCallsAPrecompiledShouldGoToSequential() throws Dsl " contract tx01\n" + " data " + "688c62c5000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000145b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000" + "\n" + " nonce 1\n" + - " gas 2500000\n" + + " gas 1000000\n" + " build\n" + "\n" + "block_build b01\n" + @@ -959,7 +959,7 @@ void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequential() " contract tx01\n" + " data " + "688c62c500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024e674f5e8000000000000000000000000000000000000000000000000000000000100000600000000000000000000000000000000000000000000000000000000" + "\n" + " nonce 1\n" + - " gas 2500000\n" + + " gas 1000000\n" + " build\n" + "\n" + "block_build b01\n" + @@ -1043,7 +1043,7 @@ private static String deployProxyToProxyCallAContractAndAssert(RskAddress proxyC " contract tx02\n" + " data " + dataInputToCallProxyToProxyThatReverts + "\n" + " nonce 2\n" + - " gas 2500000\n" + + " gas 1000000\n" + " build\n" + "\n" + "block_build b02\n" + diff --git a/rskj-core/src/test/resources/dsl/blake2b/eip_152_example.txt b/rskj-core/src/test/resources/dsl/blake2b/eip_152_example.txt index e6043a6fb16..511af853320 100644 --- a/rskj-core/src/test/resources/dsl/blake2b/eip_152_example.txt +++ b/rskj-core/src/test/resources/dsl/blake2b/eip_152_example.txt @@ -69,7 +69,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b50610693806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806382fe1c9b1461003b578063fc75ac4714610045575b600080fd5b61004361008b565b005b61004d6101f6565b6040518082600260200280838360005b8381101561007857808201518184015260208101905061005d565b5050505090500191505060405180910390f35b60007fba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1905060007f7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd400992390506100dd610591565b6100e56101f6565b9050600083826000600281106100f757fe5b60200201511490506000838360016002811061010f57fe5b60200201511490508161016d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603381526020018061062c6033913960400191505060405180910390fd5b806101c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260348152602001806105f86034913960400191505060405180910390fd5b7f28ac248877e8517478dfc963b8e1f727f15b62bc721ea3aa078bd81873f8a88160405160405180910390a15050505050565b6101fe610591565b6000600c905061020c610591565b7f48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa58160006002811061023a57fe5b6020020181815250507fd182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b8160016002811061027157fe5b6020020181815250506102826105b3565b7f6162630000000000000000000000000000000000000000000000000000000000816000600481106102b057fe5b6020020181815250506000816001600481106102c857fe5b6020020181815250506000816002600481106102e057fe5b6020020181815250506000816003600481106102f857fe5b6020020181815250506103096105d5565b7f03000000000000000000000000000000000000000000000000000000000000008160006002811061033757fe5b602002019077ffffffffffffffffffffffffffffffffffffffffffffffff1916908177ffffffffffffffffffffffffffffffffffffffffffffffff19168152505060008160016002811061038757fe5b602002019077ffffffffffffffffffffffffffffffffffffffffffffffff1916908177ffffffffffffffffffffffffffffffffffffffffffffffff1916815250506000600190506103db85858585856103e5565b9550505050505090565b6103ed610591565b6103f5610591565b6060878760006002811061040557fe5b60200201518860016002811061041757fe5b60200201518860006004811061042957fe5b60200201518960016004811061043b57fe5b60200201518a60026004811061044d57fe5b60200201518b60036004811061045f57fe5b60200201518b60006002811061047157fe5b60200201518c60016002811061048357fe5b60200201518c604051602001808b63ffffffff1663ffffffff1660e01b81526004018a81526020018981526020018881526020018781526020018681526020018581526020018477ffffffffffffffffffffffffffffffffffffffffffffffff191677ffffffffffffffffffffffffffffffffffffffffffffffff191681526008018377ffffffffffffffffffffffffffffffffffffffffffffffff191677ffffffffffffffffffffffffffffffffffffffffffffffff19168152600801821515151560f81b81526001019a5050505050505050505050604051602081830303815290604052905060408260d5602084016009600019fa61058357600080fd5b819250505095945050505050565b6040518060400160405280600290602082028038833980820191505090505090565b6040518060800160405280600490602082028038833980820191505090505090565b604051806040016040528060029060208202803883398082019150509050509056fe626c616b6532622e663a207365636f6e642076616c756520646f65736e2774206d617463682065787065637465642076616c7565626c616b6532622e663a2066697273742076616c756520646f65736e2774206d617463682065787065637465642076616c7565a265627a7a72315820f6f4c217e94a3b7f87cedfc47c7b5b9a9ab1b048c36a62bcba4d0f9c20c6fc6264736f6c63430005100032 - gas 2000000 + gas 1000000 build # invoke runTest() @@ -81,7 +81,7 @@ transaction_build tx02 value 0 #data 12ca565c data 82fe1c9b - gas 2000000000 + gas 1000000000 build block_build b01 diff --git a/rskj-core/src/test/resources/dsl/codeSizeAfterSuicide.txt b/rskj-core/src/test/resources/dsl/codeSizeAfterSuicide.txt index 63ef04301f7..6453e86541e 100644 --- a/rskj-core/src/test/resources/dsl/codeSizeAfterSuicide.txt +++ b/rskj-core/src/test/resources/dsl/codeSizeAfterSuicide.txt @@ -6,7 +6,7 @@ transaction_build createCreatorAndRun nonce 0 value 0 data 608060405234801561001057600080fd5b50606060405180602001610023906100ba565b6020820181038252601f19601f82011660405250905060008090506000818351602085016000f5905060008190508073ffffffffffffffffffffffffffffffffffffffff1663c04062266040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561009957600080fd5b505af11580156100ad573d6000803e3d6000fd5b50505050505050506100c7565b6101768061011383390190565b603e806100d56000396000f3fe6080604052600080fdfea265627a7a72305820a7e17e7e5eb7b65cf24fc5474caad0c255ba2c294b137214a1ec5ec7ddd7548364736f6c63430005090032608060405234801561001057600080fd5b50610156806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c040622614602d575b600080fd5b60336035565b005b6060604051806020016045906073565b6020820181038252601f19601f82011660405250905060008090506000818351602085016000f59050505050565b60a2806100808339019056fe6080604052348015600f57600080fd5b5060848061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806383197ef014602d575b600080fd5b60336035565b005b600073ffffffffffffffffffffffffffffffffffffffff16fffea265627a7a723058201116e4763485ea3809dcd4a200f7c55712ae437dab27c860563fe1c4884f2a3664736f6c63430005090032a265627a7a7230582038b99a100f4e2adb982bfa08a60406860f9ea5d73b082a02670cf258bfc89e0264736f6c63430005090032 - gas 2000000 + gas 1000000 build transaction_build runSuicider diff --git a/rskj-core/src/test/resources/dsl/contract_call/contract_nested_abi_calls.txt b/rskj-core/src/test/resources/dsl/contract_call/contract_nested_abi_calls.txt index edb8e699a7c..591f17c1980 100644 --- a/rskj-core/src/test/resources/dsl/contract_call/contract_nested_abi_calls.txt +++ b/rskj-core/src/test/resources/dsl/contract_call/contract_nested_abi_calls.txt @@ -73,7 +73,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 6080604052600b60005534801561001557600080fd5b5061011d806100256000396000f3fe608060405260043610601c5760003560e01c8063d96a094a146021575b600080fd5b604a60048036036020811015603557600080fd5b81019080803590602001909291905050506060565b6040518082815260200191505060405180910390f35b600080821160d6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e656761746976652076616c75652e000000000000000000000000000000000081525060200191505060405180910390fd5b81600081905550600054905091905056fea26469706673582212209d6bb30c4cdfb29525a661c56652bb5676e62de7e1fa11da6baee0122871d73764736f6c63430006010033 - gas 2000000 + gas 1000000 build # Deploy ContractB (address: 56aa252dd82173789984fa164ee26ce2da9336ff) @@ -86,7 +86,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 6080604052600b60015534801561001557600080fd5b506040516102b73803806102b78339818101604052602081101561003857600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061021e806100996000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063d96a094a14610030575b600080fd5b61005c6004803603602081101561004657600080fd5b8101908080359060200190929190505050610072565b6040518082815260200191505060405180910390f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1682604051602401808281526020019150506040516020818303038152906040527fd96a094a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b6020831061016d578051825260208201915060208101905060208303925061014a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146101cf576040519150601f19603f3d011682016040523d82523d6000602084013e6101d4565b606091505b50505081600181905550600154905091905056fea264697066735822122020ecc8df14a8b94f9ab73c0cdd1558fc3c9d92e7d0b6ce196cc7c2972250ee6c64736f6c634300060100330000000000000000000000006252703f5ba322ec64d3ac45e56241b7d9e481ad - gas 2000000 + gas 1000000 build # Deploy ContractA (address: 27444fbce96cb2d27b94e116d1506d7739c05862) @@ -99,7 +99,7 @@ transaction_build tx03 receiverAddress 00 value 0 data 6080604052600b60015534801561001557600080fd5b506040516102b73803806102b78339818101604052602081101561003857600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061021e806100996000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063d96a094a14610030575b600080fd5b61005c6004803603602081101561004657600080fd5b8101908080359060200190929190505050610072565b6040518082815260200191505060405180910390f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1682604051602401808281526020019150506040516020818303038152906040527fd96a094a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b6020831061016d578051825260208201915060208101905060208303925061014a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146101cf576040519150601f19603f3d011682016040523d82523d6000602084013e6101d4565b606091505b50505081600181905550600154905091905056fea26469706673582212200990ca360d90787a3bd0042e6267b7476cf8decb859f92a92c6b6b109d316ade64736f6c6343000601003300000000000000000000000056aa252dd82173789984fa164ee26ce2da9336ff - gas 2000000 + gas 1000000 build block_build b01 diff --git a/rskj-core/src/test/resources/dsl/contract_call/contract_nested_interface_calls.txt b/rskj-core/src/test/resources/dsl/contract_call/contract_nested_interface_calls.txt index c09e08e3a31..b86537696a0 100644 --- a/rskj-core/src/test/resources/dsl/contract_call/contract_nested_interface_calls.txt +++ b/rskj-core/src/test/resources/dsl/contract_call/contract_nested_interface_calls.txt @@ -67,7 +67,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 6080604052600b60005534801561001557600080fd5b5061015f806100256000396000f3fe6080604052600436106100295760003560e01c80630dbe671f1461002e578063d96a094a14610059575b600080fd5b34801561003a57600080fd5b5061004361009b565b6040518082815260200191505060405180910390f35b6100856004803603602081101561006f57600080fd5b81019080803590602001909291905050506100a1565b6040518082815260200191505060405180910390f35b60005481565b6000808211610118576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e656761746976652076616c75652e000000000000000000000000000000000081525060200191505060405180910390fd5b81600081905550600054905091905056fea264697066735822122059b4c86b4cc24c6a58060fc78f3e3bae4bc889144ec64ad30416d58d549e9c9e64736f6c63430006010033 - gas 2000000 + gas 1000000 build # Deploy ContractB (address: 56aa252dd82173789984fa164ee26ce2da9336ff) @@ -80,7 +80,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 6080604052600b60015534801561001557600080fd5b506040516102163803806102168339818101604052602081101561003857600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061017d806100996000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80630dbe671f1461003b578063d96a094a14610059575b600080fd5b610043610087565b6040518082815260200191505060405180910390f35b6100856004803603602081101561006f57600080fd5b810190808035906020019092919050505061008d565b005b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d96a094a826040518263ffffffff1660e01b815260040180828152602001915050602060405180830381600087803b15801561010157600080fd5b505af1158015610115573d6000803e3d6000fd5b505050506040513d602081101561012b57600080fd5b810190808051906020019092919050505050806001819055505056fea26469706673582212202ea598ad7db9f0f4fd9f2fe977c221997dc15e3bd0bb426c469d882099df084264736f6c634300060100330000000000000000000000006252703f5ba322ec64d3ac45e56241b7d9e481ad - gas 2000000 + gas 1000000 build # Deploy ContractA (address: 27444fbce96cb2d27b94e116d1506d7739c05862) @@ -93,7 +93,7 @@ transaction_build tx03 receiverAddress 00 value 0 data 6080604052600b60015534801561001557600080fd5b506040516101f23803806101f28339818101604052602081101561003857600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050610159806100996000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80630dbe671f1461003b578063d96a094a14610059575b600080fd5b610043610087565b6040518082815260200191505060405180910390f35b6100856004803603602081101561006f57600080fd5b810190808035906020019092919050505061008d565b005b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663d96a094a826040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561010157600080fd5b505af1158015610115573d6000803e3d6000fd5b50505050806001819055505056fea2646970667358221220e01a719553468e6072c3d9c5b718164b705c2e4550a5168446d6fa86d09dc55d64736f6c6343000601003300000000000000000000000056aa252dd82173789984fa164ee26ce2da9336ff - gas 2000000 + gas 1000000 build block_build b01 diff --git a/rskj-core/src/test/resources/dsl/contracts07.txt b/rskj-core/src/test/resources/dsl/contracts07.txt index 54b6bdb4432..77a27de9d03 100644 --- a/rskj-core/src/test/resources/dsl/contracts07.txt +++ b/rskj-core/src/test/resources/dsl/contracts07.txt @@ -50,7 +50,7 @@ transaction_build tx03 contract tx01 # created in tx01 value 0 data 45d9a1ce00000000000000000000000028fdc38c327f4a3bbdf9501fd3a01ac7228c7af700000000000000000000000067bde3af536b093abcee56fbdf2e003453186dc500000000000000000000000000000000000000000000000000000000000001f4 - gas 3000000 + gas 1000000 build block_build b03 diff --git a/rskj-core/src/test/resources/dsl/create02.txt b/rskj-core/src/test/resources/dsl/create02.txt index 3356bf77cbc..1faa17cc676 100644 --- a/rskj-core/src/test/resources/dsl/create02.txt +++ b/rskj-core/src/test/resources/dsl/create02.txt @@ -13,7 +13,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b504361001a61017b565b80828152602001915050604051809103906000f080158015610040573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f80ae3ec8027d0c5d1f3e47fb4bf1d9fc28225e7f4bcb1971b36efb81fe40574d6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663209652556040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15801561012657600080fd5b505af115801561013a573d6000803e3d6000fd5b505050506040513d602081101561015057600080fd5b81019080805190602001909291905050506040518082815260200191505060405180910390a161018b565b6040516101e38061028283390190565b60e9806101996000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806361bc221a146044575b600080fd5b348015604f57600080fd5b5060566098565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a723058209aa0690aab1fdc50c2666446294d07319939d78fbf0060b72ebeb7f23be19c620029608060405234801561001057600080fd5b506040516020806101e383398101806040528101908080519060200190929190505050806000819055507f06acbfb32bcf8383f3b0a768b70ac9ec234ea0f2d3b9c77fa6a2de69b919aad16000546040518082815260200191505060405180910390a150610160806100836000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632096525514610051578063d09de08a1461007c575b600080fd5b34801561005d57600080fd5b50610066610093565b6040518082815260200191505060405180910390f35b34801561008857600080fd5b506100916100d6565b005b60007f1ee041944547858a75ebef916083b6d4f5ae04bea9cd809334469dd07dbf441b6000546040518082815260200191505060405180910390a1600054905090565b600080815460010191905081905550600160026000548115156100f557fe5b061415157f6e61ef44ac2747ff8b84d353a908eb8bd5c3fb118334d57698c5cfc7041196ad6000546040518082815260200191505060405180910390a25600a165627a7a723058205f51e4885fb38fdd8afbd598712eec4c95c6b0d690be063b728631de76bcb2730029 - gas 2000000 + gas 1000000 build block_build b01b diff --git a/rskj-core/src/test/resources/dsl/createAfterSuicide.txt b/rskj-core/src/test/resources/dsl/createAfterSuicide.txt index 27a4ab2b37f..1da8c0cd38a 100644 --- a/rskj-core/src/test/resources/dsl/createAfterSuicide.txt +++ b/rskj-core/src/test/resources/dsl/createAfterSuicide.txt @@ -6,7 +6,7 @@ transaction_build createCreatorAndRun nonce 0 value 0 data 608060405234801561001057600080fd5b50606060405180602001610023906100ba565b6020820181038252601f19601f82011660405250905060008090506000818351602085016000f5905060008190508073ffffffffffffffffffffffffffffffffffffffff1663c04062266040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561009957600080fd5b505af11580156100ad573d6000803e3d6000fd5b50505050505050506100c7565b6101768061011383390190565b603e806100d56000396000f3fe6080604052600080fdfea265627a7a72305820a7e17e7e5eb7b65cf24fc5474caad0c255ba2c294b137214a1ec5ec7ddd7548364736f6c63430005090032608060405234801561001057600080fd5b50610156806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063c040622614602d575b600080fd5b60336035565b005b6060604051806020016045906073565b6020820181038252601f19601f82011660405250905060008090506000818351602085016000f59050505050565b60a2806100808339019056fe6080604052348015600f57600080fd5b5060848061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806383197ef014602d575b600080fd5b60336035565b005b600073ffffffffffffffffffffffffffffffffffffffff16fffea265627a7a723058201116e4763485ea3809dcd4a200f7c55712ae437dab27c860563fe1c4884f2a3664736f6c63430005090032a265627a7a7230582038b99a100f4e2adb982bfa08a60406860f9ea5d73b082a02670cf258bfc89e0264736f6c63430005090032 - gas 2000000 + gas 1000000 build transaction_build runSuicider diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValue.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValue.txt index 7452fa8349b..229975ca8dd 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValue.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValue.txt @@ -21,7 +21,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 6080604052348015600f57600080fd5b5060a98061001e6000396000f3fe608060405260043610601f5760003560e01c8063c3cefd36146022576020565b5b005b6028602a565b005b3073ffffffffffffffffffffffffffffffffffffffff166108fc60019081150290604051600060405180830381858888f193505050501580156070573d6000803e3d6000fd5b5056fea264697066735822122088f249a82f3007a2761278a171ae52bad97182b2fd8cfe635e9f0a7f8082d50864736f6c63430007030033 - gas 2000000 + gas 1000000 build block_build b01 @@ -38,7 +38,7 @@ transaction_build tx02 nonce 1 value 100 data c3cefd36 - gas 2000000 + gas 1000000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValuePlusSstoreRefund.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValuePlusSstoreRefund.txt index d097aecdaa5..69eeaabf81a 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValuePlusSstoreRefund.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValuePlusSstoreRefund.txt @@ -32,7 +32,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b50602a600060016064811061002157fe5b018190555061016e806100356000396000f3fe6080604052600436106100385760003560e01c80635b3f81401461003b5780635e383d211461004557806394b3f5ee1461009457610039565b5b005b6100436100ab565b005b34801561005157600080fd5b5061007e6004803603602081101561006857600080fd5b8101908080359060200190929190505050610109565b6040518082815260200191505060405180910390f35b3480156100a057600080fd5b506100a9610121565b005b6000806001606481106100ba57fe5b01819055503073ffffffffffffffffffffffffffffffffffffffff166108fc60019081150290604051600060405180830381858888f19350505050158015610106573d6000803e3d6000fd5b50565b6000816064811061011657fe5b016000915090505481565b602a600060016064811061013157fe5b018190555056fea26469706673582212204f4296fafbb59e65b8ab38addc7f71380bbcba3f71112e6f4c4304298ed663c864736f6c63430007030033 - gas 2000000 + gas 1000000 build block_build b01 @@ -51,7 +51,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 1 data 5b3f8140 - gas 6800000 + gas 3400000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/gasCap.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/gasCap.txt index 4b4ae942803..d245d282947 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/gasCap.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/gasCap.txt @@ -22,7 +22,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5060a78061001f6000396000f3fe608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806331fe52e8146044575b600080fd5b348015604f57600080fd5b5060566058565b005b60008090505b60005a11156078576000819050508080600101915050605e565b5056fea165627a7a72305820189bbcc31f3f86bceb6648ec2f3ce61273ec572330bae05a5890d64703648c5d0029 - gas 2000000 + gas 1000000 build block_build b01 diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValue.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValue.txt index 6ed781f8fb8..e5deec4c7f2 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValue.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValue.txt @@ -41,7 +41,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405260008060146101000a81548160ff02191690831515021790555034801561002a57600080fd5b5061023f8061003a6000396000f3fe6080604052600436106100385760003560e01c80633aecee241461003b578063ba26473514610052578063fb60f709146100a357610039565b5b005b34801561004757600080fd5b506100506100ad565b005b34801561005e57600080fd5b506100a16004803603602081101561007557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506100ca565b005b6100ab61010d565b005b6001600060146101000a81548160ff021916908315150217905550565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600060149054906101000a900460ff166101da5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fb60f709346127105a03906040518363ffffffff1660e01b81526004016000604051808303818589803b15801561018f57600080fd5b5088f11580156101a3573d6000803e3d6000fd5b5050505050507fde0f1ac890b93f05f29eb21d0298f6bf3b9954213c196fefecdb445b4ed047a560405160405180910390a1610207565b7faf10f11658b97bcc0ed656fdfe4e20ca91e4a2a150f363233da11ec1d4898a4760405160405180910390a15b56fea2646970667358221220a49fa0e8a4947075a1ccbc823ca128cf2b35d6dc594337da9924f07596c2285b64736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx02 @@ -50,7 +50,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 608060405260008060146101000a81548160ff02191690831515021790555034801561002a57600080fd5b5061023f8061003a6000396000f3fe6080604052600436106100385760003560e01c80633aecee241461003b578063ba26473514610052578063fb60f709146100a357610039565b5b005b34801561004757600080fd5b506100506100ad565b005b34801561005e57600080fd5b506100a16004803603602081101561007557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506100ca565b005b6100ab61010d565b005b6001600060146101000a81548160ff021916908315150217905550565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600060149054906101000a900460ff166101da5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fb60f709346127105a03906040518363ffffffff1660e01b81526004016000604051808303818589803b15801561018f57600080fd5b5088f11580156101a3573d6000803e3d6000fd5b5050505050507fde0f1ac890b93f05f29eb21d0298f6bf3b9954213c196fefecdb445b4ed047a560405160405180910390a1610207565b7faf10f11658b97bcc0ed656fdfe4e20ca91e4a2a150f363233da11ec1d4898a4760405160405180910390a15b56fea2646970667358221220a49fa0e8a4947075a1ccbc823ca128cf2b35d6dc594337da9924f07596c2285b64736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx03 @@ -59,7 +59,7 @@ transaction_build tx03 receiverAddress 00 value 0 data 608060405260008060146101000a81548160ff02191690831515021790555034801561002a57600080fd5b5061023f8061003a6000396000f3fe6080604052600436106100385760003560e01c80633aecee241461003b578063ba26473514610052578063fb60f709146100a357610039565b5b005b34801561004757600080fd5b506100506100ad565b005b34801561005e57600080fd5b506100a16004803603602081101561007557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506100ca565b005b6100ab61010d565b005b6001600060146101000a81548160ff021916908315150217905550565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600060149054906101000a900460ff166101da5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fb60f709346127105a03906040518363ffffffff1660e01b81526004016000604051808303818589803b15801561018f57600080fd5b5088f11580156101a3573d6000803e3d6000fd5b5050505050507fde0f1ac890b93f05f29eb21d0298f6bf3b9954213c196fefecdb445b4ed047a560405160405180910390a1610207565b7faf10f11658b97bcc0ed656fdfe4e20ca91e4a2a150f363233da11ec1d4898a4760405160405180910390a15b56fea2646970667358221220a49fa0e8a4947075a1ccbc823ca128cf2b35d6dc594337da9924f07596c2285b64736f6c63430007030033 - gas 2000000 + gas 1000000 build block_build b01 @@ -76,7 +76,7 @@ transaction_build tx04 nonce 3 receiverAddress 6252703f5ba322ec64d3ac45e56241b7d9e481ad data ba26473500000000000000000000000056aa252dd82173789984fa164ee26ce2da9336ff - gas 2000000 + gas 1000000 build # init next address 27444fbce96cb2d27b94e116d1506d7739c05862 @@ -85,7 +85,7 @@ transaction_build tx05 nonce 4 receiverAddress 56aa252dd82173789984fa164ee26ce2da9336ff data ba26473500000000000000000000000027444fbce96cb2d27b94e116d1506d7739c05862 - gas 2000000 + gas 1000000 build # init last call @@ -94,7 +94,7 @@ transaction_build tx06 nonce 5 receiverAddress 27444fbce96cb2d27b94e116d1506d7739c05862 data 3aecee24 - gas 2000000 + gas 1000000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueAndStorageRefund.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueAndStorageRefund.txt index 5ce34b98e24..c40e44fcdce 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueAndStorageRefund.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueAndStorageRefund.txt @@ -56,7 +56,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405260008060146101000a81548160ff02191690831515021790555034801561002a57600080fd5b50602a600160006001815260200190815260200160002081905550602a600160006002815260200190815260200160002081905550602a600160006003815260200190815260200160002081905550602a600160006004815260200190815260200160002081905550602a60016000600581526020019081526020016000208190555061037a806100bc6000396000f3fe6080604052600436106100435760003560e01c80633aecee24146100465780635e383d211461005d578063ba264735146100ac578063fb60f709146100fd57610044565b5b005b34801561005257600080fd5b5061005b610107565b005b34801561006957600080fd5b506100966004803603602081101561008057600080fd5b8101908080359060200190929190505050610124565b6040518082815260200191505060405180910390f35b3480156100b857600080fd5b506100fb600480360360208110156100cf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061013c565b005b61010561017f565b005b6001600060146101000a81548160ff021916908315150217905550565b60016020528060005260406000206000915090505481565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600060149054906101000a900460ff1661024c5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fb60f709346127105a03906040518363ffffffff1660e01b81526004016000604051808303818589803b15801561020157600080fd5b5088f1158015610215573d6000803e3d6000fd5b5050505050507fde0f1ac890b93f05f29eb21d0298f6bf3b9954213c196fefecdb445b4ed047a560405160405180910390a1610342565b600060016000600181526020019081526020016000208190555060006001600060028152602001908152602001600020819055506000600160006003815260200190815260200160002081905550600060016000600481526020019081526020016000208190555060006001600060058152602001908152602001600020819055503073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f19350505050158015610314573d6000803e3d6000fd5b507faf10f11658b97bcc0ed656fdfe4e20ca91e4a2a150f363233da11ec1d4898a4760405160405180910390a15b56fea2646970667358221220597bb4cb03e45f2e30e7bec4738d5b1056f3f8f5156d8fd74ef927091ce7968d64736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx02 @@ -65,7 +65,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 608060405260008060146101000a81548160ff02191690831515021790555034801561002a57600080fd5b50602a600160006001815260200190815260200160002081905550602a600160006002815260200190815260200160002081905550602a600160006003815260200190815260200160002081905550602a600160006004815260200190815260200160002081905550602a60016000600581526020019081526020016000208190555061037a806100bc6000396000f3fe6080604052600436106100435760003560e01c80633aecee24146100465780635e383d211461005d578063ba264735146100ac578063fb60f709146100fd57610044565b5b005b34801561005257600080fd5b5061005b610107565b005b34801561006957600080fd5b506100966004803603602081101561008057600080fd5b8101908080359060200190929190505050610124565b6040518082815260200191505060405180910390f35b3480156100b857600080fd5b506100fb600480360360208110156100cf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061013c565b005b61010561017f565b005b6001600060146101000a81548160ff021916908315150217905550565b60016020528060005260406000206000915090505481565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600060149054906101000a900460ff1661024c5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fb60f709346127105a03906040518363ffffffff1660e01b81526004016000604051808303818589803b15801561020157600080fd5b5088f1158015610215573d6000803e3d6000fd5b5050505050507fde0f1ac890b93f05f29eb21d0298f6bf3b9954213c196fefecdb445b4ed047a560405160405180910390a1610342565b600060016000600181526020019081526020016000208190555060006001600060028152602001908152602001600020819055506000600160006003815260200190815260200160002081905550600060016000600481526020019081526020016000208190555060006001600060058152602001908152602001600020819055503073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f19350505050158015610314573d6000803e3d6000fd5b507faf10f11658b97bcc0ed656fdfe4e20ca91e4a2a150f363233da11ec1d4898a4760405160405180910390a15b56fea2646970667358221220597bb4cb03e45f2e30e7bec4738d5b1056f3f8f5156d8fd74ef927091ce7968d64736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx03 @@ -74,7 +74,7 @@ transaction_build tx03 receiverAddress 00 value 0 data 608060405260008060146101000a81548160ff02191690831515021790555034801561002a57600080fd5b50602a600160006001815260200190815260200160002081905550602a600160006002815260200190815260200160002081905550602a600160006003815260200190815260200160002081905550602a600160006004815260200190815260200160002081905550602a60016000600581526020019081526020016000208190555061037a806100bc6000396000f3fe6080604052600436106100435760003560e01c80633aecee24146100465780635e383d211461005d578063ba264735146100ac578063fb60f709146100fd57610044565b5b005b34801561005257600080fd5b5061005b610107565b005b34801561006957600080fd5b506100966004803603602081101561008057600080fd5b8101908080359060200190929190505050610124565b6040518082815260200191505060405180910390f35b3480156100b857600080fd5b506100fb600480360360208110156100cf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061013c565b005b61010561017f565b005b6001600060146101000a81548160ff021916908315150217905550565b60016020528060005260406000206000915090505481565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600060149054906101000a900460ff1661024c5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fb60f709346127105a03906040518363ffffffff1660e01b81526004016000604051808303818589803b15801561020157600080fd5b5088f1158015610215573d6000803e3d6000fd5b5050505050507fde0f1ac890b93f05f29eb21d0298f6bf3b9954213c196fefecdb445b4ed047a560405160405180910390a1610342565b600060016000600181526020019081526020016000208190555060006001600060028152602001908152602001600020819055506000600160006003815260200190815260200160002081905550600060016000600481526020019081526020016000208190555060006001600060058152602001908152602001600020819055503073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f19350505050158015610314573d6000803e3d6000fd5b507faf10f11658b97bcc0ed656fdfe4e20ca91e4a2a150f363233da11ec1d4898a4760405160405180910390a15b56fea2646970667358221220597bb4cb03e45f2e30e7bec4738d5b1056f3f8f5156d8fd74ef927091ce7968d64736f6c63430007030033 - gas 2000000 + gas 1000000 build block_build b01 @@ -91,7 +91,7 @@ transaction_build tx04 nonce 3 receiverAddress 6252703f5ba322ec64d3ac45e56241b7d9e481ad data ba26473500000000000000000000000056aa252dd82173789984fa164ee26ce2da9336ff - gas 2000000 + gas 1000000 build # init next address 27444fbce96cb2d27b94e116d1506d7739c05862 @@ -100,7 +100,7 @@ transaction_build tx05 nonce 4 receiverAddress 56aa252dd82173789984fa164ee26ce2da9336ff data ba26473500000000000000000000000027444fbce96cb2d27b94e116d1506d7739c05862 - gas 2000000 + gas 1000000 build # init last call @@ -109,7 +109,7 @@ transaction_build tx06 nonce 5 receiverAddress 27444fbce96cb2d27b94e116d1506d7739c05862 data 3aecee24 - gas 2000000 + gas 1000000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueStorageRefundAndFixedGas.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueStorageRefundAndFixedGas.txt index 17674bc585d..0fcc583ed90 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueStorageRefundAndFixedGas.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueStorageRefundAndFixedGas.txt @@ -56,7 +56,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405260008060146101000a81548160ff02191690831515021790555034801561002a57600080fd5b50602a600160006001815260200190815260200160002081905550602a600160006002815260200190815260200160002081905550602a600160006003815260200190815260200160002081905550602a600160006004815260200190815260200160002081905550602a600160006005815260200190815260200160002081905550610379806100bc6000396000f3fe6080604052600436106100435760003560e01c80633aecee24146100465780635e383d211461005d578063ba264735146100ac578063fb60f709146100fd57610044565b5b005b34801561005257600080fd5b5061005b610107565b005b34801561006957600080fd5b506100966004803603602081101561008057600080fd5b8101908080359060200190929190505050610124565b6040518082815260200191505060405180910390f35b3480156100b857600080fd5b506100fb600480360360208110156100cf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061013c565b005b61010561017f565b005b6001600060146101000a81548160ff021916908315150217905550565b60016020528060005260406000206000915090505481565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600060149054906101000a900460ff1661024b5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fb60f70934620186a0906040518363ffffffff1660e01b81526004016000604051808303818589803b15801561020057600080fd5b5088f1158015610214573d6000803e3d6000fd5b5050505050507fde0f1ac890b93f05f29eb21d0298f6bf3b9954213c196fefecdb445b4ed047a560405160405180910390a1610341565b600060016000600181526020019081526020016000208190555060006001600060028152602001908152602001600020819055506000600160006003815260200190815260200160002081905550600060016000600481526020019081526020016000208190555060006001600060058152602001908152602001600020819055503073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f19350505050158015610313573d6000803e3d6000fd5b507faf10f11658b97bcc0ed656fdfe4e20ca91e4a2a150f363233da11ec1d4898a4760405160405180910390a15b56fea2646970667358221220c905291e932b21b40fd3481b37e86ccdc27a82c41d4b79c2b9ba1493e806168e64736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx02 @@ -65,7 +65,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 608060405260008060146101000a81548160ff02191690831515021790555034801561002a57600080fd5b50602a600160006001815260200190815260200160002081905550602a600160006002815260200190815260200160002081905550602a600160006003815260200190815260200160002081905550602a600160006004815260200190815260200160002081905550602a600160006005815260200190815260200160002081905550610379806100bc6000396000f3fe6080604052600436106100435760003560e01c80633aecee24146100465780635e383d211461005d578063ba264735146100ac578063fb60f709146100fd57610044565b5b005b34801561005257600080fd5b5061005b610107565b005b34801561006957600080fd5b506100966004803603602081101561008057600080fd5b8101908080359060200190929190505050610124565b6040518082815260200191505060405180910390f35b3480156100b857600080fd5b506100fb600480360360208110156100cf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061013c565b005b61010561017f565b005b6001600060146101000a81548160ff021916908315150217905550565b60016020528060005260406000206000915090505481565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600060149054906101000a900460ff1661024b5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fb60f70934620186a0906040518363ffffffff1660e01b81526004016000604051808303818589803b15801561020057600080fd5b5088f1158015610214573d6000803e3d6000fd5b5050505050507fde0f1ac890b93f05f29eb21d0298f6bf3b9954213c196fefecdb445b4ed047a560405160405180910390a1610341565b600060016000600181526020019081526020016000208190555060006001600060028152602001908152602001600020819055506000600160006003815260200190815260200160002081905550600060016000600481526020019081526020016000208190555060006001600060058152602001908152602001600020819055503073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f19350505050158015610313573d6000803e3d6000fd5b507faf10f11658b97bcc0ed656fdfe4e20ca91e4a2a150f363233da11ec1d4898a4760405160405180910390a15b56fea2646970667358221220c905291e932b21b40fd3481b37e86ccdc27a82c41d4b79c2b9ba1493e806168e64736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx03 @@ -74,7 +74,7 @@ transaction_build tx03 receiverAddress 00 value 0 data 608060405260008060146101000a81548160ff02191690831515021790555034801561002a57600080fd5b50602a600160006001815260200190815260200160002081905550602a600160006002815260200190815260200160002081905550602a600160006003815260200190815260200160002081905550602a600160006004815260200190815260200160002081905550602a600160006005815260200190815260200160002081905550610379806100bc6000396000f3fe6080604052600436106100435760003560e01c80633aecee24146100465780635e383d211461005d578063ba264735146100ac578063fb60f709146100fd57610044565b5b005b34801561005257600080fd5b5061005b610107565b005b34801561006957600080fd5b506100966004803603602081101561008057600080fd5b8101908080359060200190929190505050610124565b6040518082815260200191505060405180910390f35b3480156100b857600080fd5b506100fb600480360360208110156100cf57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061013c565b005b61010561017f565b005b6001600060146101000a81548160ff021916908315150217905550565b60016020528060005260406000206000915090505481565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b600060149054906101000a900460ff1661024b5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fb60f70934620186a0906040518363ffffffff1660e01b81526004016000604051808303818589803b15801561020057600080fd5b5088f1158015610214573d6000803e3d6000fd5b5050505050507fde0f1ac890b93f05f29eb21d0298f6bf3b9954213c196fefecdb445b4ed047a560405160405180910390a1610341565b600060016000600181526020019081526020016000208190555060006001600060028152602001908152602001600020819055506000600160006003815260200190815260200160002081905550600060016000600481526020019081526020016000208190555060006001600060058152602001908152602001600020819055503073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f19350505050158015610313573d6000803e3d6000fd5b507faf10f11658b97bcc0ed656fdfe4e20ca91e4a2a150f363233da11ec1d4898a4760405160405180910390a15b56fea2646970667358221220c905291e932b21b40fd3481b37e86ccdc27a82c41d4b79c2b9ba1493e806168e64736f6c63430007030033 - gas 2000000 + gas 1000000 build block_build b01 @@ -91,7 +91,7 @@ transaction_build tx04 nonce 3 receiverAddress 6252703f5ba322ec64d3ac45e56241b7d9e481ad data ba26473500000000000000000000000056aa252dd82173789984fa164ee26ce2da9336ff - gas 2000000 + gas 1000000 build # init next address 27444fbce96cb2d27b94e116d1506d7739c05862 @@ -100,7 +100,7 @@ transaction_build tx05 nonce 4 receiverAddress 56aa252dd82173789984fa164ee26ce2da9336ff data ba26473500000000000000000000000027444fbce96cb2d27b94e116d1506d7739c05862 - gas 2000000 + gas 1000000 build # init last call @@ -109,7 +109,7 @@ transaction_build tx06 nonce 5 receiverAddress 27444fbce96cb2d27b94e116d1506d7739c05862 data 3aecee24 - gas 2000000 + gas 1000000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase1.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase1.txt index 78fc7d33004..e22c3e8d04b 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase1.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase1.txt @@ -48,7 +48,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061031d806100206000396000f3fe6080604052600436106100385760003560e01c806318d3af631461003b5780636efb15df14610045578063ba2647351461008057610039565b5b005b6100436100d1565b005b34801561005157600080fd5b5061007e6004803603602081101561006857600080fd5b810190808035906020019092919050505061029a565b005b34801561008c57600080fd5b506100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102a4565b005b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561012b57610298565b600060015411156101ea5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636001546040518263ffffffff1660e01b81526004016000604051808303818588803b1580156101a057600080fd5b505af11580156101b4573d6000803e3d6000fd5b50505050507fd99f0cfde566379669cb08fdc25d80461af2e181833081dd90ca21eaabfc1f9560405160405180910390a1610297565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561025257600080fd5b505af1158015610266573d6000803e3d6000fd5b505050507fc912088d570658c8656f32ff15e09d77c5d9eb8c4304e077777143ba6225dc8860405160405180910390a15b5b565b8060018190555050565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea26469706673582212209458b89f68ed66dc26086396962dad91224bfad1e1711e6151d84b91dfe798c564736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx02 @@ -57,7 +57,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061031d806100206000396000f3fe6080604052600436106100385760003560e01c806318d3af631461003b5780636efb15df14610045578063ba2647351461008057610039565b5b005b6100436100d1565b005b34801561005157600080fd5b5061007e6004803603602081101561006857600080fd5b810190808035906020019092919050505061029a565b005b34801561008c57600080fd5b506100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102a4565b005b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561012b57610298565b600060015411156101ea5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636001546040518263ffffffff1660e01b81526004016000604051808303818588803b1580156101a057600080fd5b505af11580156101b4573d6000803e3d6000fd5b50505050507fd99f0cfde566379669cb08fdc25d80461af2e181833081dd90ca21eaabfc1f9560405160405180910390a1610297565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561025257600080fd5b505af1158015610266573d6000803e3d6000fd5b505050507fc912088d570658c8656f32ff15e09d77c5d9eb8c4304e077777143ba6225dc8860405160405180910390a15b5b565b8060018190555050565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea26469706673582212209458b89f68ed66dc26086396962dad91224bfad1e1711e6151d84b91dfe798c564736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx03 @@ -66,7 +66,7 @@ transaction_build tx03 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061031d806100206000396000f3fe6080604052600436106100385760003560e01c806318d3af631461003b5780636efb15df14610045578063ba2647351461008057610039565b5b005b6100436100d1565b005b34801561005157600080fd5b5061007e6004803603602081101561006857600080fd5b810190808035906020019092919050505061029a565b005b34801561008c57600080fd5b506100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102a4565b005b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561012b57610298565b600060015411156101ea5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636001546040518263ffffffff1660e01b81526004016000604051808303818588803b1580156101a057600080fd5b505af11580156101b4573d6000803e3d6000fd5b50505050507fd99f0cfde566379669cb08fdc25d80461af2e181833081dd90ca21eaabfc1f9560405160405180910390a1610297565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561025257600080fd5b505af1158015610266573d6000803e3d6000fd5b505050507fc912088d570658c8656f32ff15e09d77c5d9eb8c4304e077777143ba6225dc8860405160405180910390a15b5b565b8060018190555050565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea26469706673582212209458b89f68ed66dc26086396962dad91224bfad1e1711e6151d84b91dfe798c564736f6c63430007030033 - gas 2000000 + gas 1000000 build block_build b01 @@ -83,7 +83,7 @@ transaction_build tx04 nonce 3 receiverAddress 6252703f5ba322ec64d3ac45e56241b7d9e481ad data ba26473500000000000000000000000056aa252dd82173789984fa164ee26ce2da9336ff - gas 2000000 + gas 1000000 build # init next address 27444fbce96cb2d27b94e116d1506d7739c05862 @@ -92,7 +92,7 @@ transaction_build tx05 nonce 4 receiverAddress 56aa252dd82173789984fa164ee26ce2da9336ff data ba26473500000000000000000000000027444fbce96cb2d27b94e116d1506d7739c05862 - gas 2000000 + gas 1000000 build # set 1000 wei to send for second contract @@ -101,7 +101,7 @@ transaction_build tx06 nonce 5 receiverAddress 56aa252dd82173789984fa164ee26ce2da9336ff data 6efb15df00000000000000000000000000000000000000000000000000000000000003e8 - gas 2000000 + gas 1000000 build # deposit 1000 wei to second contract @@ -110,7 +110,7 @@ transaction_build tx07 nonce 6 receiverAddress 56aa252dd82173789984fa164ee26ce2da9336ff value 1000 - gas 2000000 + gas 1000000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase2.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase2.txt index cb2817c9555..fdc1840e451 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase2.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase2.txt @@ -48,7 +48,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061031d806100206000396000f3fe6080604052600436106100385760003560e01c806318d3af631461003b5780636efb15df14610045578063ba2647351461008057610039565b5b005b6100436100d1565b005b34801561005157600080fd5b5061007e6004803603602081101561006857600080fd5b810190808035906020019092919050505061029a565b005b34801561008c57600080fd5b506100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102a4565b005b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561012b57610298565b600060015411156101ea5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636001546040518263ffffffff1660e01b81526004016000604051808303818588803b1580156101a057600080fd5b505af11580156101b4573d6000803e3d6000fd5b50505050507fd99f0cfde566379669cb08fdc25d80461af2e181833081dd90ca21eaabfc1f9560405160405180910390a1610297565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561025257600080fd5b505af1158015610266573d6000803e3d6000fd5b505050507fc912088d570658c8656f32ff15e09d77c5d9eb8c4304e077777143ba6225dc8860405160405180910390a15b5b565b8060018190555050565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea26469706673582212209458b89f68ed66dc26086396962dad91224bfad1e1711e6151d84b91dfe798c564736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx02 @@ -57,7 +57,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061031d806100206000396000f3fe6080604052600436106100385760003560e01c806318d3af631461003b5780636efb15df14610045578063ba2647351461008057610039565b5b005b6100436100d1565b005b34801561005157600080fd5b5061007e6004803603602081101561006857600080fd5b810190808035906020019092919050505061029a565b005b34801561008c57600080fd5b506100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102a4565b005b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561012b57610298565b600060015411156101ea5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636001546040518263ffffffff1660e01b81526004016000604051808303818588803b1580156101a057600080fd5b505af11580156101b4573d6000803e3d6000fd5b50505050507fd99f0cfde566379669cb08fdc25d80461af2e181833081dd90ca21eaabfc1f9560405160405180910390a1610297565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561025257600080fd5b505af1158015610266573d6000803e3d6000fd5b505050507fc912088d570658c8656f32ff15e09d77c5d9eb8c4304e077777143ba6225dc8860405160405180910390a15b5b565b8060018190555050565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea26469706673582212209458b89f68ed66dc26086396962dad91224bfad1e1711e6151d84b91dfe798c564736f6c63430007030033 - gas 2000000 + gas 1000000 build transaction_build tx03 @@ -66,7 +66,7 @@ transaction_build tx03 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061031d806100206000396000f3fe6080604052600436106100385760003560e01c806318d3af631461003b5780636efb15df14610045578063ba2647351461008057610039565b5b005b6100436100d1565b005b34801561005157600080fd5b5061007e6004803603602081101561006857600080fd5b810190808035906020019092919050505061029a565b005b34801561008c57600080fd5b506100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506102a4565b005b600073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561012b57610298565b600060015411156101ea5760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636001546040518263ffffffff1660e01b81526004016000604051808303818588803b1580156101a057600080fd5b505af11580156101b4573d6000803e3d6000fd5b50505050507fd99f0cfde566379669cb08fdc25d80461af2e181833081dd90ca21eaabfc1f9560405160405180910390a1610297565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166318d3af636040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561025257600080fd5b505af1158015610266573d6000803e3d6000fd5b505050507fc912088d570658c8656f32ff15e09d77c5d9eb8c4304e077777143ba6225dc8860405160405180910390a15b5b565b8060018190555050565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea26469706673582212209458b89f68ed66dc26086396962dad91224bfad1e1711e6151d84b91dfe798c564736f6c63430007030033 - gas 2000000 + gas 1000000 build block_build b01 @@ -83,7 +83,7 @@ transaction_build tx04 nonce 3 receiverAddress 6252703f5ba322ec64d3ac45e56241b7d9e481ad data ba26473500000000000000000000000056aa252dd82173789984fa164ee26ce2da9336ff - gas 2000000 + gas 1000000 build # init next address 27444fbce96cb2d27b94e116d1506d7739c05862 @@ -92,7 +92,7 @@ transaction_build tx05 nonce 4 receiverAddress 56aa252dd82173789984fa164ee26ce2da9336ff data ba26473500000000000000000000000027444fbce96cb2d27b94e116d1506d7739c05862 - gas 2000000 + gas 1000000 build # set 1000 wei to send for first contract @@ -101,7 +101,7 @@ transaction_build tx06 nonce 5 receiverAddress 6252703f5ba322ec64d3ac45e56241b7d9e481ad data 6efb15df00000000000000000000000000000000000000000000000000000000000003e8 - gas 2000000 + gas 1000000 build # deposit 1000 wei to first contract @@ -110,7 +110,7 @@ transaction_build tx07 nonce 6 receiverAddress 6252703f5ba322ec64d3ac45e56241b7d9e481ad value 1000 - gas 2000000 + gas 1000000 build # set 1000 wei to send for second contract @@ -119,7 +119,7 @@ transaction_build tx08 nonce 7 receiverAddress 56aa252dd82173789984fa164ee26ce2da9336ff data 6efb15df00000000000000000000000000000000000000000000000000000000000003e8 - gas 2000000 + gas 1000000 build # deposit 1000 wei to second contract @@ -128,7 +128,7 @@ transaction_build tx09 nonce 8 receiverAddress 56aa252dd82173789984fa164ee26ce2da9336ff value 1000 - gas 2000000 + gas 1000000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/updateStorage.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/updateStorage.txt index 72317ab1377..2d3a0bc175f 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/updateStorage.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/updateStorage.txt @@ -27,7 +27,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b50610144806100206000396000f3fe60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635e383d21146100515780637b8d56e3146100a0575b600080fd5b34801561005d57600080fd5b5061008a6004803603602081101561007457600080fd5b81019080803590602001909291905050506100e5565b6040518082815260200191505060405180910390f35b3480156100ac57600080fd5b506100e3600480360360408110156100c357600080fd5b8101908080359060200190929190803590602001909291905050506100ff565b005b6000816064811015156100f457fe5b016000915090505481565b8060008360648110151561010f57fe5b0181905550505056fea165627a7a72305820d27e1f1cce816508bdc0611d325d8a6695742256aac16b8ea5b69034d0da31830029 - gas 2000000 + gas 1000000 build block_build b01 @@ -48,7 +48,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data 7b8d56e30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a - gas 6400000 + gas 3400000 build transaction_build tx03 @@ -57,7 +57,7 @@ transaction_build tx03 contract tx01 # created in tx01 value 0 data 7b8d56e30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002a - gas 6400000 + gas 3400000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/revert_reason.txt b/rskj-core/src/test/resources/dsl/eth_module/revert_reason.txt index 1a5ac29a8d6..d04ce79a9a8 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/revert_reason.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/revert_reason.txt @@ -36,6 +36,7 @@ transaction_build tx01 block_build b01 parent g00 transactions tx01 + gasLimit 6500000 build block_connect b01 @@ -51,7 +52,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data d96a094a0000000000000000000000000000000000000000000000000000000000000000 - gas 6400000 + gas 2000000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_does_not_exist.txt b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_does_not_exist.txt index 87bc51d9c0e..6984baffc26 100644 --- a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_does_not_exist.txt +++ b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_does_not_exist.txt @@ -28,7 +28,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061015c806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80631db2d21f14610046578063a696053f14610090578063d13319c4146100ae575b600080fd5b61004e6100cc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100986100f1565b6040518082815260200191505060405180910390f35b6100b66100f7565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b600080600073f55de706d4277ab783c8505b0f47c1e82648ba1e9050803f91508160018190555081925050509056fea264697066735822122071cdf36b83787a689e547268ef6d896410a3e67433ee452bf2295b5b9b6506dd64736f6c634300060b0033 - gas 2000000 + gas 1000000 build block_build b01 @@ -47,7 +47,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data d13319c4 - gas 2000000000 + gas 1000000000 build block_build b02 @@ -81,7 +81,7 @@ transaction_build tx03 contract tx01 # created in tx01 value 0 data d13319c4 - gas 2000000000 + gas 1000000000 build block_build b05 diff --git a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_exists_but_not_contract.txt b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_exists_but_not_contract.txt index ccc6610f84e..769c9a9af7f 100644 --- a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_exists_but_not_contract.txt +++ b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_exists_but_not_contract.txt @@ -29,7 +29,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061015c806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80631db2d21f14610046578063a696053f14610090578063d13319c4146100ae575b600080fd5b61004e6100cc565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100986100f1565b6040518082815260200191505060405180910390f35b6100b66100f7565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b600080600073cd2a3d9f938e13cd947ec05abc7fe734df8dd8269050803f91508160018190555081925050509056fea2646970667358221220a6e3e81bfb2357b1b84953afbf2f2154a6967f09e175f68a7061c86dbcf2fc2964736f6c634300060b0033 - gas 2000000 + gas 1000000 build block_build b01 @@ -48,7 +48,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data d13319c4 - gas 2000000000 + gas 1000000000 build block_build b02 @@ -82,7 +82,7 @@ transaction_build tx03 contract tx01 # created in tx01 value 0 data d13319c4 - gas 2000000000 + gas 1000000000 build block_build b05 diff --git a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_with_cache_empty_deploy.txt b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_with_cache_empty_deploy.txt index fd3cff0932c..7e405380731 100644 --- a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_with_cache_empty_deploy.txt +++ b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_with_cache_empty_deploy.txt @@ -39,7 +39,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b506101d9806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80631db2d21f14610051578063775c300c1461009b578063a696053f146100a5578063d13319c4146100c3575b600080fd5b6100596100e1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100a3610106565b005b6100ad610161565b6040518082815260200191505060405180910390f35b6100cb610167565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080600080600080f59150813f9050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806001819055505050565b60015481565b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050803f91508160018190555081925050509056fea2646970667358221220eca721e5908cc7ae068ebb47cdea1d0d71dc84902e0df17c516889dbc7d8b5fc64736f6c634300060b0033 - gas 2000000 + gas 1000000 build block_build b01 @@ -58,7 +58,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data 775c300c - gas 2000000000 + gas 1000000000 build block_build b02 @@ -79,7 +79,7 @@ transaction_build tx03 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b506101d9806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80631db2d21f14610051578063775c300c1461009b578063a696053f146100a5578063d13319c4146100c3575b600080fd5b6100596100e1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100a3610106565b005b6100ad610161565b6040518082815260200191505060405180910390f35b6100cb610167565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080600080600080f59150813f9050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806001819055505050565b60015481565b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050803f91508160018190555081925050509056fea2646970667358221220eca721e5908cc7ae068ebb47cdea1d0d71dc84902e0df17c516889dbc7d8b5fc64736f6c634300060b0033 - gas 2000000 + gas 1000000 build block_build b03 @@ -105,7 +105,7 @@ transaction_build tx04 contract tx03 # created in tx01 value 0 data 775c300c - gas 2000000000 + gas 1000000000 build block_build b05 diff --git a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_without_cache_empty_deploy.txt b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_without_cache_empty_deploy.txt index f20efd3e309..4a76021add5 100644 --- a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_without_cache_empty_deploy.txt +++ b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_without_cache_empty_deploy.txt @@ -39,7 +39,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b506101d9806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80631db2d21f14610051578063775c300c1461009b578063a696053f146100a5578063d13319c4146100c3575b600080fd5b6100596100e1565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100a3610106565b005b6100ad610161565b6040518082815260200191505060405180910390f35b6100cb610167565b6040518082815260200191505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600080600080600080f59150813f9050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550806001819055505050565b60015481565b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050803f91508160018190555081925050509056fea2646970667358221220eca721e5908cc7ae068ebb47cdea1d0d71dc84902e0df17c516889dbc7d8b5fc64736f6c634300060b0033 - gas 2000000 + gas 1000000 build block_build b01 @@ -58,7 +58,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data 775c300c - gas 2000000000 + gas 1000000000 build block_build b02 @@ -78,7 +78,7 @@ transaction_build tx03 contract tx01 # created in tx01 value 0 data d13319c4 - gas 2000000000 + gas 1000000000 build block_build b03 @@ -99,7 +99,7 @@ transaction_build tx04 contract tx01 # created in tx01 value 0 data d13319c4 - gas 2000000000 + gas 1000000000 build block_build b04 @@ -118,7 +118,7 @@ transaction_build tx05 contract tx01 # created in tx01 value 0 data d13319c4 - gas 2000000000 + gas 1000000000 build block_build b05 diff --git a/rskj-core/src/test/resources/dsl/logs01.txt b/rskj-core/src/test/resources/dsl/logs01.txt index fe2df498ab9..d34428ffd33 100644 --- a/rskj-core/src/test/resources/dsl/logs01.txt +++ b/rskj-core/src/test/resources/dsl/logs01.txt @@ -8,7 +8,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 6060604052341561000f57600080fd5b5b610018610151565b604051809103906000f080151561002e57600080fd5b6000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f80ae3ec8027d0c5d1f3e47fb4bf1d9fc28225e7f4bcb1971b36efb81fe40574d6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663209652556000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b151561011b57600080fd5b6102c65a03f1151561012c57600080fd5b505050604051805190506040518082815260200191505060405180910390a15b610161565b6040516101b8806101a583390190565b60368061016f6000396000f30060606040525b600080fd00a165627a7a7230582009dde7c398f78ed62145cdd71de22468ae180f49faf182761851a96e601e785600296060604052341561000f57600080fd5b5b60466000819055507f06acbfb32bcf8383f3b0a768b70ac9ec234ea0f2d3b9c77fa6a2de69b919aad16000546040518082815260200191505060405180910390a15b5b610156806100626000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632096525514610049578063d09de08a14610072575b600080fd5b341561005457600080fd5b61005c610087565b6040518082815260200191505060405180910390f35b341561007d57600080fd5b6100856100cb565b005b60007f1ee041944547858a75ebef916083b6d4f5ae04bea9cd809334469dd07dbf441b6000546040518082815260200191505060405180910390a160005490505b90565b600080815460010191905081905550600160026000548115156100ea57fe5b061415157f6e61ef44ac2747ff8b84d353a908eb8bd5c3fb118334d57698c5cfc7041196ad6000546040518082815260200191505060405180910390a25b5600a165627a7a7230582038120cf814b0bf518df643fa226e3c7323c39ac92e8d558d548537ec43a152b10029 - gas 2000000 + gas 1000000 build block_build b01 diff --git a/rskj-core/src/test/resources/dsl/recursive01.txt b/rskj-core/src/test/resources/dsl/recursive01.txt index 396f989317a..985d43490e2 100644 --- a/rskj-core/src/test/resources/dsl/recursive01.txt +++ b/rskj-core/src/test/resources/dsl/recursive01.txt @@ -76,7 +76,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b503060405161001e906100b6565b808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604051809103906000f080158015610070573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506100c3565b610285806102c283390190565b6101f0806100d26000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806361bc221a146100465780637cf5dab01461006457806399c93b7b14610092575b600080fd5b61004e6100dc565b6040518082815260200191505060405180910390f35b6100906004803603602081101561007a57600080fd5b81019080803590602001909291905050506100e2565b005b61009a610195565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60005481565b60008114156100f057610192565b6000808154809291906001019190505550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637cf5dab0600183036040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561017957600080fd5b505af115801561018d573d6000803e3d6000fd5b505050505b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fea265627a7a72315820902aa0324b2c98c86efd82a6d396a26f478dc098874af497ccf57dba64c0014f64736f6c63430005100032608060405234801561001057600080fd5b506040516102853803806102858339818101604052602081101561003357600080fd5b810190808051906020019092919050505080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101f0806100956000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806361bc221a146100465780637cf5dab01461006457806399c93b7b14610092575b600080fd5b61004e6100dc565b6040518082815260200191505060405180910390f35b6100906004803603602081101561007a57600080fd5b81019080803590602001909291905050506100e2565b005b61009a610195565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60005481565b60008114156100f057610192565b6000808154809291906001019190505550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637cf5dab0600183036040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561017957600080fd5b505af115801561018d573d6000803e3d6000fd5b505050505b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fea265627a7a72315820fcf11fb16267f8707a34d30e6b2c980491bc4c79582a4b80c3d0958f83f4e3b964736f6c63430005100032 - gas 2000000 + gas 1000000 build block_build b01 @@ -97,7 +97,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data 7cf5dab00000000000000000000000000000000000000000000000000000000000000190 - gas 6400000 + gas 3200000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/recursive02.txt b/rskj-core/src/test/resources/dsl/recursive02.txt index 10762f4066a..5082065122e 100644 --- a/rskj-core/src/test/resources/dsl/recursive02.txt +++ b/rskj-core/src/test/resources/dsl/recursive02.txt @@ -76,7 +76,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b503060405161001e906100b6565b808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001915050604051809103906000f080158015610070573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506100c3565b610285806102c283390190565b6101f0806100d26000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806361bc221a146100465780637cf5dab01461006457806399c93b7b14610092575b600080fd5b61004e6100dc565b6040518082815260200191505060405180910390f35b6100906004803603602081101561007a57600080fd5b81019080803590602001909291905050506100e2565b005b61009a610195565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60005481565b60008114156100f057610192565b6000808154809291906001019190505550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637cf5dab0600183036040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561017957600080fd5b505af115801561018d573d6000803e3d6000fd5b505050505b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fea265627a7a72315820902aa0324b2c98c86efd82a6d396a26f478dc098874af497ccf57dba64c0014f64736f6c63430005100032608060405234801561001057600080fd5b506040516102853803806102858339818101604052602081101561003357600080fd5b810190808051906020019092919050505080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506101f0806100956000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806361bc221a146100465780637cf5dab01461006457806399c93b7b14610092575b600080fd5b61004e6100dc565b6040518082815260200191505060405180910390f35b6100906004803603602081101561007a57600080fd5b81019080803590602001909291905050506100e2565b005b61009a610195565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60005481565b60008114156100f057610192565b6000808154809291906001019190505550600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16637cf5dab0600183036040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b15801561017957600080fd5b505af115801561018d573d6000803e3d6000fd5b505050505b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff168156fea265627a7a72315820fcf11fb16267f8707a34d30e6b2c980491bc4c79582a4b80c3d0958f83f4e3b964736f6c63430005100032 - gas 2000000 + gas 1000000 build block_build b01 @@ -103,7 +103,7 @@ transaction_build tx02 block_build b02 parent b01 transactions tx02 - gasLimit 6500000 + gasLimit 13000000 build block_connect b02 From 9b90491d0a828c3e29f7b9df9678535cba40e3fa Mon Sep 17 00:00:00 2001 From: Julian Len Date: Wed, 19 Apr 2023 15:46:30 -0300 Subject: [PATCH 32/89] Now the gas limit considered is the sublist gas limit --- .../rsk/core/TransactionExecutorFactory.java | 11 ++-- .../co/rsk/core/TransactionListExecutor.java | 9 ++-- .../java/co/rsk/core/bc/BlockExecutor.java | 19 +++++-- .../bc/ParallelizeTransactionHandler.java | 6 +-- .../ethereum/core/TransactionExecutor.java | 32 +++++++----- .../co/rsk/core/bc/BlockExecutorTest.java | 51 ++++++++++++++++++- .../bc/ParallelizeTransactionHandlerTest.java | 4 +- .../PrecompiledContractHasBeenCalledTest.java | 1 + .../TransactionExecutorTest.java | 11 ++-- 9 files changed, 107 insertions(+), 37 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java b/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java index 1878ca81616..c8b951f5e43 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionExecutorFactory.java @@ -24,6 +24,7 @@ import org.ethereum.db.BlockStore; import org.ethereum.db.ReceiptStore; import org.ethereum.vm.DataWord; +import org.ethereum.vm.GasCost; import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.program.invoke.ProgramInvokeFactory; @@ -66,6 +67,7 @@ public TransactionExecutor newInstance( long totalGasUsed) { return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, false, 0, new HashSet<>()); } + // TODO(JULI): newInstance calls a second newInstance hardcoding Postpone payment fees and sublist gas limit as block.gasLimit() public TransactionExecutor newInstance( Transaction tx, @@ -77,7 +79,8 @@ public TransactionExecutor newInstance( boolean vmTrace, int vmTraceOptions, Set deletedAccounts, - boolean postponeFeePayment) { + boolean postponeFeePayment, + long sublistGasLimit) { // Tracing configuration is scattered across different files (VM, DetailedProgramTrace, etc.) and // TransactionExecutor#extractTrace doesn't work when called independently. // It would be great to decouple from VmConfig#vmTrace, but sadly that's a major refactor we can't do now. @@ -111,7 +114,8 @@ public TransactionExecutor newInstance( precompiledContracts, deletedAccounts, blockTxSignatureCache, - postponeFeePayment + postponeFeePayment, + sublistGasLimit ); } @@ -125,6 +129,7 @@ public TransactionExecutor newInstance( boolean vmTrace, int vmTraceOptions, Set deletedAccounts) { - return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, vmTrace, vmTraceOptions, deletedAccounts, false); + return newInstance(tx, txindex, coinbase, track, block, totalGasUsed, vmTrace, vmTraceOptions, deletedAccounts, false, GasCost.toGas(block.getGasLimit())); } + // TODO(JULI): set the sublist gas limit as the whole block is wrong. However, this method is just used either when RSKIP144 is deactivated or for testing. } diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 46c7ff5f570..46b0dfc8c49 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -30,6 +30,7 @@ public class TransactionListExecutor implements Callable { private final Map receipts; private final Map transactionResults; private final ProgramTraceProcessor programTraceProcessor; + private long sublistGasLimit; private final boolean remascEnabled; private long totalGas; private int i; @@ -53,7 +54,8 @@ public TransactionListExecutor( @Nullable ProgramTraceProcessor programTraceProcessor, int firstTxIndex, Coin totalPaidFees, - boolean remascEnabled) { + boolean remascEnabled, + long sublistGasLimit) { this.block = block; this.transactionExecutorFactory = transactionExecutorFactory; this.track = track; @@ -72,6 +74,7 @@ public TransactionListExecutor( this.i = firstTxIndex; this.totalPaidFees = totalPaidFees; this.remascEnabled = remascEnabled; + this.sublistGasLimit = sublistGasLimit; } @Override @@ -95,8 +98,8 @@ public Boolean call() { vmTrace, vmTraceOptions, deletedAccounts, - true - ); + true, + sublistGasLimit); boolean transactionExecuted = txExecutor.executeTransaction(); if (!acceptInvalidTransactions && !transactionExecuted) { diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 88dc4d763c0..17bcdb2147a 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -434,6 +434,7 @@ private BlockResult executeParallel( ExecutorService executorService = Executors.newFixedThreadPool(Constants.getTransactionExecutionThreads()); ExecutorCompletionService completionService = new ExecutorCompletionService<>(executorService); List transactionListExecutors = new ArrayList<>(); + long sublistGasLimit = getSublistGasLimit(block); short start = 0; @@ -456,7 +457,8 @@ private BlockResult executeParallel( programTraceProcessor, start, Coin.ZERO, - remascEnabled + remascEnabled, + sublistGasLimit ); completionService.submit(txListExecutor); transactionListExecutors.add(txListExecutor); @@ -530,7 +532,8 @@ private BlockResult executeParallel( programTraceProcessor, start, totalPaidFees, - remascEnabled + remascEnabled, + sublistGasLimit ); Boolean success = txListExecutor.call(); if (!Boolean.TRUE.equals(success)) { @@ -593,8 +596,9 @@ private BlockResult executeForMiningAfterRSKIP144( int txindex = 0; - long sublistGasLimit = GasCost.toGas(block.getGasLimit()) / 2; - ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) Constants.getTransactionExecutionThreads(), sublistGasLimit, sublistGasLimit); + int transactionExecutionThreads = Constants.getTransactionExecutionThreads(); + long sublistGasLimit = getSublistGasLimit(block); + ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) transactionExecutionThreads, sublistGasLimit); for (Transaction tx : transactionsList) { loggingApplyBlockToTx(block, i); @@ -609,7 +613,8 @@ private BlockResult executeForMiningAfterRSKIP144( false, 0, deletedAccounts, - true + true, + sublistGasLimit ); boolean transactionExecuted = txExecutor.executeTransaction(); @@ -692,6 +697,10 @@ private BlockResult executeForMiningAfterRSKIP144( return result; } + private static long getSublistGasLimit(Block block) { + return GasCost.toGas(block.getGasLimit()) / 2; + } + private void registerExecutedTx(ProgramTraceProcessor programTraceProcessor, boolean vmTrace, List executedTransactions, Transaction tx, TransactionExecutor txExecutor) { executedTransactions.add(tx); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index 898c06f9ba8..f15941af371 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -31,15 +31,15 @@ public class ParallelizeTransactionHandler { private final Map sublistOfSender; private final ArrayList sublists; - public ParallelizeTransactionHandler(short numberOfSublists, long parallelSublistGasLimit, long sequentialSublistGasLimit) { + public ParallelizeTransactionHandler(short numberOfSublists, long sublistGasLimit) { this.sublistOfSender = new HashMap<>(); this.sublistsHavingWrittenToKey = new HashMap<>(); this.sublistsHavingReadFromKey = new HashMap<>(); this.sublists = new ArrayList<>(); for (short i = 0; i < numberOfSublists; i++){ - this.sublists.add(new TransactionSublist(parallelSublistGasLimit, false)); + this.sublists.add(new TransactionSublist(sublistGasLimit, false)); } - this.sublists.add(new TransactionSublist(sequentialSublistGasLimit, true)); + this.sublists.add(new TransactionSublist(sublistGasLimit, true)); } public Optional addTransaction(Transaction tx, Set newReadKeys, Set newWrittenKeys, long gasUsedByTx) { diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index 72b96959583..f24c3d9c35c 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -49,6 +49,7 @@ import static co.rsk.util.ListArrayUtil.getLength; import static co.rsk.util.ListArrayUtil.isEmpty; +import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP144; import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP174; import static org.ethereum.util.BIUtil.*; import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY; @@ -76,7 +77,7 @@ public class TransactionExecutor { private final PrecompiledContracts precompiledContracts; private final boolean enableRemasc; private String executionError = ""; - private final long gasUsedInTheBlock; + private final long gasUsedInTheContainer; private Coin paidFees; private final ProgramInvokeFactory programInvokeFactory; @@ -97,6 +98,7 @@ public class TransactionExecutor { private List logs = null; private final Set deletedAccounts; private final SignatureCache signatureCache; + private long sublistGasLimit; private boolean localCall = false; private boolean precompiledContractHasBeenCalledFlag = false; @@ -113,15 +115,16 @@ public TransactionExecutor( track, blockStore, receiptStore, blockFactory, programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, remascEnabled, precompiledContracts, deletedAccounts, - signatureCache, false); + signatureCache, false, GasCost.toGas(executionBlock.getGasLimit())); } + // TODO(JULI): set the sublist gas limit as the whole block is wrong. However, this method is just used for testing. public TransactionExecutor( Constants constants, ActivationConfig activationConfig, Transaction tx, int txindex, RskAddress coinbase, Repository track, BlockStore blockStore, ReceiptStore receiptStore, BlockFactory blockFactory, - ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsedInTheBlock, VmConfig vmConfig, + ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsedInTheContainer, VmConfig vmConfig, boolean remascEnabled, PrecompiledContracts precompiledContracts, Set deletedAccounts, - SignatureCache signatureCache, boolean postponeFeePayment) { + SignatureCache signatureCache, boolean postponeFeePayment, long sublistGasLimit) { this.constants = constants; this.signatureCache = signatureCache; this.activations = activationConfig.forBlock(executionBlock.getNumber()); @@ -135,12 +138,13 @@ public TransactionExecutor( this.blockFactory = blockFactory; this.programInvokeFactory = programInvokeFactory; this.executionBlock = executionBlock; - this.gasUsedInTheBlock = gasUsedInTheBlock; + this.gasUsedInTheContainer = gasUsedInTheContainer; this.vmConfig = vmConfig; this.precompiledContracts = precompiledContracts; this.enableRemasc = remascEnabled; this.deletedAccounts = new HashSet<>(deletedAccounts); this.postponeFeePayment = postponeFeePayment; + this.sublistGasLimit = sublistGasLimit; } /** @@ -173,9 +177,9 @@ private boolean init() { } long txGasLimit = GasCost.toGas(tx.getGasLimit()); - long curBlockGasLimit = GasCost.toGas(executionBlock.getGasLimit()); + long containerGasLimit = activations.isActive(RSKIP144)? sublistGasLimit : GasCost.toGas(executionBlock.getGasLimit()); - if (!gasIsValid(txGasLimit, curBlockGasLimit)) { + if (!gasIsValid(txGasLimit, containerGasLimit)) { return false; } @@ -257,18 +261,18 @@ private boolean nonceIsValid() { return true; } - private boolean gasIsValid(long txGasLimit, long curBlockGasLimit) { - // if we've passed the curBlockGas limit we must stop exec + private boolean gasIsValid(long txGasLimit, long curContainerGasLimit) { + // if we've passed the curContainerGasLimit limit we must stop exec // cumulativeGas being equal to GasCost.MAX_GAS is a border condition // which is used on some stress tests, but its far from being practical // as the current gas limit on blocks is 6.8M... several orders of magnitude // less than the theoretical max gas on blocks. - long cumulativeGas = GasCost.add(txGasLimit, gasUsedInTheBlock); + long cumulativeGas = GasCost.add(txGasLimit, gasUsedInTheContainer); - boolean cumulativeGasReached = cumulativeGas > curBlockGasLimit || cumulativeGas == GasCost.MAX_GAS; + boolean cumulativeGasReached = cumulativeGas > curContainerGasLimit || cumulativeGas == GasCost.MAX_GAS; if (cumulativeGasReached) { - execError(String.format("Too much gas used in this block: available in block: %s tx sent: %s", - curBlockGasLimit - txGasLimit, + execError(String.format("Too much gas used in this block or sublist(RSKIP144): available in sublist: %s tx sent: %s", + curContainerGasLimit - txGasLimit, txGasLimit)); return false; } @@ -504,7 +508,7 @@ private void createContract() { public TransactionReceipt getReceipt() { if (receipt == null) { receipt = new TransactionReceipt(); - long totalGasUsed = GasCost.add(gasUsedInTheBlock, getGasUsed()); + long totalGasUsed = GasCost.add(gasUsedInTheContainer, getGasUsed()); receipt.setCumulativeGas(totalGasUsed); receipt.setTransaction(tx); receipt.setLogInfoList(getVMLogs()); diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index aaba1bf948e..0cf6e225cef 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -697,10 +697,59 @@ void executeInvalidParallelBlockDueToCollision(boolean activeRskip144) { BlockResult result = executor.execute(null, 0, pBlock, parent.getHeader(), true, false, true); Assertions.assertEquals(BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT, result); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void whenExecuteATxWithGasLimitExceedingSublistGasLimitShouldNotBeInlcuded(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + doReturn(activeRskip144).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + Block parent = blockchain.getBestBlock(); + + + Repository track = repository.startTracking(); + Account sender = createAccount("sender", track, Coin.valueOf(6000000)); + Account receiver = createAccount("receiver", track, Coin.valueOf(6000000)); + + track.commit(); + parent.setStateRoot(repository.getRoot()); + + List txs = new LinkedList<>(); + Transaction tx = Transaction.builder() + .nonce(BigInteger.ZERO) + .gasPrice(BigInteger.ONE) + .gasLimit(parent.getGasLimit()) + .destination(receiver.getAddress()) + .chainId(CONFIG.getNetworkConstants().getChainId()) + .value(BigInteger.TEN) + .build(); + tx.sign(sender.getEcKey().getPrivKeyBytes()); + txs.add(tx); + List uncles = new ArrayList<>(); + + Block block = new BlockGenerator(Constants.regtest(), activationConfig) + .createChildBlock( + parent, + txs, + uncles, + 1, + null, + parent.getGasLimit(), + parent.getCoinbase(), + new short[]{1} + ); + + BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); + Assertions.assertEquals(0, blockResult.getExecutedTransactions().size()); + + BlockResult result = executor.execute(null, 0, block, parent.getHeader(), true, false, true); + Assertions.assertEquals(0, result.getExecutedTransactions().size()); + } @ParameterizedTest @ValueSource(booleans = {true, false}) - void ifThereIsACollisionBetweenParallelAndSequentialSublistsItShouldNotBeConsidered(boolean activeRskip144) { + void ifThereIsACollisionBetweenParallelAndSequentialSublistsTxShouldNotBeConsidered(boolean activeRskip144) { if (!activeRskip144) { return; } diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index fb3cc3ccdbc..827af546d80 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -61,7 +61,7 @@ public void setup() { aWrappedKey = new ByteArrayWrapper(aKey); sublists = 2; sequentialSublistNumber = sublists; - handler = new ParallelizeTransactionHandler(sublists, sublistGasLimit, sublistGasLimit); + handler = new ParallelizeTransactionHandler(sublists, sublistGasLimit); tx = new TransactionBuilder().nonce(1).sender(sender).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); tx2 = new TransactionBuilder().nonce(1).sender(sender2).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); tx3 = new TransactionBuilder().nonce(1).sender(sender3).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); @@ -859,7 +859,7 @@ void writeAfterWriteToSequentialForOutOfGasShouldGoToSequential() { AccountBuilder accountBuilder = new AccountBuilder(); - ParallelizeTransactionHandler handler = new ParallelizeTransactionHandler((short) 2, 1000, 1000); + ParallelizeTransactionHandler handler = new ParallelizeTransactionHandler((short) 2, 1000); // write X with 800 handler.addTransaction( diff --git a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java index 68022d28908..ec0d006b599 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java @@ -19,6 +19,7 @@ import org.ethereum.datasource.HashMapDB; import org.ethereum.db.BlockStoreDummy; import org.ethereum.db.MutableRepository; +import org.ethereum.vm.GasCost; import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.program.ProgramResult; import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl; diff --git a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java index 657faab7c0c..22c2c1bba0b 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java @@ -100,9 +100,13 @@ void setUp() { @Test void testInitHandlesFreeTransactionsOK() { - BlockTxSignatureCache blockTxSignatureCache = mock(BlockTxSignatureCache.class); Transaction transaction = mock(Transaction.class); + // paperwork: transaction has high gas limit, execution block has normal gas limit + // and the nonces are okey + when(transaction.getGasLimit()).thenReturn(BigInteger.valueOf(4000000).toByteArray()); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); + TransactionExecutor txExecutor = new TransactionExecutor( constants, activationConfig, transaction, txIndex, rskAddress, repository, blockStore, receiptStore, blockFactory, @@ -111,11 +115,6 @@ void testInitHandlesFreeTransactionsOK() { blockTxSignatureCache ); - - // paperwork: transaction has high gas limit, execution block has normal gas limit - // and the nonces are okey - when(transaction.getGasLimit()).thenReturn(BigInteger.valueOf(4000000).toByteArray()); - when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); when(repository.getNonce(transaction.getSender())).thenReturn(BigInteger.valueOf(1L)); when(transaction.getNonce()).thenReturn(BigInteger.valueOf(1L).toByteArray()); // more paperwork, the receiver is just someone From 9689245e85ed1eb6e15dddd157582f005bb6d411 Mon Sep 17 00:00:00 2001 From: Julian Len Date: Thu, 20 Apr 2023 12:48:08 -0300 Subject: [PATCH 33/89] Refactored TxExecutorTest so there is no extra constructor in TransactionExecutor --- .../ethereum/core/TransactionExecutor.java | 14 -- .../TransactionExecutorTest.java | 178 +++++++----------- 2 files changed, 66 insertions(+), 126 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index f24c3d9c35c..5fd5c06f4fd 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -105,20 +105,6 @@ public class TransactionExecutor { private boolean postponeFeePayment; - public TransactionExecutor( - Constants constants, ActivationConfig activationConfig, Transaction tx, int txindex, RskAddress coinbase, - Repository track, BlockStore blockStore, ReceiptStore receiptStore, BlockFactory blockFactory, - ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsedInTheBlock, VmConfig vmConfig, - boolean remascEnabled, PrecompiledContracts precompiledContracts, Set deletedAccounts, - SignatureCache signatureCache) { - this(constants, activationConfig, tx, txindex, coinbase, - track, blockStore, receiptStore, blockFactory, - programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, - remascEnabled, precompiledContracts, deletedAccounts, - signatureCache, false, GasCost.toGas(executionBlock.getGasLimit())); - } - // TODO(JULI): set the sublist gas limit as the whole block is wrong. However, this method is just used for testing. - public TransactionExecutor( Constants constants, ActivationConfig activationConfig, Transaction tx, int txindex, RskAddress coinbase, Repository track, BlockStore blockStore, ReceiptStore receiptStore, BlockFactory blockFactory, diff --git a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java index 22c2c1bba0b..c3e0c50e48a 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/TransactionExecutorTest.java @@ -17,45 +17,27 @@ */ package co.rsk.core.bc.transactionexecutor; -import co.rsk.blockchain.utils.BlockGenerator; import co.rsk.config.TestSystemProperties; -import co.rsk.config.VmConfig; import co.rsk.core.Coin; import co.rsk.core.RskAddress; import co.rsk.core.TransactionExecutorFactory; import co.rsk.crypto.Keccak256; -import co.rsk.db.MutableTrieImpl; -import co.rsk.peg.BridgeSupportFactory; -import co.rsk.peg.BtcBlockStoreWithCache; -import co.rsk.peg.RepositoryBtcBlockStoreWithCache; -import co.rsk.trie.Trie; -import co.rsk.trie.TrieStore; -import co.rsk.trie.TrieStoreImpl; -import org.bouncycastle.util.encoders.Hex; import org.ethereum.TestUtils; import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; import org.ethereum.core.*; import org.ethereum.crypto.HashUtil; -import org.ethereum.datasource.HashMapDB; import org.ethereum.db.BlockStore; -import org.ethereum.db.BlockStoreDummy; import org.ethereum.db.MutableRepository; import org.ethereum.db.ReceiptStore; -import org.ethereum.vm.DataWord; import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.program.invoke.ProgramInvokeFactory; -import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigInteger; -import java.util.*; -import static co.rsk.core.bc.BlockExecutorTest.createAccount; -import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -65,36 +47,36 @@ class TransactionExecutorTest { private static final int MAX_CACHE_SIZE = 900; private ActivationConfig activationConfig; private Constants constants; - private RskAddress rskAddress; private Repository repository; private BlockStore blockStore; private ReceiptStore receiptStore; private BlockFactory blockFactory; private ProgramInvokeFactory programInvokeFactory; private Block executionBlock; - private VmConfig vmConfig; private PrecompiledContracts precompiledContracts; - private Set deletedAccounts; private int txIndex; - private long gasUsedInTheBlock; + private TestSystemProperties config; + private RskAddress receiver; + private RskAddress sender; @BeforeEach void setUp() { // paperwork: mock a whole nice transaction executor + receiver = new RskAddress("0000000000000000000000000000000000000002"); + sender = new RskAddress("0000000000000000000000000000000000000001"); txIndex = 1; - gasUsedInTheBlock = 0; activationConfig = ActivationConfigsForTest.all(); constants = mock(Constants.class); - rskAddress = mock(RskAddress.class); repository = mock(Repository.class); blockStore = mock(BlockStore.class); receiptStore = mock(ReceiptStore.class); blockFactory = mock(BlockFactory.class); programInvokeFactory = mock(ProgramInvokeFactory.class); executionBlock = mock(Block.class); - vmConfig = mock(VmConfig.class); precompiledContracts = mock(PrecompiledContracts.class); - deletedAccounts = new HashSet<>(); + config = spy(new TestSystemProperties()); + when(config.getActivationConfig()).thenReturn(activationConfig); + when(config.getNetworkConstants()).thenReturn(constants); when(executionBlock.getNumber()).thenReturn(10L); } @@ -107,18 +89,12 @@ void testInitHandlesFreeTransactionsOK() { when(transaction.getGasLimit()).thenReturn(BigInteger.valueOf(4000000).toByteArray()); when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); - TransactionExecutor txExecutor = new TransactionExecutor( - constants, activationConfig, transaction, txIndex, rskAddress, - repository, blockStore, receiptStore, blockFactory, - programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, - true, precompiledContracts, deletedAccounts, - blockTxSignatureCache - ); + TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory(config, blockStore, receiptStore, blockFactory, programInvokeFactory, precompiledContracts, blockTxSignatureCache); + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance(transaction, txIndex, executionBlock.getCoinbase(), repository, executionBlock, 0L); when(repository.getNonce(transaction.getSender())).thenReturn(BigInteger.valueOf(1L)); when(transaction.getNonce()).thenReturn(BigInteger.valueOf(1L).toByteArray()); // more paperwork, the receiver is just someone - RskAddress receiver = new RskAddress("0000000000000000000000000000000000000001"); when(transaction.getReceiveAddress()).thenReturn(receiver); when(transaction.acceptTransactionSignature(constants.getChainId())).thenReturn(true); // sender has no balance @@ -136,20 +112,20 @@ void txInBlockIsExecutedAndShouldBeAddedInCache(){ BlockTxSignatureCache blockTxSignatureCache = new BlockTxSignatureCache(receivedTxSignatureCache); MutableRepository cacheTrack = mock(MutableRepository.class); - when(repository.startTracking()).thenReturn(cacheTrack); - - RskAddress sender = new RskAddress("0000000000000000000000000000000000000001"); - RskAddress receiver = new RskAddress("0000000000000000000000000000000000000002"); byte[] gasLimit = BigInteger.valueOf(4000000).toByteArray(); byte[] txNonce = BigInteger.valueOf(1L).toByteArray(); Coin gasPrice = Coin.valueOf(1); Coin value = new Coin(BigInteger.valueOf(2)); - when(repository.getNonce(sender)).thenReturn(BigInteger.valueOf(1L)); - when(repository.getBalance(sender)).thenReturn(new Coin(BigInteger.valueOf(68000L))); + when(repository.startTracking()).thenReturn(cacheTrack); + mockRepositoryForAnAccountWithBalance(sender, 68000L); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); + Transaction transaction = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value); + TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory(config, blockStore, receiptStore, blockFactory, programInvokeFactory, precompiledContracts, blockTxSignatureCache); + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance(transaction, txIndex, executionBlock.getCoinbase(), repository, executionBlock, 0L); - assertTrue(executeValidTransaction(transaction, blockTxSignatureCache)); + assertTrue(txExecutor.executeTransaction()); assertNotNull(blockTxSignatureCache.getSender(transaction)); assertArrayEquals(blockTxSignatureCache.getSender(transaction).getBytes(), sender.getBytes()); } @@ -159,27 +135,27 @@ void TwoTxsAreInBlockAndThemShouldBeContainedInCache() { ReceivedTxSignatureCache receivedTxSignatureCache = mock(ReceivedTxSignatureCache.class); BlockTxSignatureCache blockTxSignatureCache = new BlockTxSignatureCache(receivedTxSignatureCache); MutableRepository cacheTrack = mock(MutableRepository.class); - when(repository.startTracking()).thenReturn(cacheTrack); - RskAddress sender = new RskAddress("0000000000000000000000000000000000000001"); RskAddress sender2 = new RskAddress("0000000000000000000000000000000000000003"); - RskAddress receiver = new RskAddress("0000000000000000000000000000000000000002"); byte[] gasLimit = BigInteger.valueOf(4000000).toByteArray(); byte[] txNonce = BigInteger.valueOf(1L).toByteArray(); Coin gasPrice = Coin.valueOf(1); Coin value = new Coin(BigInteger.valueOf(2)); - when(repository.getNonce(sender)).thenReturn(BigInteger.valueOf(1L)); - when(repository.getBalance(sender)).thenReturn(new Coin(BigInteger.valueOf(68000L))); - Transaction transaction = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value, 1); + when(repository.startTracking()).thenReturn(cacheTrack); + mockRepositoryForAnAccountWithBalance(sender, 68000L); + mockRepositoryForAnAccountWithBalance(sender2, 68000L); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); - assertTrue(executeValidTransaction(transaction, blockTxSignatureCache)); + Transaction transaction = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value, 1); + TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory(config, blockStore, receiptStore, blockFactory, programInvokeFactory, precompiledContracts, blockTxSignatureCache); + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance(transaction, txIndex++, executionBlock.getCoinbase(), repository, executionBlock, 0L); + assertTrue(txExecutor.executeTransaction()); - when(repository.getNonce(sender2)).thenReturn(BigInteger.valueOf(1L)); - when(repository.getBalance(sender2)).thenReturn(new Coin(BigInteger.valueOf(68000L))); Transaction transaction2 = getTransaction(sender2, receiver, gasLimit, txNonce, gasPrice, value, 2); - - assertTrue(executeValidTransaction(transaction2, blockTxSignatureCache)); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); + TransactionExecutor txExecutor1 = transactionExecutorFactory.newInstance(transaction2, txIndex, executionBlock.getCoinbase(), repository, executionBlock, 0L); + assertTrue(txExecutor1.executeTransaction()); assertNotNull(blockTxSignatureCache.getSender(transaction)); assertArrayEquals(blockTxSignatureCache.getSender(transaction).getBytes(), sender.getBytes()); @@ -189,32 +165,23 @@ void TwoTxsAreInBlockAndThemShouldBeContainedInCache() { } @Test - void InvalidTxsIsInBlockAndShouldntBeInCache(){ + void InvalidTxsIsInBlockAndShouldntBeInCache() { ReceivedTxSignatureCache receivedTxSignatureCache = mock(ReceivedTxSignatureCache.class); BlockTxSignatureCache blockTxSignatureCache = new BlockTxSignatureCache(receivedTxSignatureCache); MutableRepository cacheTrack = mock(MutableRepository.class); - when(repository.startTracking()).thenReturn(cacheTrack); - - RskAddress sender = new RskAddress("0000000000000000000000000000000000000001"); - RskAddress receiver = new RskAddress("0000000000000000000000000000000000000002"); byte[] gasLimit = BigInteger.valueOf(4000000).toByteArray(); byte[] txNonce = BigInteger.valueOf(1L).toByteArray(); Coin gasPrice = Coin.valueOf(1); Coin value = new Coin(BigInteger.valueOf(2)); - Transaction transaction = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value); + when(repository.startTracking()).thenReturn(cacheTrack); + mockRepositoryForAnAccountWithBalance(sender, 0L); when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); - when(repository.getNonce(sender)).thenReturn(BigInteger.valueOf(1L)); - when(repository.getBalance(sender)).thenReturn(new Coin(BigInteger.valueOf(0L))); - TransactionExecutor txExecutor = new TransactionExecutor( - constants, activationConfig, transaction, txIndex, rskAddress, - repository, blockStore, receiptStore, blockFactory, - programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, - true, precompiledContracts, deletedAccounts, - blockTxSignatureCache - ); + Transaction transaction = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value); + TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory(config, blockStore, receiptStore, blockFactory, programInvokeFactory, precompiledContracts, blockTxSignatureCache); + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance(transaction, txIndex, executionBlock.getCoinbase(), repository, executionBlock, 0L); assertEquals(0, transaction.transactionCost(constants, activationConfig.forBlock(executionBlock.getNumber()), new BlockTxSignatureCache(new ReceivedTxSignatureCache()))); assertFalse(txExecutor.executeTransaction()); @@ -227,27 +194,19 @@ void remascTxIsReceivedAndShouldntBeInCache(){ BlockTxSignatureCache blockTxSignatureCache = new BlockTxSignatureCache(receivedTxSignatureCache); MutableRepository cacheTrack = mock(MutableRepository.class); - when(repository.startTracking()).thenReturn(cacheTrack); - RskAddress sender = PrecompiledContracts.REMASC_ADDR; - RskAddress receiver = new RskAddress("0000000000000000000000000000000000000002"); byte[] gasLimit = BigInteger.valueOf(4000000).toByteArray(); byte[] txNonce = BigInteger.valueOf(1L).toByteArray(); Coin gasPrice = Coin.valueOf(1); Coin value = new Coin(BigInteger.valueOf(2)); - Transaction transaction = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value); + when(repository.startTracking()).thenReturn(cacheTrack); + mockRepositoryForAnAccountWithBalance(sender, 0L); when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); - when(repository.getNonce(sender)).thenReturn(BigInteger.valueOf(1L)); - when(repository.getBalance(sender)).thenReturn(new Coin(BigInteger.valueOf(0L))); - TransactionExecutor txExecutor = new TransactionExecutor( - constants, activationConfig, transaction, txIndex, rskAddress, - repository, blockStore, receiptStore, blockFactory, - programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, - true, precompiledContracts, deletedAccounts, - blockTxSignatureCache - ); + Transaction transaction = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value); + TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory(config, blockStore, receiptStore, blockFactory, programInvokeFactory, precompiledContracts, blockTxSignatureCache); + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance(transaction, txIndex, executionBlock.getCoinbase(), repository, executionBlock, 0L); assertEquals(0, transaction.transactionCost(constants, activationConfig.forBlock(executionBlock.getNumber()), new BlockTxSignatureCache(new ReceivedTxSignatureCache()))); assertFalse(txExecutor.executeTransaction()); @@ -260,22 +219,25 @@ void txInBlockIsReceivedAndShouldBeUsedInTxExecutorInsteadOfComputeSender(){ BlockTxSignatureCache blockTxSignatureCache = new BlockTxSignatureCache(receivedTxSignatureCache); MutableRepository cacheTrack = mock(MutableRepository.class); - when(repository.startTracking()).thenReturn(cacheTrack); - - RskAddress sender = new RskAddress("0000000000000000000000000000000000000001"); - RskAddress receiver = new RskAddress("0000000000000000000000000000000000000002"); byte[] gasLimit = BigInteger.valueOf(4000000).toByteArray(); byte[] txNonce = BigInteger.valueOf(1L).toByteArray(); Coin gasPrice = Coin.valueOf(1); Coin value = new Coin(BigInteger.valueOf(2)); - when(repository.getNonce(sender)).thenReturn(BigInteger.valueOf(1L)); - when(repository.getBalance(sender)).thenReturn(new Coin(BigInteger.valueOf(68000L))); + when(repository.startTracking()).thenReturn(cacheTrack); + mockRepositoryForAnAccountWithBalance(sender, 68000L); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); + Transaction transaction = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value); when(receivedTxSignatureCache.getSender(transaction)).thenReturn(sender); - assertTrue(executeValidTransaction(transaction, blockTxSignatureCache)); - assertTrue(executeValidTransaction(transaction, blockTxSignatureCache)); //Execute two times the same tx + TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory(config, blockStore, receiptStore, blockFactory, programInvokeFactory, precompiledContracts, blockTxSignatureCache); + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance(transaction, txIndex++, executionBlock.getCoinbase(), repository, executionBlock, 0L); + assertTrue(txExecutor.executeTransaction()); + + TransactionExecutor txExecutor1 = transactionExecutorFactory.newInstance(transaction, txIndex, executionBlock.getCoinbase(), repository, executionBlock, 0L); + assertTrue(txExecutor1.executeTransaction()); //Execute two times the same tx + verify(receivedTxSignatureCache, times(1)).getSender(transaction); assertArrayEquals(blockTxSignatureCache.getSender(transaction).getBytes(), sender.getBytes()); } @@ -286,50 +248,42 @@ void firstTxIsRemovedWhenTheCacheLimitSizeIsExceeded() { BlockTxSignatureCache blockTxSignatureCache = new BlockTxSignatureCache(receivedTxSignatureCache); MutableRepository cacheTrack = mock(MutableRepository.class); - when(repository.startTracking()).thenReturn(cacheTrack); - RskAddress sender = new RskAddress("0000000000000000000000000000000000000001"); - RskAddress receiver = new RskAddress("0000000000000000000000000000000000000002"); byte[] gasLimit = BigInteger.valueOf(4000000).toByteArray(); byte[] txNonce = BigInteger.valueOf(1L).toByteArray(); Coin gasPrice = Coin.valueOf(1); Coin value = new Coin(BigInteger.valueOf(2)); - when(repository.getNonce(sender)).thenReturn(BigInteger.valueOf(1L)); - when(repository.getBalance(sender)).thenReturn(new Coin(BigInteger.valueOf(68000L))); + when(repository.startTracking()).thenReturn(cacheTrack); + mockRepositoryForAnAccountWithBalance(sender, 68000L); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); + Transaction transaction = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value, -1); - assertTrue(executeValidTransaction(transaction, blockTxSignatureCache)); + + TransactionExecutorFactory transactionExecutorFactory = new TransactionExecutorFactory(config, blockStore, receiptStore, blockFactory, programInvokeFactory, precompiledContracts, blockTxSignatureCache); + TransactionExecutor txExecutor = transactionExecutorFactory.newInstance(transaction, txIndex++, executionBlock.getCoinbase(), repository, executionBlock, 0L); + assertTrue(txExecutor.executeTransaction()); for (int i = 0; i < MAX_CACHE_SIZE; i++) { if (i == MAX_CACHE_SIZE - 1) { assertNotNull(blockTxSignatureCache.getSender(transaction)); } sender = new RskAddress(TestUtils.randomAddress().getBytes()); - when(repository.getNonce(sender)).thenReturn(BigInteger.valueOf(1L)); - when(repository.getBalance(sender)).thenReturn(new Coin(BigInteger.valueOf(68000L))); + mockRepositoryForAnAccountWithBalance(sender, 68000L); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); + Transaction transactionAux = getTransaction(sender, receiver, gasLimit, txNonce, gasPrice, value, i); - assertTrue(executeValidTransaction(transactionAux, blockTxSignatureCache)); + txExecutor = transactionExecutorFactory.newInstance(transactionAux, txIndex++, executionBlock.getCoinbase(), repository, executionBlock, 0L); + assertTrue(txExecutor.executeTransaction()); } assertNotNull(blockTxSignatureCache.getSender(transaction)); } - - - private boolean executeValidTransaction(Transaction transaction, BlockTxSignatureCache blockTxSignatureCache) { - when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6800000).toByteArray()); - - TransactionExecutor txExecutor = new TransactionExecutor( - constants, activationConfig, transaction, txIndex, rskAddress, - repository, blockStore, receiptStore, blockFactory, - programInvokeFactory, executionBlock, gasUsedInTheBlock, vmConfig, - true, precompiledContracts, deletedAccounts, - blockTxSignatureCache - ); - - return txExecutor.executeTransaction(); + private void mockRepositoryForAnAccountWithBalance(RskAddress sender, long val) { + when(repository.getNonce(sender)).thenReturn(BigInteger.valueOf(1L)); + when(repository.getBalance(sender)).thenReturn(new Coin(BigInteger.valueOf(val))); } - private Transaction getTransaction(RskAddress sender, RskAddress receiver, byte[] gasLimit, byte[] txNonce, Coin gasPrice, Coin value) { Transaction transaction = mock(Transaction.class); when(transaction.getSender()).thenReturn(sender); From 3e1b9de0d54a568980ccc5b7b0f5e4aeea29be48 Mon Sep 17 00:00:00 2001 From: Julian Len Date: Thu, 20 Apr 2023 15:18:10 -0300 Subject: [PATCH 34/89] Changed the gas limits for all the tests since the new considered gas limit is the sublist one instead of the block gas limit --- .../java/co/rsk/core/bc/BlockExecutor.java | 2 +- .../ethereum/core/TransactionExecutor.java | 4 ++-- .../co/rsk/core/bc/BlockExecutorTest.java | 2 +- .../parallel/ParallelExecutionStateTest.java | 23 +++++++++++++------ .../java/co/rsk/peg/RskForksBridgeTest.java | 5 ++-- .../org/ethereum/rpc/Web3ImplLogsTest.java | 12 ++++------ .../resources/dsl/blake2b/eip_152_example.txt | 3 ++- rskj-core/src/test/resources/dsl/call01.txt | 1 + .../resources/dsl/codeSizeAfterSuicide.txt | 2 ++ .../contract_nested_abi_calls.txt | 7 +++--- .../contract_nested_interface_calls.txt | 7 +++--- .../src/test/resources/dsl/contracts01.txt | 1 + .../src/test/resources/dsl/contracts02.txt | 2 ++ .../src/test/resources/dsl/contracts03.txt | 6 ++++- .../src/test/resources/dsl/contracts05.txt | 1 + .../src/test/resources/dsl/contracts06.txt | 1 + .../src/test/resources/dsl/contracts07.txt | 3 +++ .../src/test/resources/dsl/contracts08.txt | 4 ++++ .../src/test/resources/dsl/contracts09.txt | 1 + rskj-core/src/test/resources/dsl/create01.txt | 2 +- rskj-core/src/test/resources/dsl/create02.txt | 3 +++ .../src/test/resources/dsl/create201.txt | 1 + .../test/resources/dsl/createAfterSuicide.txt | 1 + .../dsl/create_and_preserve_balance.txt | 2 +- .../dsl/create_and_preserve_no_balance.txt | 2 +- .../src/test/resources/dsl/delegatecall01.txt | 1 + .../eth_module/estimateGas/callWithValue.txt | 2 ++ .../callWithValuePlusSstoreRefund.txt | 3 ++- .../dsl/eth_module/estimateGas/gasCap.txt | 1 + .../estimateGas/nestedCallsWithValue.txt | 2 ++ .../nestedCallsWithValueAndStorageRefund.txt | 2 ++ ...CallsWithValueStorageRefundAndFixedGas.txt | 2 ++ .../subsequentCallWithValueCase1.txt | 2 ++ .../subsequentCallWithValueCase2.txt | 2 ++ .../eth_module/estimateGas/updateStorage.txt | 9 ++++---- .../dsl/eth_module/revert_reason.txt | 8 +++---- .../extcodehash_address_does_not_exist.txt | 9 ++++---- ...dehash_address_exists_but_not_contract.txt | 9 ++++---- .../extcodehash_with_cache_empty_deploy.txt | 11 +++++---- ...extcodehash_without_cache_empty_deploy.txt | 17 +++++++------- rskj-core/src/test/resources/dsl/logs01.txt | 1 + .../src/test/resources/dsl/opcode_revert1.txt | 3 +++ .../src/test/resources/dsl/opcode_revert2.txt | 3 +++ .../src/test/resources/dsl/recursive01.txt | 3 ++- .../src/test/resources/dsl/recursive02.txt | 3 ++- 45 files changed, 128 insertions(+), 63 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 17bcdb2147a..9d63f500d99 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -698,7 +698,7 @@ private BlockResult executeForMiningAfterRSKIP144( } private static long getSublistGasLimit(Block block) { - return GasCost.toGas(block.getGasLimit()) / 2; + return GasCost.toGas(block.getGasLimit()) / (Constants.getTransactionExecutionThreads()+1); } private void registerExecutedTx(ProgramTraceProcessor programTraceProcessor, boolean vmTrace, List executedTransactions, Transaction tx, TransactionExecutor txExecutor) { diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index 5fd5c06f4fd..8499d5e831d 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -98,12 +98,12 @@ public class TransactionExecutor { private List logs = null; private final Set deletedAccounts; private final SignatureCache signatureCache; - private long sublistGasLimit; + private final long sublistGasLimit; private boolean localCall = false; private boolean precompiledContractHasBeenCalledFlag = false; - private boolean postponeFeePayment; + private final boolean postponeFeePayment; public TransactionExecutor( Constants constants, ActivationConfig activationConfig, Transaction tx, int txindex, RskAddress coinbase, diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index 0cf6e225cef..06e2d05de26 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -990,7 +990,7 @@ void invalidBlockBadLogsBloom(boolean activeRskip144) { } private static long getSublistGaslimit(Block parent) { - return GasCost.toGas(parent.getGasLimit()) / 2; + return GasCost.toGas(parent.getGasLimit()) / (Constants.getTransactionExecutionThreads() + 1); } private static TestObjects generateBlockWithOneTransaction(Boolean activeRskip144, boolean rskip126IsActive) { diff --git a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java index d1e5239307e..7aadb289319 100644 --- a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java +++ b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java @@ -183,11 +183,12 @@ private String getProxyCreationCode (String address) { " sender acc3\n" + " receiverAddress 00\n" + " data " + creationData + "\n" + - " gas 1200000\n" + + " gas 500000\n" + " build\n" + "\n" + "block_build b01\n" + " parent g00\n" + + " gasLimit 7500000\n" + " transactions tx01\n" + " build\n" + "\n" + @@ -268,7 +269,7 @@ private World processCallWithContract(String firstCall, String secondCall, int r " sender acc3\n" + " receiverAddress 00\n" + " data " + getProxyCreationCode(readWriteAddress) + "\n" + - " gas 1200000\n" + + " gas 600000\n" + " nonce 1\n" + " build\n" + "\n" + @@ -571,6 +572,7 @@ void callCreatedContract() throws DslProcessorException { "\n" + "block_build b01\n" + " parent g00\n" + + " gasLimit 10000000\n" + " transactions tx01 tx02\n" + " build\n" + "\n" + @@ -602,6 +604,7 @@ void sendToCreatedContract() throws DslProcessorException { "\n" + "block_build b01\n" + " parent g00\n" + + " gasLimit 7500000\n" + " transactions tx01 tx02\n" + " build\n" + "\n" + @@ -630,7 +633,7 @@ void useSequentialForGas() throws DslProcessorException { " build\n" + "\n", true), 0); - Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(7500000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[] { 1 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } @@ -677,7 +680,7 @@ void useSequentialForCollisionWithSequential() throws DslProcessorException { "assert_tx_success tx04\n" + "\n", 0); - Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(7500000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); Assertions.assertEquals(3, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[] { 1 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } @@ -723,7 +726,7 @@ void useSequentialForCollisionWithTwoParallel() throws DslProcessorException { "assert_tx_success tx04\n" + "\n", 0); - Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(7500000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); Assertions.assertEquals(3, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[] { 1, 2 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } @@ -769,7 +772,7 @@ void useSequentialForCollisionWithTwoParallelWithoutGas() throws DslProcessorExc "assert_tx_success tx04\n" + "\n", 0); - Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(7500000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); Assertions.assertEquals(3, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[] { 1, 2 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } @@ -824,7 +827,7 @@ void useSequentialForCollisionWithSequentialAndParallel() throws DslProcessorExc "assert_tx_success tx05\n" + "\n", 0); - Assertions.assertEquals(3000000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); + Assertions.assertEquals(7500000L, GasCost.toGas(parallel.getBlockChain().getBestBlock().getHeader().getGasLimit())); Assertions.assertEquals(4, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[] { 1, 2 }, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } @@ -842,6 +845,7 @@ void whenATxCallsAPrecompiledItShouldGoToSequential() throws DslProcessorExcepti "\n" + "block_build b01\n" + " parent g00\n" + + " gasLimit 7500000\n" + " transactions tx01\n" + " build\n" + "\n" + @@ -868,6 +872,7 @@ void whenATxSendsValueToAPrecompiledItShouldGoToSequential() throws DslProcessor "\n" + "block_build b01\n" + " parent g00\n" + + " gasLimit 7500000\n" + " transactions tx01\n" + " build\n" + "\n" + @@ -895,6 +900,7 @@ void whenATxCallsAPrecompiledThatThrowsAnExceptionShouldGoToSequential() throws "\n" + "block_build b01\n" + " parent g00\n" + + " gasLimit 7500000\n" + " transactions tx01\n" + " build\n" + "\n" + @@ -929,6 +935,7 @@ void whenATxCallsAContractThatCallsAPrecompiledShouldGoToSequential() throws Dsl "\n" + "block_build b01\n" + " parent g00\n" + + " gasLimit 7500000\n" + " transactions tx01 tx02\n" + " build\n" + "\n" + @@ -964,6 +971,7 @@ void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequential() "\n" + "block_build b01\n" + " parent g00\n" + + " gasLimit 7500000\n" + " transactions tx01 tx02\n" + " build\n" + "\n" + @@ -1068,6 +1076,7 @@ private static String deployProxyTo(RskAddress address) { "\n" + "block_build b01\n" + " parent g00\n" + + " gasLimit 7500000\n" + " transactions tx01\n" + " build\n" + "\n" + diff --git a/rskj-core/src/test/java/co/rsk/peg/RskForksBridgeTest.java b/rskj-core/src/test/java/co/rsk/peg/RskForksBridgeTest.java index c956f52a37b..4d51dc285a2 100644 --- a/rskj-core/src/test/java/co/rsk/peg/RskForksBridgeTest.java +++ b/rskj-core/src/test/java/co/rsk/peg/RskForksBridgeTest.java @@ -252,7 +252,7 @@ private Transaction buildWhitelistTx() { long nonce = 0; long value = 0; BigInteger gasPrice = BigInteger.valueOf(0); - BigInteger gasLimit = BigInteger.valueOf(1000000); + BigInteger gasLimit = BigInteger.valueOf(500000); Transaction rskTx = CallTransaction.createCallTransaction(nonce, gasPrice.longValue(), gasLimit.longValue(), PrecompiledContracts.BRIDGE_ADDR, value, Bridge.ADD_ONE_OFF_LOCK_WHITELIST_ADDRESS, Constants.REGTEST_CHAIN_ID, new Object[]{ "mhxk5q8QdGFoaP4SJ3DPtXjrbxAgxjNm3C", BigInteger.valueOf(Coin.COIN.multiply(4).value) }); @@ -378,7 +378,7 @@ private Transaction buildReceiveHeadersTx() { long nonce = 0; long value = 0; BigInteger gasPrice = BigInteger.valueOf(0); - BigInteger gasLimit = BigInteger.valueOf(1000000); + BigInteger gasLimit = BigInteger.valueOf(500000); Transaction rskTx = CallTransaction.createCallTransaction(nonce, gasPrice.longValue(), gasLimit.longValue(), PrecompiledContracts.BRIDGE_ADDR, value, Bridge.RECEIVE_HEADERS, Constants.REGTEST_CHAIN_ID, new Object[]{headerArray}); @@ -408,7 +408,6 @@ private Transaction buildRegisterBtcTransactionTx() { private Transaction buildReleaseTx() throws AddressFormatException { String btcAddressString = "mhoDGMzHHDq2ZD6cFrKV9USnMfpxEtLwGm"; - Address btcAddress = Address.fromBase58(RegTestParams.get(), btcAddressString); long nonce = 2; long value = 1000000000000000000l; BigInteger gasPrice = BigInteger.valueOf(0); diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java index 1fbdd06855b..1cfae8802ab 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplLogsTest.java @@ -111,11 +111,10 @@ class Web3ImplLogsTest { private static final String ONE_TOPIC = "0000000000000000000000000000000000000000000000000000000000000001"; private static final String INCREMENT_METHOD_SIGNATURE = "371303c0"; private static final String GET_VALUE_METHOD_SIGNATURE = "20965255"; - private static final String TRACKED_TEST_BLOCK_HASH = "0x1d3137d39f8467053020d1521019d23d33e3a03e92f296f4a0e8ed12b2891ae7"; + private static final String TRACKED_TEST_BLOCK_HASH = "0x00ed383312dfc27d0ff0b85632ac24380a32ce511bb6bcb776d5812f35d4d157"; private static final String UNTRACKED_TEST_BLOCK_HASH = "0xdea168a4f74e51a3eeb6d72b049c4fc7bc750dd51f13a3afa4fee4bece0e85eb"; private final TestSystemProperties config = new TestSystemProperties(); private Blockchain blockChain; - private MiningMainchainView mainchainView; private RepositoryLocator repositoryLocator; private TransactionPool transactionPool; private Ethereum eth; @@ -134,7 +133,6 @@ void setUp() { blockChain = factory.getBlockchain(); blockStore = factory.getBlockStore(); trieStore = factory.getTrieStore(); - mainchainView = factory.getMiningMainchainView(); repositoryLocator = factory.getRepositoryLocator(); transactionPool = factory.getTransactionPool(); eth = factory.getRsk(); @@ -1287,7 +1285,7 @@ function getValue() constant returns (uint) { } */ return new TransactionBuilder() .sender(acc1) - .gasLimit(BigInteger.valueOf(1000000)) + .gasLimit(BigInteger.valueOf(500000)) .gasPrice(BigInteger.ONE) .data(compiled_0_4_11) .build(); @@ -1329,7 +1327,7 @@ function emit(uint n){ return new TransactionBuilder() .sender(acc1) - .gasLimit(BigInteger.valueOf(1000000)) + .gasLimit(BigInteger.valueOf(500000)) .gasPrice(BigInteger.ONE) .data(compiledLogExample) .build(); @@ -1359,7 +1357,7 @@ function doSomething(address mainAddr) { String compiledCaller = "606060405234610000576040516020806101f8833981016040528080519060200190919050505b8073ffffffffffffffffffffffffffffffffffffffff1663195977a66130396040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b156100005760325a03f115610000575050507f2012ef02e82e91abf55727cc31c3b6e3375003aa9e879f855db72d9e78822c40607b6040518082815260200191505060405180910390a15b505b610111806100e76000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063e60c2d4414603c575b6000565b34600057606a600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050606c565b005b8073ffffffffffffffffffffffffffffffffffffffff1663195977a661303a6040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b1560005760325a03f1156000575050505b505600a165627a7a72305820f8bc730651ba568de3f84a81088f94a8701c5c41f732d5c7a447077ee40f97a80029"; return new TransactionBuilder() .sender(acc1) - .gasLimit(BigInteger.valueOf(1000000)) + .gasLimit(BigInteger.valueOf(500000)) .gasPrice(BigInteger.ONE) .data( compiledCaller + address) .nonce(1) @@ -1377,7 +1375,7 @@ private static Transaction getCallerContractTransactionWithInvoke(Account acc1, return new TransactionBuilder() .sender(acc1) .receiverAddress(receiverAddress) - .gasLimit(BigInteger.valueOf(1000000)) + .gasLimit(BigInteger.valueOf(500000)) .gasPrice(BigInteger.ONE) .data(ByteUtil.toHexString(func.encode("0x" + address))) .nonce(2) diff --git a/rskj-core/src/test/resources/dsl/blake2b/eip_152_example.txt b/rskj-core/src/test/resources/dsl/blake2b/eip_152_example.txt index 511af853320..5663d776711 100644 --- a/rskj-core/src/test/resources/dsl/blake2b/eip_152_example.txt +++ b/rskj-core/src/test/resources/dsl/blake2b/eip_152_example.txt @@ -81,11 +81,12 @@ transaction_build tx02 value 0 #data 12ca565c data 82fe1c9b - gas 1000000000 + gas 100000000 build block_build b01 parent g00 + gasLimit 3000000000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/call01.txt b/rskj-core/src/test/resources/dsl/call01.txt index 5b81c63351e..af368679a26 100644 --- a/rskj-core/src/test/resources/dsl/call01.txt +++ b/rskj-core/src/test/resources/dsl/call01.txt @@ -36,6 +36,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/codeSizeAfterSuicide.txt b/rskj-core/src/test/resources/dsl/codeSizeAfterSuicide.txt index 6453e86541e..2469ecb6f4b 100644 --- a/rskj-core/src/test/resources/dsl/codeSizeAfterSuicide.txt +++ b/rskj-core/src/test/resources/dsl/codeSizeAfterSuicide.txt @@ -27,6 +27,7 @@ transaction_build callCodeSizeChecker block_build b01 parent g00 + gasLimit 7500000 transactions createCreatorAndRun build @@ -34,6 +35,7 @@ block_connect b01 block_build b02 parent b01 + gasLimit 7500000 transactions runSuicider callCodeSizeChecker build diff --git a/rskj-core/src/test/resources/dsl/contract_call/contract_nested_abi_calls.txt b/rskj-core/src/test/resources/dsl/contract_call/contract_nested_abi_calls.txt index 591f17c1980..6cb0f3933e2 100644 --- a/rskj-core/src/test/resources/dsl/contract_call/contract_nested_abi_calls.txt +++ b/rskj-core/src/test/resources/dsl/contract_call/contract_nested_abi_calls.txt @@ -104,6 +104,7 @@ transaction_build tx03 block_build b01 parent g00 + gasLimit 10000000 transactions tx01 tx02 tx03 build @@ -120,13 +121,13 @@ transaction_build tx04 receiverAddress 27444fbce96cb2d27b94e116d1506d7739c05862 value 0 data d96a094a0000000000000000000000000000000000000000000000000000000000000000 - gas 2000000 + gas 500000 build block_build b02 parent b01 transactions tx04 - gasLimit 6500000 + gasLimit 10000000 build block_connect b02 @@ -147,7 +148,7 @@ transaction_build tx05 block_build b03 parent b02 transactions tx05 - gasLimit 6500000 + gasLimit 10000000 build block_connect b03 diff --git a/rskj-core/src/test/resources/dsl/contract_call/contract_nested_interface_calls.txt b/rskj-core/src/test/resources/dsl/contract_call/contract_nested_interface_calls.txt index b86537696a0..74c56e41f1d 100644 --- a/rskj-core/src/test/resources/dsl/contract_call/contract_nested_interface_calls.txt +++ b/rskj-core/src/test/resources/dsl/contract_call/contract_nested_interface_calls.txt @@ -98,6 +98,7 @@ transaction_build tx03 block_build b01 parent g00 + gasLimit 10000000 transactions tx01 tx02 tx03 build @@ -113,13 +114,13 @@ transaction_build tx04 receiverAddress 27444fbce96cb2d27b94e116d1506d7739c05862 value 0 data d96a094a0000000000000000000000000000000000000000000000000000000000000000 - gas 2000000 + gas 500000 build block_build b02 parent b01 transactions tx04 - gasLimit 6500000 + gasLimit 10000000 build block_connect b02 @@ -140,7 +141,7 @@ transaction_build tx05 block_build b03 parent b02 transactions tx05 - gasLimit 6500000 + gasLimit 10000000 build block_connect b03 diff --git a/rskj-core/src/test/resources/dsl/contracts01.txt b/rskj-core/src/test/resources/dsl/contracts01.txt index 1b727bc67ea..66972166f69 100644 --- a/rskj-core/src/test/resources/dsl/contracts01.txt +++ b/rskj-core/src/test/resources/dsl/contracts01.txt @@ -10,6 +10,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/contracts02.txt b/rskj-core/src/test/resources/dsl/contracts02.txt index 50b29cc46e6..40bdc334c5d 100644 --- a/rskj-core/src/test/resources/dsl/contracts02.txt +++ b/rskj-core/src/test/resources/dsl/contracts02.txt @@ -10,6 +10,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 3000000000 transactions tx01 build @@ -29,6 +30,7 @@ transaction_build tx02 block_build b02 parent b01 + gasLimit 3000000000 transactions tx02 build diff --git a/rskj-core/src/test/resources/dsl/contracts03.txt b/rskj-core/src/test/resources/dsl/contracts03.txt index 47078752831..bd6a6923d32 100644 --- a/rskj-core/src/test/resources/dsl/contracts03.txt +++ b/rskj-core/src/test/resources/dsl/contracts03.txt @@ -10,6 +10,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build @@ -29,6 +30,7 @@ transaction_build tx02 block_build b02 parent b01 + gasLimit 7500000 transactions tx02 build @@ -51,6 +53,7 @@ transaction_build tx03 block_build b03 parent b02 + gasLimit 7500000 transactions tx03 build @@ -73,6 +76,7 @@ transaction_build tx04 block_build b04 parent b03 + gasLimit 75000000 transactions tx04 build @@ -120,7 +124,7 @@ transaction_build tx06 contract tx01 # created in tx01 value 5000 data 9530dcee - gas 1000000 + gas 500000 build block_build b06 diff --git a/rskj-core/src/test/resources/dsl/contracts05.txt b/rskj-core/src/test/resources/dsl/contracts05.txt index f47da56a892..fe4b76c9973 100644 --- a/rskj-core/src/test/resources/dsl/contracts05.txt +++ b/rskj-core/src/test/resources/dsl/contracts05.txt @@ -10,6 +10,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/contracts06.txt b/rskj-core/src/test/resources/dsl/contracts06.txt index 3b09b09b929..189f0d797ec 100644 --- a/rskj-core/src/test/resources/dsl/contracts06.txt +++ b/rskj-core/src/test/resources/dsl/contracts06.txt @@ -16,6 +16,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/contracts07.txt b/rskj-core/src/test/resources/dsl/contracts07.txt index 77a27de9d03..50fddc13fbd 100644 --- a/rskj-core/src/test/resources/dsl/contracts07.txt +++ b/rskj-core/src/test/resources/dsl/contracts07.txt @@ -13,6 +13,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build @@ -34,6 +35,7 @@ transaction_build tx02 block_build b02 parent b01 + gasLimit 7500000 transactions tx02 build @@ -55,6 +57,7 @@ transaction_build tx03 block_build b03 parent b02 + gasLimit 7500000 transactions tx03 build diff --git a/rskj-core/src/test/resources/dsl/contracts08.txt b/rskj-core/src/test/resources/dsl/contracts08.txt index ba4f9559f60..561752bf98d 100644 --- a/rskj-core/src/test/resources/dsl/contracts08.txt +++ b/rskj-core/src/test/resources/dsl/contracts08.txt @@ -107,6 +107,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build @@ -129,6 +130,7 @@ transaction_build tx02 block_build b02 parent b01 + gasLimit 6800000 transactions tx02 build @@ -150,6 +152,7 @@ transaction_build tx03 block_build b03 parent b02 + gasLimit 6800000 transactions tx03 build @@ -171,6 +174,7 @@ transaction_build tx04 block_build b04 parent b03 + gasLimit 6800000 transactions tx04 build diff --git a/rskj-core/src/test/resources/dsl/contracts09.txt b/rskj-core/src/test/resources/dsl/contracts09.txt index 36b698d77f7..271d610024a 100644 --- a/rskj-core/src/test/resources/dsl/contracts09.txt +++ b/rskj-core/src/test/resources/dsl/contracts09.txt @@ -14,6 +14,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/create01.txt b/rskj-core/src/test/resources/dsl/create01.txt index 91981e389dc..bb3aa07d067 100644 --- a/rskj-core/src/test/resources/dsl/create01.txt +++ b/rskj-core/src/test/resources/dsl/create01.txt @@ -7,7 +7,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820b25edb28bec763685838b8044760e105b5385638276b4768c8045237b8fc6bf10029 - gas 1200000 + gas 500000 build block_build b01 diff --git a/rskj-core/src/test/resources/dsl/create02.txt b/rskj-core/src/test/resources/dsl/create02.txt index 1faa17cc676..dee1b5104e5 100644 --- a/rskj-core/src/test/resources/dsl/create02.txt +++ b/rskj-core/src/test/resources/dsl/create02.txt @@ -18,10 +18,12 @@ transaction_build tx02 block_build b01b parent g00 + gasLimit 7500000 build block_build b01 parent g00 + gasLimit 7500000 transactions tx02 build @@ -29,6 +31,7 @@ block_connect b01b block_build b02b parent b01b + gasLimit 7500000 transactions tx02 build diff --git a/rskj-core/src/test/resources/dsl/create201.txt b/rskj-core/src/test/resources/dsl/create201.txt index c00b7a619a3..9fccb1e3079 100644 --- a/rskj-core/src/test/resources/dsl/create201.txt +++ b/rskj-core/src/test/resources/dsl/create201.txt @@ -24,6 +24,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/createAfterSuicide.txt b/rskj-core/src/test/resources/dsl/createAfterSuicide.txt index 1da8c0cd38a..b594174c4e8 100644 --- a/rskj-core/src/test/resources/dsl/createAfterSuicide.txt +++ b/rskj-core/src/test/resources/dsl/createAfterSuicide.txt @@ -27,6 +27,7 @@ transaction_build callSecondCreator block_build b01 parent g00 + gasLimit 6800000 transactions createCreatorAndRun build diff --git a/rskj-core/src/test/resources/dsl/create_and_preserve_balance.txt b/rskj-core/src/test/resources/dsl/create_and_preserve_balance.txt index e278173938e..661bc25c69e 100644 --- a/rskj-core/src/test/resources/dsl/create_and_preserve_balance.txt +++ b/rskj-core/src/test/resources/dsl/create_and_preserve_balance.txt @@ -46,7 +46,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820b25edb28bec763685838b8044760e105b5385638276b4768c8045237b8fc6bf10029 - gas 1200000 + gas 500000 build # build block #5 with rskip174 activated diff --git a/rskj-core/src/test/resources/dsl/create_and_preserve_no_balance.txt b/rskj-core/src/test/resources/dsl/create_and_preserve_no_balance.txt index 960f0b6e44c..204620b99bc 100644 --- a/rskj-core/src/test/resources/dsl/create_and_preserve_no_balance.txt +++ b/rskj-core/src/test/resources/dsl/create_and_preserve_no_balance.txt @@ -31,7 +31,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00a165627a7a72305820b25edb28bec763685838b8044760e105b5385638276b4768c8045237b8fc6bf10029 - gas 1200000 + gas 500000 build block_build b02 diff --git a/rskj-core/src/test/resources/dsl/delegatecall01.txt b/rskj-core/src/test/resources/dsl/delegatecall01.txt index 9cc1a3a8dd6..c958d9dc366 100644 --- a/rskj-core/src/test/resources/dsl/delegatecall01.txt +++ b/rskj-core/src/test/resources/dsl/delegatecall01.txt @@ -35,6 +35,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValue.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValue.txt index 229975ca8dd..141f7bf0a72 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValue.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValue.txt @@ -26,6 +26,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build @@ -43,6 +44,7 @@ transaction_build tx02 block_build b02 parent b01 + gasLimit 6800000 #transactions tx02 build diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValuePlusSstoreRefund.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValuePlusSstoreRefund.txt index 69eeaabf81a..b100b39b83e 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValuePlusSstoreRefund.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/callWithValuePlusSstoreRefund.txt @@ -37,6 +37,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build @@ -57,7 +58,7 @@ transaction_build tx02 block_build b02 parent b01 transactions tx02 - gasLimit 6800000 + gasLimit 7500000 build #block_connect b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/gasCap.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/gasCap.txt index d245d282947..370c100af4f 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/gasCap.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/gasCap.txt @@ -27,6 +27,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValue.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValue.txt index e5deec4c7f2..17406b79474 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValue.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValue.txt @@ -64,6 +64,7 @@ transaction_build tx03 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 tx02 tx03 build @@ -99,6 +100,7 @@ transaction_build tx06 block_build b02 parent b01 + gasLimit 7500000 transactions tx04 tx05 tx06 build diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueAndStorageRefund.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueAndStorageRefund.txt index c40e44fcdce..22aac21db9c 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueAndStorageRefund.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueAndStorageRefund.txt @@ -79,6 +79,7 @@ transaction_build tx03 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 tx02 tx03 build @@ -114,6 +115,7 @@ transaction_build tx06 block_build b02 parent b01 + gasLimit 7500000 transactions tx04 tx05 tx06 build diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueStorageRefundAndFixedGas.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueStorageRefundAndFixedGas.txt index 0fcc583ed90..e0a47bfef3c 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueStorageRefundAndFixedGas.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/nestedCallsWithValueStorageRefundAndFixedGas.txt @@ -79,6 +79,7 @@ transaction_build tx03 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 tx02 tx03 build @@ -114,6 +115,7 @@ transaction_build tx06 block_build b02 parent b01 + gasLimit 7500000 transactions tx04 tx05 tx06 build diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase1.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase1.txt index e22c3e8d04b..99791f0e461 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase1.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase1.txt @@ -71,6 +71,7 @@ transaction_build tx03 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 tx02 tx03 build @@ -115,6 +116,7 @@ transaction_build tx07 block_build b02 parent b01 + gasLimit 7500000 transactions tx04 tx05 tx06 tx07 build diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase2.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase2.txt index fdc1840e451..79842272f00 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase2.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/subsequentCallWithValueCase2.txt @@ -71,6 +71,7 @@ transaction_build tx03 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 tx02 tx03 build @@ -133,6 +134,7 @@ transaction_build tx09 block_build b02 parent b01 + gasLimit 7500000 transactions tx04 tx05 tx06 tx07 tx08 tx09 build diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/updateStorage.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/updateStorage.txt index 2d3a0bc175f..31c7106fbab 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/updateStorage.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/updateStorage.txt @@ -27,11 +27,12 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b50610144806100206000396000f3fe60806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635e383d21146100515780637b8d56e3146100a0575b600080fd5b34801561005d57600080fd5b5061008a6004803603602081101561007457600080fd5b81019080803590602001909291905050506100e5565b6040518082815260200191505060405180910390f35b3480156100ac57600080fd5b506100e3600480360360408110156100c357600080fd5b8101908080359060200190929190803590602001909291905050506100ff565b005b6000816064811015156100f457fe5b016000915090505481565b8060008360648110151561010f57fe5b0181905550505056fea165627a7a72305820d27e1f1cce816508bdc0611d325d8a6695742256aac16b8ea5b69034d0da31830029 - gas 1000000 + gas 500000 build block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build @@ -48,7 +49,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data 7b8d56e30000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a - gas 3400000 + gas 500000 build transaction_build tx03 @@ -57,13 +58,13 @@ transaction_build tx03 contract tx01 # created in tx01 value 0 data 7b8d56e30000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000002a - gas 3400000 + gas 500000 build block_build b02 parent b01 transactions tx02 tx03 - gasLimit 6800000 + gasLimit 7500000 build block_connect b02 diff --git a/rskj-core/src/test/resources/dsl/eth_module/revert_reason.txt b/rskj-core/src/test/resources/dsl/eth_module/revert_reason.txt index d04ce79a9a8..9e745363b81 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/revert_reason.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/revert_reason.txt @@ -30,13 +30,13 @@ transaction_build tx01 receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061010f806100206000396000f3fe6080604052600436106039576000357c010000000000000000000000000000000000000000000000000000000090048063d96a094a14603e575b600080fd5b606760048036036020811015605257600080fd5b81019080803590602001909291905050506069565b005b60008111151560e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e656761746976652076616c75652e000000000000000000000000000000000081525060200191505060405180910390fd5b5056fea165627a7a7230582028a98e792c3f36ab792c10a5bd8d5f46b19e22fbc9c43635c77fec5a5858254e0029 - gas 2000000 + gas 500000 build block_build b01 parent g00 transactions tx01 - gasLimit 6500000 + gasLimit 7500000 build block_connect b01 @@ -52,13 +52,13 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data d96a094a0000000000000000000000000000000000000000000000000000000000000000 - gas 2000000 + gas 500000 build block_build b02 parent b01 transactions tx02 - gasLimit 6500000 + gasLimit 7500000 build block_connect b02 diff --git a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_does_not_exist.txt b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_does_not_exist.txt index 6984baffc26..0ad9510a9b6 100644 --- a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_does_not_exist.txt +++ b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_does_not_exist.txt @@ -33,6 +33,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build @@ -47,13 +48,13 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data d13319c4 - gas 1000000000 + gas 1000000 build block_build b02 parent b01 transactions tx02 - gasLimit 3000000000 + gasLimit 30000000 build block_connect b02 @@ -81,13 +82,13 @@ transaction_build tx03 contract tx01 # created in tx01 value 0 data d13319c4 - gas 1000000000 + gas 1000000 build block_build b05 parent b04 transactions tx03 - gasLimit 3000000000 + gasLimit 30000000 build block_connect b05 diff --git a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_exists_but_not_contract.txt b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_exists_but_not_contract.txt index 769c9a9af7f..d24e699f5c8 100644 --- a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_exists_but_not_contract.txt +++ b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_address_exists_but_not_contract.txt @@ -34,6 +34,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build @@ -48,13 +49,13 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data d13319c4 - gas 1000000000 + gas 1000000 build block_build b02 parent b01 transactions tx02 - gasLimit 3000000000 + gasLimit 30000000 build block_connect b02 @@ -82,13 +83,13 @@ transaction_build tx03 contract tx01 # created in tx01 value 0 data d13319c4 - gas 1000000000 + gas 1000000 build block_build b05 parent b04 transactions tx03 - gasLimit 3000000000 + gasLimit 30000000 build block_connect b05 diff --git a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_with_cache_empty_deploy.txt b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_with_cache_empty_deploy.txt index 7e405380731..fb7b0bdd5bf 100644 --- a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_with_cache_empty_deploy.txt +++ b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_with_cache_empty_deploy.txt @@ -44,6 +44,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build @@ -58,13 +59,13 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data 775c300c - gas 1000000000 + gas 1000000 build block_build b02 parent b01 transactions tx02 - gasLimit 3000000000 + gasLimit 6800000 build block_connect b02 @@ -84,6 +85,7 @@ transaction_build tx03 block_build b03 parent b02 + gasLimit 6800000 transactions tx03 build @@ -93,6 +95,7 @@ assert_best b03 # empty block just to get to the activation block_build b04 parent b03 + gasLimit 6800000 build block_connect b04 @@ -105,13 +108,13 @@ transaction_build tx04 contract tx03 # created in tx01 value 0 data 775c300c - gas 1000000000 + gas 1000000 build block_build b05 parent b04 transactions tx04 - gasLimit 3000000000 + gasLimit 30000000 build block_connect b05 diff --git a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_without_cache_empty_deploy.txt b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_without_cache_empty_deploy.txt index 4a76021add5..5c87b8b3121 100644 --- a/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_without_cache_empty_deploy.txt +++ b/rskj-core/src/test/resources/dsl/extcodehash/extcodehash_without_cache_empty_deploy.txt @@ -44,6 +44,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build @@ -58,13 +59,13 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data 775c300c - gas 1000000000 + gas 1000000 build block_build b02 parent b01 transactions tx02 - gasLimit 3000000000 + gasLimit 30000000 build block_connect b02 @@ -78,13 +79,13 @@ transaction_build tx03 contract tx01 # created in tx01 value 0 data d13319c4 - gas 1000000000 + gas 1000000 build block_build b03 parent b02 transactions tx03 - gasLimit 3000000000 + gasLimit 30000000 build block_connect b03 @@ -99,13 +100,13 @@ transaction_build tx04 contract tx01 # created in tx01 value 0 data d13319c4 - gas 1000000000 + gas 1000000 build block_build b04 parent b03 transactions tx04 - gasLimit 3000000000 + gasLimit 30000000 build block_connect b04 @@ -118,13 +119,13 @@ transaction_build tx05 contract tx01 # created in tx01 value 0 data d13319c4 - gas 1000000000 + gas 1000000 build block_build b05 parent b04 transactions tx05 - gasLimit 3000000000 + gasLimit 30000000 build block_connect b05 diff --git a/rskj-core/src/test/resources/dsl/logs01.txt b/rskj-core/src/test/resources/dsl/logs01.txt index d34428ffd33..5d47d8db641 100644 --- a/rskj-core/src/test/resources/dsl/logs01.txt +++ b/rskj-core/src/test/resources/dsl/logs01.txt @@ -13,6 +13,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build diff --git a/rskj-core/src/test/resources/dsl/opcode_revert1.txt b/rskj-core/src/test/resources/dsl/opcode_revert1.txt index 1f9899c6560..dfa1582ef61 100644 --- a/rskj-core/src/test/resources/dsl/opcode_revert1.txt +++ b/rskj-core/src/test/resources/dsl/opcode_revert1.txt @@ -10,6 +10,7 @@ transaction_build contract_with_revert block_build b01 parent g00 + gasLimit 7500000 transactions contract_with_revert build @@ -31,6 +32,7 @@ transaction_build tx02 block_build b02 parent b01 + gasLimit 7500000 transactions tx02 build @@ -53,6 +55,7 @@ transaction_build tx03 block_build b03 parent b02 + gasLimit 7500000 transactions tx03 build diff --git a/rskj-core/src/test/resources/dsl/opcode_revert2.txt b/rskj-core/src/test/resources/dsl/opcode_revert2.txt index 587af03a1be..9fab4bbbdd1 100644 --- a/rskj-core/src/test/resources/dsl/opcode_revert2.txt +++ b/rskj-core/src/test/resources/dsl/opcode_revert2.txt @@ -10,6 +10,7 @@ transaction_build contract_with_revert block_build b01 parent g00 + gasLimit 7500000 transactions contract_with_revert build @@ -31,6 +32,7 @@ transaction_build tx02 block_build b02 parent b01 + gasLimit 7500000 transactions tx02 build @@ -53,6 +55,7 @@ transaction_build tx03 block_build b03 parent b02 + gasLimit 7500000 transactions tx03 build diff --git a/rskj-core/src/test/resources/dsl/recursive01.txt b/rskj-core/src/test/resources/dsl/recursive01.txt index 985d43490e2..134062f2a3e 100644 --- a/rskj-core/src/test/resources/dsl/recursive01.txt +++ b/rskj-core/src/test/resources/dsl/recursive01.txt @@ -81,6 +81,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build @@ -103,7 +104,7 @@ transaction_build tx02 block_build b02 parent b01 transactions tx02 - gasLimit 6500000 + gasLimit 65000000 build block_connect b02 diff --git a/rskj-core/src/test/resources/dsl/recursive02.txt b/rskj-core/src/test/resources/dsl/recursive02.txt index 5082065122e..97d7df47d9a 100644 --- a/rskj-core/src/test/resources/dsl/recursive02.txt +++ b/rskj-core/src/test/resources/dsl/recursive02.txt @@ -81,6 +81,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 7500000 transactions tx01 build @@ -97,7 +98,7 @@ transaction_build tx02 contract tx01 # created in tx01 value 0 data 7cf5dab00000000000000000000000000000000000000000000000000000000000000191 - gas 6500000 + gas 2500000 build block_build b02 From 40e3ebf759e448fca3c02b0810edcdb0c3d2fea7 Mon Sep 17 00:00:00 2001 From: Julian Len Date: Tue, 25 Apr 2023 15:54:23 -0300 Subject: [PATCH 35/89] Tested that even with the sublists full the used gas states below or equal to the block gas limit --- .../java/co/rsk/core/bc/BlockExecutorTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index 06e2d05de26..872d4f6da59 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -546,6 +546,24 @@ void executeSequentiallyTenIndependentTxsAndThemShouldGoInBothSublists(boolean a Assertions.assertEquals(i, transactionReceipts.size()); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void gasUsedShouldNeverSurprassBlockGasLimit(boolean activeRskip144) { + if (!activeRskip144) { + return; + } + + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); + Block parent = blockchain.getBestBlock(); + int gasLimit = 21000; + int transactionNumberToFillParallelSublist = (int) (getSublistGaslimit(parent) / gasLimit); + int totalNumberOfSublists = Constants.getTransactionExecutionThreads() + 1; + int totalTxs = (transactionNumberToFillParallelSublist) * totalNumberOfSublists + 1; + Block block = getBlockWithNIndependentTransactions(totalTxs, BigInteger.valueOf(gasLimit), false); + BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); + Assertions.assertFalse(block.getGasUsed() > GasCost.toGas(block.getGasLimit())); + } + @ParameterizedTest @ValueSource(booleans = {true, false}) void whenParallelSublistsAreFullTheLastTxShouldGoToSequential(boolean activeRskip144) { From fe1ba90aa1752a74d586ae47175d3eff85880b01 Mon Sep 17 00:00:00 2001 From: Julian Len Date: Thu, 27 Apr 2023 12:36:48 -0300 Subject: [PATCH 36/89] Renaming of variables --- .../org/ethereum/core/TransactionExecutor.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index 8499d5e831d..54c6c05363c 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -77,7 +77,7 @@ public class TransactionExecutor { private final PrecompiledContracts precompiledContracts; private final boolean enableRemasc; private String executionError = ""; - private final long gasUsedInTheContainer; + private final long gasUsed; private Coin paidFees; private final ProgramInvokeFactory programInvokeFactory; @@ -108,7 +108,7 @@ public class TransactionExecutor { public TransactionExecutor( Constants constants, ActivationConfig activationConfig, Transaction tx, int txindex, RskAddress coinbase, Repository track, BlockStore blockStore, ReceiptStore receiptStore, BlockFactory blockFactory, - ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsedInTheContainer, VmConfig vmConfig, + ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsed, VmConfig vmConfig, boolean remascEnabled, PrecompiledContracts precompiledContracts, Set deletedAccounts, SignatureCache signatureCache, boolean postponeFeePayment, long sublistGasLimit) { this.constants = constants; @@ -124,7 +124,7 @@ public TransactionExecutor( this.blockFactory = blockFactory; this.programInvokeFactory = programInvokeFactory; this.executionBlock = executionBlock; - this.gasUsedInTheContainer = gasUsedInTheContainer; + this.gasUsed = gasUsed; this.vmConfig = vmConfig; this.precompiledContracts = precompiledContracts; this.enableRemasc = remascEnabled; @@ -163,9 +163,9 @@ private boolean init() { } long txGasLimit = GasCost.toGas(tx.getGasLimit()); - long containerGasLimit = activations.isActive(RSKIP144)? sublistGasLimit : GasCost.toGas(executionBlock.getGasLimit()); + long gasLimit = activations.isActive(RSKIP144)? sublistGasLimit : GasCost.toGas(executionBlock.getGasLimit()); - if (!gasIsValid(txGasLimit, containerGasLimit)) { + if (!gasIsValid(txGasLimit, gasLimit)) { return false; } @@ -253,7 +253,7 @@ private boolean gasIsValid(long txGasLimit, long curContainerGasLimit) { // which is used on some stress tests, but its far from being practical // as the current gas limit on blocks is 6.8M... several orders of magnitude // less than the theoretical max gas on blocks. - long cumulativeGas = GasCost.add(txGasLimit, gasUsedInTheContainer); + long cumulativeGas = GasCost.add(txGasLimit, gasUsed); boolean cumulativeGasReached = cumulativeGas > curContainerGasLimit || cumulativeGas == GasCost.MAX_GAS; if (cumulativeGasReached) { @@ -494,7 +494,7 @@ private void createContract() { public TransactionReceipt getReceipt() { if (receipt == null) { receipt = new TransactionReceipt(); - long totalGasUsed = GasCost.add(gasUsedInTheContainer, getGasUsed()); + long totalGasUsed = GasCost.add(gasUsed, getGasUsed()); receipt.setCumulativeGas(totalGasUsed); receipt.setTransaction(tx); receipt.setLogInfoList(getVMLogs()); From 6b21bb603cc399504bd72b367b8aa78a04d50bf3 Mon Sep 17 00:00:00 2001 From: Julian Len Date: Wed, 3 May 2023 16:26:59 -0300 Subject: [PATCH 37/89] Fixes for sonarcloud --- .../co/rsk/core/TransactionListExecutor.java | 2 +- .../java/co/rsk/core/bc/BlockExecutor.java | 6 +-- .../bc/ParallelizeTransactionHandler.java | 16 ++++---- .../blockchain/upgrades/ActivationConfig.java | 5 ++- .../java/org/ethereum/core/BlockHeader.java | 9 +++-- .../org/ethereum/core/BlockHeaderBuilder.java | 28 +++++++------- .../ethereum/core/BlockHeaderExtension.java | 4 +- .../ethereum/core/BlockHeaderExtensionV1.java | 37 +++++++++---------- .../java/org/ethereum/core/BlockHeaderV1.java | 14 +++---- .../ethereum/core/TransactionExecutor.java | 16 ++++---- .../co/rsk/core/BlockHeaderExtensionTest.java | 4 +- .../rsk/core/BlockHeaderExtensionV1Test.java | 14 +++---- .../java/co/rsk/core/BlockHeaderV0Test.java | 2 +- .../java/co/rsk/core/BlockHeaderV1Test.java | 2 +- .../co/rsk/core/TransactionIsRemascTest.java | 2 +- .../bc/ParallelizeTransactionHandlerTest.java | 2 +- .../core/bc/ReadWrittenKeysTrackerTest.java | 2 +- .../parallel/ParallelExecutionStateTest.java | 4 +- .../co/rsk/db/RepositoryTrackingTest.java | 8 ++-- .../co/rsk/mine/BlockToMineBuilderTest.java | 8 ++-- .../ValidTxExecutionSublistsEdgesTest.java | 2 +- .../src/test/java/co/rsk/vm/MinerHelper.java | 3 +- .../upgrades/ActivationConfigTest.java | 4 +- .../ethereum/core/BlockHeaderBuilderTest.java | 6 +-- 24 files changed, 103 insertions(+), 97 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 46b0dfc8c49..23d4fff7bf8 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -122,7 +122,7 @@ public Boolean call() { logger.trace("tx[{}] executed", i + 1); logger.trace("track commit"); - long txGasUsed = txExecutor.getGasUsed(); + long txGasUsed = txExecutor.getGasConsumed(); totalGasUsed += txGasUsed; addPaidFeesToToal(txExecutor); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 9d63f500d99..84109c9d597 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -364,7 +364,7 @@ private BlockResult executeInternal( registerExecutedTx(programTraceProcessor, vmTrace, executedTransactions, tx, txExecutor); - long gasUsed = txExecutor.getGasUsed(); + long gasUsed = txExecutor.getGasConsumed(); totalGasUsed += gasUsed; totalPaidFees = addTotalPaidFees(totalPaidFees, txExecutor); @@ -633,7 +633,7 @@ private BlockResult executeForMiningAfterRSKIP144( parallelizeTransactionHandler, tx, tx.isRemascTransaction(txindex, transactionsList.size()), - txExecutor.getGasUsed(), + txExecutor.getGasConsumed(), txExecutor.precompiledContractHasBeenCalled()); if (!acceptInvalidTransactions && !sublistGasAccumulated.isPresent()) { @@ -648,7 +648,7 @@ private BlockResult executeForMiningAfterRSKIP144( registerTxExecutedForMiningAfterRSKIP144(readWrittenKeysTracker, tx, txExecutor); - long gasUsed = txExecutor.getGasUsed(); + long gasUsed = txExecutor.getGasConsumed(); totalGasUsed += gasUsed; totalPaidFees = addTotalPaidFees(totalPaidFees, txExecutor); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index f15941af371..8c3ae580d7b 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -45,13 +45,13 @@ public ParallelizeTransactionHandler(short numberOfSublists, long sublistGasLimi public Optional addTransaction(Transaction tx, Set newReadKeys, Set newWrittenKeys, long gasUsedByTx) { TransactionSublist sublistCandidate = getSublistCandidates(tx, newReadKeys, newWrittenKeys); - if (!sublistHasAvailableGas(tx, sublistCandidate)) { + if (sublistDoesNotHaveEnoughGas(tx, sublistCandidate)) { if (sublistCandidate.isSequential()) { return Optional.empty(); } sublistCandidate = getSequentialSublist(); - if (!sublistHasAvailableGas(tx, sublistCandidate)) { + if (sublistDoesNotHaveEnoughGas(tx, sublistCandidate)) { return Optional.empty(); } } @@ -61,8 +61,8 @@ public Optional addTransaction(Transaction tx, Set newRe return Optional.of(sublistCandidate.getGasUsed()); } - private boolean sublistHasAvailableGas(Transaction tx, TransactionSublist sublistCandidate) { - return sublistCandidate.hasGasAvailable(GasCost.toGas(tx.getGasLimit())); + private boolean sublistDoesNotHaveEnoughGas(Transaction tx, TransactionSublist sublistCandidate) { + return !sublistCandidate.hasGasAvailable(GasCost.toGas(tx.getGasLimit())); } public Optional addRemascTransaction(Transaction tx, long gasUsedByTx) { @@ -78,7 +78,7 @@ public Optional addRemascTransaction(Transaction tx, long gasUsedByTx) { public Optional addTxSentToPrecompiledContract(Transaction tx, long gasUsedByTx) { TransactionSublist sequentialSublist = getSequentialSublist(); - if (!sublistHasAvailableGas(tx, sequentialSublist)) { + if (sublistDoesNotHaveEnoughGas(tx, sequentialSublist)) { return Optional.empty(); } @@ -216,14 +216,14 @@ private TransactionSublist getSublistCandidates(Transaction tx, Set sublists = sublistsHavingReadFromKey.get(newWrittenKey); - if (sublists.size() > 1) { + Set setOfsublists = sublistsHavingReadFromKey.get(newWrittenKey); + if (setOfsublists.size() > 1) { // there is a write-read collision with multiple sublists return getSequentialSublist(); } // there is only one colluded sublist - TransactionSublist sublist = getNextSublist(sublists); + TransactionSublist sublist = getNextSublist(setOfsublists); if (!sublistCandidate.isPresent()) { // if there is no candidate, take the colluded sublist sublistCandidate = Optional.of(sublist); diff --git a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java index 941f4f55913..301b046e1c7 100644 --- a/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java +++ b/rskj-core/src/main/java/org/ethereum/config/blockchain/upgrades/ActivationConfig.java @@ -45,7 +45,10 @@ public ActivationConfig(Map activationHeights) { } public byte getHeaderVersion(long blockNumber) { - if (this.isActive(ConsensusRule.RSKIP351, blockNumber)) return 0x1; + if (this.isActive(ConsensusRule.RSKIP351, blockNumber)) { + return 0x1; + } + return 0x0; } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index f2b6ca90407..9505b05b304 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -48,10 +48,6 @@ public abstract class BlockHeader { /* RSKIP 351 */ public abstract byte getVersion(); public abstract BlockHeaderExtension getExtension(); - public abstract void setExtension(BlockHeaderExtension extension); - - // contains the logs bloom or the hash of the extension depending on version - public byte[] getExtensionData() { return this.extensionData; } // fields from block header extension public abstract byte[] getLogsBloom(); @@ -174,6 +170,11 @@ protected BlockHeader(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, this.ummRoot = ummRoot != null ? Arrays.copyOf(ummRoot, ummRoot.length) : null; } + public abstract void setExtension(BlockHeaderExtension extension); + + // contains the logs bloom or the hash of the extension depending on version + public byte[] getExtensionData() { return this.extensionData; } + @VisibleForTesting public boolean isSealed() { return this.sealed; diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java index 51496403adf..61aeb871d05 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderBuilder.java @@ -337,19 +337,21 @@ public BlockHeader build() { txExecutionSublistsEdges = new short[0]; } - if (activationConfig.getHeaderVersion(number) == 0x1) return new BlockHeaderV1( - parentHash, unclesHash, coinbase, - stateRoot, txTrieRoot, receiptTrieRoot, - logsBloom, difficulty, number, - gasLimit, gasUsed, timestamp, extraData, paidFees, - bitcoinMergedMiningHeader, - bitcoinMergedMiningMerkleProof, - bitcoinMergedMiningCoinbaseTransaction, - mergedMiningForkDetectionData, - minimumGasPrice, uncleCount, - false, useRskip92Encoding, - includeForkDetectionData, ummRoot, txExecutionSublistsEdges, false - ); + if (activationConfig.getHeaderVersion(number) == 0x1) { + return new BlockHeaderV1( + parentHash, unclesHash, coinbase, + stateRoot, txTrieRoot, receiptTrieRoot, + logsBloom, difficulty, number, + gasLimit, gasUsed, timestamp, extraData, paidFees, + bitcoinMergedMiningHeader, + bitcoinMergedMiningMerkleProof, + bitcoinMergedMiningCoinbaseTransaction, + mergedMiningForkDetectionData, + minimumGasPrice, uncleCount, + false, useRskip92Encoding, + includeForkDetectionData, ummRoot, txExecutionSublistsEdges, false + ); + } return new BlockHeaderV0( parentHash, unclesHash, coinbase, diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java index 3ef30bb9af9..44b39a49475 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java @@ -8,7 +8,9 @@ public interface BlockHeaderExtension { static BlockHeaderExtension fromEncoded(RLPList encoded) { byte version = encoded.get(0).getRLPData()[0]; - if (version == 0x1) return BlockHeaderExtensionV1.fromEncoded(encoded.getRLPData()); + if (version == 0x1) { + return BlockHeaderExtensionV1.fromEncoded(encoded.getRLPData()); + } return null; } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java index 8706156338a..05284518872 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java @@ -13,12 +13,6 @@ public class BlockHeaderExtensionV1 implements BlockHeaderExtension { private byte[] logsBloom; private short[] txExecutionSublistsEdges; - public byte[] getLogsBloom() { return this.logsBloom; } - public void setLogsBloom(byte[] logsBloom) { this.logsBloom = logsBloom; } - - public short[] getTxExecutionSublistsEdges() { return this.txExecutionSublistsEdges != null ? Arrays.copyOf(this.txExecutionSublistsEdges, this.txExecutionSublistsEdges.length) : null; } - public void setTxExecutionSublistsEdges(short[] edges) { this.txExecutionSublistsEdges = edges != null? Arrays.copyOf(edges, edges.length) : null; } - public BlockHeaderExtensionV1(byte[] logsBloom, short[] edges) { this.logsBloom = logsBloom; this.txExecutionSublistsEdges = edges != null ? Arrays.copyOf(edges, edges.length) : null; @@ -29,25 +23,17 @@ public byte[] getHash() { return HashUtil.keccak256(this.getEncodedForHash()); } - private void addEdgesEncoded(List fieldToEncodeList) { - short[] txExecutionSublistsEdges = this.getTxExecutionSublistsEdges(); - if (txExecutionSublistsEdges != null) { - fieldToEncodeList.add(ByteUtil.shortsToRLP(this.getTxExecutionSublistsEdges())); - } - } - - private byte[] getEncodedForHash() { - List fieldToEncodeList = Lists.newArrayList(RLP.encodeElement(HashUtil.keccak256(this.getLogsBloom()))); - this.addEdgesEncoded(fieldToEncodeList); - return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); - } - @Override public byte[] getEncoded() { List fieldToEncodeList = Lists.newArrayList(RLP.encodeElement(this.getLogsBloom())); this.addEdgesEncoded(fieldToEncodeList); return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); } + public byte[] getLogsBloom() { return this.logsBloom; } + public void setLogsBloom(byte[] logsBloom) { this.logsBloom = logsBloom; } + + public short[] getTxExecutionSublistsEdges() { return this.txExecutionSublistsEdges != null ? Arrays.copyOf(this.txExecutionSublistsEdges, this.txExecutionSublistsEdges.length) : null; } + public void setTxExecutionSublistsEdges(short[] edges) { this.txExecutionSublistsEdges = edges != null? Arrays.copyOf(edges, edges.length) : null; } public static BlockHeaderExtensionV1 fromEncoded(byte[] encoded) { RLPList rlpExtension = RLP.decodeList(encoded); @@ -56,4 +42,17 @@ public static BlockHeaderExtensionV1 fromEncoded(byte[] encoded) { rlpExtension.size() == 2 ? ByteUtil.rlpToShorts(rlpExtension.get(1).getRLPData()): null ); } + + private void addEdgesEncoded(List fieldToEncodeList) { + short[] internalExecutionSublistsEdges = this.getTxExecutionSublistsEdges(); + if (internalExecutionSublistsEdges != null) { + fieldToEncodeList.add(ByteUtil.shortsToRLP(internalExecutionSublistsEdges)); + } + } + + private byte[] getEncodedForHash() { + List fieldToEncodeList = Lists.newArrayList(RLP.encodeElement(HashUtil.keccak256(this.getLogsBloom()))); + this.addEdgesEncoded(fieldToEncodeList); + return RLP.encodeList(fieldToEncodeList.toArray(new byte[][]{})); + } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java index 2f395bfbc06..5ad22d5435d 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderV1.java @@ -10,14 +10,7 @@ import java.util.List; public class BlockHeaderV1 extends BlockHeader { - @Override - public byte getVersion() { return 0x1; } - private BlockHeaderExtensionV1 extension; - @Override - public BlockHeaderExtensionV1 getExtension() { return this.extension; } - @Override - public void setExtension(BlockHeaderExtension extension) { this.extension = (BlockHeaderExtensionV1) extension; } public BlockHeaderV1(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, byte[] stateRoot, byte[] txTrieRoot, byte[] receiptTrieRoot, byte[] extensionData, BlockDifficulty difficulty, @@ -41,6 +34,13 @@ public BlockHeaderV1(byte[] parentHash, byte[] unclesHash, RskAddress coinbase, } } + @Override + public byte getVersion() { return 0x1; } + + @Override + public BlockHeaderExtensionV1 getExtension() { return this.extension; } + @Override + public void setExtension(BlockHeaderExtension extension) { this.extension = (BlockHeaderExtensionV1) extension; } @VisibleForTesting public static byte[] createExtensionData(byte[] extensionHash) { diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index 54c6c05363c..77dcae4fb1f 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -77,7 +77,7 @@ public class TransactionExecutor { private final PrecompiledContracts precompiledContracts; private final boolean enableRemasc; private String executionError = ""; - private final long gasUsed; + private final long gasConsumed; private Coin paidFees; private final ProgramInvokeFactory programInvokeFactory; @@ -108,7 +108,7 @@ public class TransactionExecutor { public TransactionExecutor( Constants constants, ActivationConfig activationConfig, Transaction tx, int txindex, RskAddress coinbase, Repository track, BlockStore blockStore, ReceiptStore receiptStore, BlockFactory blockFactory, - ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasUsed, VmConfig vmConfig, + ProgramInvokeFactory programInvokeFactory, Block executionBlock, long gasConsumed, VmConfig vmConfig, boolean remascEnabled, PrecompiledContracts precompiledContracts, Set deletedAccounts, SignatureCache signatureCache, boolean postponeFeePayment, long sublistGasLimit) { this.constants = constants; @@ -124,7 +124,7 @@ public TransactionExecutor( this.blockFactory = blockFactory; this.programInvokeFactory = programInvokeFactory; this.executionBlock = executionBlock; - this.gasUsed = gasUsed; + this.gasConsumed = gasConsumed; this.vmConfig = vmConfig; this.precompiledContracts = precompiledContracts; this.enableRemasc = remascEnabled; @@ -253,7 +253,7 @@ private boolean gasIsValid(long txGasLimit, long curContainerGasLimit) { // which is used on some stress tests, but its far from being practical // as the current gas limit on blocks is 6.8M... several orders of magnitude // less than the theoretical max gas on blocks. - long cumulativeGas = GasCost.add(txGasLimit, gasUsed); + long cumulativeGas = GasCost.add(txGasLimit, gasConsumed); boolean cumulativeGasReached = cumulativeGas > curContainerGasLimit || cumulativeGas == GasCost.MAX_GAS; if (cumulativeGasReached) { @@ -494,11 +494,11 @@ private void createContract() { public TransactionReceipt getReceipt() { if (receipt == null) { receipt = new TransactionReceipt(); - long totalGasUsed = GasCost.add(gasUsed, getGasUsed()); + long totalGasUsed = GasCost.add(gasConsumed, getGasConsumed()); receipt.setCumulativeGas(totalGasUsed); receipt.setTransaction(tx); receipt.setLogInfoList(getVMLogs()); - receipt.setGasUsed(getGasUsed()); + receipt.setGasUsed(getGasConsumed()); receipt.setStatus(executionError.isEmpty() ? TransactionReceipt.SUCCESS_STATUS : TransactionReceipt.FAILED_STATUS); } return receipt; @@ -587,7 +587,7 @@ private void localCallFinalization() { long gasRefund = refundGas(); - result.setGasUsed(getGasUsed()); + result.setGasUsed(getGasConsumed()); TransactionExecutionSummary summary = buildTransactionExecutionSummary(summaryBuilder, gasRefund); @@ -679,7 +679,7 @@ public ProgramResult getResult() { return result; } - public long getGasUsed() { + public long getGasConsumed() { if (activations.isActive(ConsensusRule.RSKIP136)) { return GasCost.subtract(GasCost.toGas(tx.getGasLimit()), gasLeftover); } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java index a2bb623b188..269654725f1 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class BlockHeaderExtensionTest { +class BlockHeaderExtensionTest { @Test public void decodeV1() { byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; @@ -31,7 +31,7 @@ public void decodeV1() { } @Test - public void invalidDecode() { + void invalidDecode() { byte version = 0; byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; short[] edges = { 1, 2, 3, 4 }; diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java index c4d3b8c7055..cc240739fed 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java @@ -7,11 +7,11 @@ import java.util.Arrays; -public class BlockHeaderExtensionV1Test { +class BlockHeaderExtensionV1Test { private static short[] EDGES = new short[] { 1, 2, 3, 4 }; @Test - public void createWithLogsBloomAndEdges() { + void createWithLogsBloomAndEdges() { byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; logsBloom[0] = 0x01; logsBloom[1] = 0x02; @@ -25,7 +25,7 @@ public void createWithLogsBloomAndEdges() { } @Test - public void setLogsBloom() { + void setLogsBloom() { BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(new byte[32], EDGES); byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; @@ -41,7 +41,7 @@ public void setLogsBloom() { @Test - public void setEdges() { + void setEdges() { BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(new byte[32], EDGES); short[] edges = new short[] { 5, 6, 7, 8}; @@ -52,7 +52,7 @@ public void setEdges() { } @Test - public void hashIncludesLogsBloom() { + void hashIncludesLogsBloom() { byte[] logsBloom1 = new byte[Bloom.BLOOM_BYTES]; logsBloom1[0] = 0x01; logsBloom1[1] = 0x02; @@ -71,7 +71,7 @@ public void hashIncludesLogsBloom() { } @Test - public void hashIncludesEdges() { + void hashIncludesEdges() { byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; logsBloom[0] = 0x01; logsBloom[1] = 0x02; @@ -86,7 +86,7 @@ public void hashIncludesEdges() { } @Test - public void encodeDecode() { + void encodeDecode() { byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; logsBloom[0] = 0x01; logsBloom[1] = 0x02; diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java index bbca89dc786..03a82c0f01f 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV0Test.java @@ -12,7 +12,7 @@ import java.math.BigInteger; import java.util.Arrays; -public class BlockHeaderV0Test { +class BlockHeaderV0Test { private BlockHeaderV0 createBlockHeader(byte[] logsBloom, short[] edges) { return new BlockHeaderV0( PegTestUtils.createHash3().getBytes(), diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java index fd503921940..8a66c0e81ed 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderV1Test.java @@ -14,7 +14,7 @@ import java.math.BigInteger; import java.util.Arrays; -public class BlockHeaderV1Test { +class BlockHeaderV1Test { private BlockHeaderV1 createBlockHeader(byte[] logsBloom) { return new BlockHeaderV1( PegTestUtils.createHash3().getBytes(), diff --git a/rskj-core/src/test/java/co/rsk/core/TransactionIsRemascTest.java b/rskj-core/src/test/java/co/rsk/core/TransactionIsRemascTest.java index 8a6c727f023..ef1da7d733d 100644 --- a/rskj-core/src/test/java/co/rsk/core/TransactionIsRemascTest.java +++ b/rskj-core/src/test/java/co/rsk/core/TransactionIsRemascTest.java @@ -28,7 +28,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -public class TransactionIsRemascTest { +class TransactionIsRemascTest { int txPosition = 6; int txsSize = 7; RskAddress destination = PrecompiledContracts.REMASC_ADDR; diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index 827af546d80..30001bbfcce 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -32,7 +32,7 @@ import java.util.*; -public class ParallelizeTransactionHandlerTest { +class ParallelizeTransactionHandlerTest { private short sublists; private ParallelizeTransactionHandler handler; diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java index 297c648f669..ce51d002d87 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ReadWrittenKeysTrackerTest.java @@ -28,7 +28,7 @@ import java.util.*; import java.util.concurrent.*; -public class ReadWrittenKeysTrackerTest { +class ReadWrittenKeysTrackerTest { private IReadWrittenKeysTracker tracker; private IReadWrittenKeysTracker dummyTracker; diff --git a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java index 7aadb289319..39e88bf811a 100644 --- a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java +++ b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java @@ -311,8 +311,8 @@ private World processCallWithContract(String firstCall, String secondCall, int r Assertions.assertNotNull(world.getRepository().getCode(proxyAddress)); List txList = world.getBlockChain().getBestBlock().getTransactionsList(); - Assertions.assertTrue(proxyAddress.equals(txList.get(0).getReceiveAddress())); - Assertions.assertTrue(proxyAddress.equals(txList.get(0).getReceiveAddress())); + Assertions.assertEquals(proxyAddress, txList.get(0).getReceiveAddress()); + Assertions.assertEquals(proxyAddress, txList.get(0).getReceiveAddress()); return world; } diff --git a/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java index 5e434713064..e7d86d083bc 100644 --- a/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java +++ b/rskj-core/src/test/java/co/rsk/db/RepositoryTrackingTest.java @@ -148,21 +148,21 @@ void tracksWriteOnAddStorageSameBytes() { } @Test - public void tracksReadAndWriteOnAddBalanceOfNonExistent () { + void tracksReadAndWriteOnAddBalanceOfNonExistent () { repository.addBalance(COW, Coin.valueOf(1)); assertRepositoryHasSize(1, 1); } @Test - public void tracksReadAndWriteOnAddBalanceZeroOfNonExistent () { + void tracksReadAndWriteOnAddBalanceZeroOfNonExistent () { repository.addBalance(COW, Coin.valueOf(0)); assertRepositoryHasSize(1, 1); } @Test - public void tracksReadAndWriteOnAddBalanceOfExistent () { + void tracksReadAndWriteOnAddBalanceOfExistent () { repository.addBalance(COW, Coin.valueOf(1)); tracker.clear(); @@ -173,7 +173,7 @@ public void tracksReadAndWriteOnAddBalanceOfExistent () { } @Test - public void doesntTrackWriteOnAddBalanceZeroOfExistent () { + void doesntTrackWriteOnAddBalanceZeroOfExistent () { repository.addBalance(COW, Coin.valueOf(1)); tracker.clear(); diff --git a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java index fe500ce77c7..35acdd0a635 100644 --- a/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/BlockToMineBuilderTest.java @@ -161,7 +161,7 @@ private Block prepareForActivationTest() { } @Test - public void buildBlockBeforeUMMActivation() { + void buildBlockBeforeUMMActivation() { when(activationConfig.isActive(ConsensusRule.RSKIPUMM, 501L)).thenReturn(false); when(activationConfig.isActive(ConsensusRule.RSKIP252, 501L)).thenReturn(false); Block actualBlock = this.prepareForActivationTest(); @@ -169,7 +169,7 @@ public void buildBlockBeforeUMMActivation() { } @Test - public void buildBlockAfterUMMActivation() { + void buildBlockAfterUMMActivation() { when(activationConfig.isActive(ConsensusRule.RSKIPUMM, 501L)).thenReturn(true); when(activationConfig.isActive(ConsensusRule.RSKIP252, 501L)).thenReturn(false); Block actualBlock = this.prepareForActivationTest(); @@ -177,14 +177,14 @@ public void buildBlockAfterUMMActivation() { } @Test - public void buildBlockBeforeRskip351() { + void buildBlockBeforeRskip351() { when(activationConfig.getHeaderVersion(501L)).thenReturn((byte) 0x0); Block actualBlock = this.prepareForActivationTest(); assertEquals(0, actualBlock.getHeader().getVersion()); } @Test - public void buildBlockAfterRskip351() { + void buildBlockAfterRskip351() { when(activationConfig.getHeaderVersion(501L)).thenReturn((byte) 0x1); Block actualBlock = this.prepareForActivationTest(); assertEquals(1, actualBlock.getHeader().getVersion()); diff --git a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java index 1f63abc4bb1..6796ac836d3 100644 --- a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java +++ b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java @@ -15,7 +15,7 @@ import static org.mockito.AdditionalMatchers.geq; import static org.mockito.ArgumentMatchers.any; -public class ValidTxExecutionSublistsEdgesTest { +class ValidTxExecutionSublistsEdgesTest { private BlockHeader blockHeader; private Block block; diff --git a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java index b8d64fb4483..ada53ff10c4 100644 --- a/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java +++ b/rskj-core/src/test/java/co/rsk/vm/MinerHelper.java @@ -38,7 +38,6 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.LongAccumulator; /** * Created by Sergio on 18/07/2016. @@ -123,7 +122,7 @@ public void processBlock( Block block, Block parent) { executor.executeTransaction(); - long gasUsed = executor.getGasUsed(); + long gasUsed = executor.getGasConsumed(); Coin paidFees = executor.getPaidFees(); totalGasUsed += gasUsed; totalPaidFees = totalPaidFees.add(paidFees); diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index 627d0fdd66c..fb30aff5410 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -175,13 +175,13 @@ void failsReadingWithUnknownUpgradeConfiguration() { } @Test - public void headerVersion0() { + void headerVersion0() { ActivationConfig config = ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351); Assertions.assertEquals((byte) 0x0, config.getHeaderVersion(10)); } @Test - public void headerVersion1() { + void headerVersion1() { ActivationConfig config = ActivationConfigsForTest.all(); Assertions.assertEquals((byte) 0x1, config.getHeaderVersion(10)); } diff --git a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java index 81418b68209..b98057eb7fd 100644 --- a/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/BlockHeaderBuilderTest.java @@ -516,14 +516,14 @@ public Stream provideArguments(ExtensionContext context) { } @Test - public void createsHeaderWithVersion0WithNoRskip351() { + void createsHeaderWithVersion0WithNoRskip351() { // RSKIP351 = -1 BlockHeader header = new BlockHeaderBuilder(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)).build(); assertEquals((byte) 0x0, header.getVersion()); } @Test - public void createsHeaderWithVersion0BeforeRskip351() { + void createsHeaderWithVersion0BeforeRskip351() { // RSKIP351 > header number ActivationConfig activationConfig = Mockito.mock(ActivationConfig.class); when(activationConfig.getHeaderVersion(geq(2))).thenReturn((byte) 0x0); @@ -532,7 +532,7 @@ public void createsHeaderWithVersion0BeforeRskip351() { } @Test - public void createHeaderWithVersion1AfterRskip351() { + void createHeaderWithVersion1AfterRskip351() { // RSKIP351 = 0 BlockHeader header = new BlockHeaderBuilder(ActivationConfigsForTest.all()).build(); assertEquals((byte) 0x1, header.getVersion()); From bda76ae7cfd185ec000ad8f8fe5364b9e9862e8b Mon Sep 17 00:00:00 2001 From: Julian Len Date: Tue, 23 May 2023 13:53:06 -0300 Subject: [PATCH 38/89] Change in the gas limit in the addBlockToBlockchain method for the LogFilter test so it passes --- rskj-core/src/test/java/org/ethereum/rpc/LogFilterTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/test/java/org/ethereum/rpc/LogFilterTest.java b/rskj-core/src/test/java/org/ethereum/rpc/LogFilterTest.java index def75ae219b..a3e1f974a18 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/LogFilterTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/LogFilterTest.java @@ -191,7 +191,7 @@ public static Block addBlockToBlockchain(Block parent, Account account, BlockBui Transaction tx = new TransactionBuilder() .nonce(parent.getNumber()) // just to increase nonce .sender(account) - .gasLimit(BigInteger.valueOf(1000000)) + .gasLimit(BigInteger.valueOf(500000)) .gasPrice(BigInteger.ONE) .data(compiled_0_4_11) .build(); From c5cece840f066a9f0e8c9b975556e7de30055061 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 7 Jul 2023 15:06:07 +0300 Subject: [PATCH 39/89] Updated activation hf for rskips; Fixed unit tests --- rskj-core/src/main/resources/reference.conf | 4 ++-- .../src/test/java/co/rsk/net/NodeBlockProcessorTest.java | 2 +- .../config/blockchain/upgrades/ActivationConfigTest.java | 4 ++-- .../estimateGas/firstCallMoveAllRemainingSecondNot.txt | 3 +++ 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/rskj-core/src/main/resources/reference.conf b/rskj-core/src/main/resources/reference.conf index a146244fe35..115b2cdaec5 100644 --- a/rskj-core/src/main/resources/reference.conf +++ b/rskj-core/src/main/resources/reference.conf @@ -63,8 +63,8 @@ blockchain = { rskip293 = hop400 rskip294 = hop400 rskip297 = hop400 - rskip351 = fingerroot500 - rskip144 = fingerroot500 + rskip351 = tbd600 + rskip144 = tbd600 rskip326 = fingerroot500 rskip353 = hop401 rskip357 = hop401 diff --git a/rskj-core/src/test/java/co/rsk/net/NodeBlockProcessorTest.java b/rskj-core/src/test/java/co/rsk/net/NodeBlockProcessorTest.java index 72c9154fd0f..94f8f30ad58 100644 --- a/rskj-core/src/test/java/co/rsk/net/NodeBlockProcessorTest.java +++ b/rskj-core/src/test/java/co/rsk/net/NodeBlockProcessorTest.java @@ -794,7 +794,7 @@ void processBlockRequestMessageUsingBlockInStore() throws UnknownHostException { @Test void processBodyRequestMessageUsingBlockInBlockchainWithoutRskip351() throws UnknownHostException { TestSystemProperties config = new TestSystemProperties(rawConfig -> - rawConfig.withValue("blockchain.config.hardforkActivationHeights.fingerroot500", ConfigValueFactory.fromAnyRef(-1)) + rawConfig.withValue("blockchain.config.consensusRules.rskip351", ConfigValueFactory.fromAnyRef(-1)) ); final Blockchain blockchain = new BlockChainBuilder() .setConfig(config) diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java index 1c6fd435fc6..6b552e55281 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigTest.java @@ -102,9 +102,9 @@ class ActivationConfigTest { " rskip290: hop400", " rskip293: hop400", " rskip294: hop400", - " rskip144: fingerroot500", + " rskip144: tbd600", " rskip297: hop400", - " rskip351: fingerroot500", + " rskip351: tbd600", " rskip326: fingerroot500", " rskip353: hop401", " rskip357: hop401", diff --git a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/firstCallMoveAllRemainingSecondNot.txt b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/firstCallMoveAllRemainingSecondNot.txt index 7c82fe387d3..3c73e309eaa 100644 --- a/rskj-core/src/test/resources/dsl/eth_module/estimateGas/firstCallMoveAllRemainingSecondNot.txt +++ b/rskj-core/src/test/resources/dsl/eth_module/estimateGas/firstCallMoveAllRemainingSecondNot.txt @@ -29,6 +29,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 10000000 transactions tx01 build @@ -46,6 +47,7 @@ transaction_build tx02 block_build b02 parent b01 + gasLimit 10000000 #transactions tx02 build @@ -54,6 +56,7 @@ assert_best b02 block_build b03 parent b02 + gasLimit 10000000 transactions tx02 build From f70e601c841fd8d1a61ba909890883ae8dd50f73 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Mon, 10 Jul 2023 11:34:06 +0300 Subject: [PATCH 40/89] Fixed integration tests --- rskj-core/src/main/resources/config/regtest.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rskj-core/src/main/resources/config/regtest.conf b/rskj-core/src/main/resources/config/regtest.conf index 6becc0c884b..a11e6b116f5 100644 --- a/rskj-core/src/main/resources/config/regtest.conf +++ b/rskj-core/src/main/resources/config/regtest.conf @@ -17,6 +17,8 @@ blockchain.config { consensusRules = { rskip97 = -1 # disable orchid difficulty drop rskipUMM = 1 + rskip144 = 1 + rskip351 = 1 } } From 4592e1a0575558e13e6da9553e028b4efda64bca Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Wed, 12 Jul 2023 11:23:57 +0300 Subject: [PATCH 41/89] Added missing getEncoded(bool,bool) to BlockHeader --- .../src/main/java/org/ethereum/core/BlockHeader.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index 9505b05b304..57691c89082 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -321,9 +321,9 @@ public Keccak256 getHash() { // the encoded block header must include all fields, even the bitcoin PMT and coinbase which are not used for // calculating RSKIP92 block hashes - public byte[] getFullEncoded() { return this.getEncoded(true, true, false); } + public byte[] getFullEncoded() { return this.getEncoded(true, true); } // the encoded block header used for calculating block hashes including RSKIP92 - public byte[] getEncoded() { return this.getEncoded(true, !useRskip92Encoding, false); } + public byte[] getEncoded() { return this.getEncoded(true, !useRskip92Encoding); } public byte[] getEncodedCompressed() { return this.getEncoded(true, true, true); } public byte[] getEncodedForHash() { return this.getEncoded(true, !useRskip92Encoding, true); } @@ -332,6 +332,13 @@ public Coin getMinimumGasPrice() { return this.minimumGasPrice; } + /** + * note: being also used by powpeg + */ + public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProofAndCoinbase) { + return this.getEncoded(withMergedMiningFields, withMerkleProofAndCoinbase, false); + } + public byte[] getEncoded(boolean withMergedMiningFields, boolean withMerkleProofAndCoinbase, boolean compressed) { byte[] parentHash = RLP.encodeElement(this.parentHash); From 449d28f1c8c059263c5b81c639691da245e643b9 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Wed, 12 Jul 2023 16:26:40 +0300 Subject: [PATCH 42/89] Removed unused 144and351.conf file --- 144and351.conf | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 144and351.conf diff --git a/144and351.conf b/144and351.conf deleted file mode 100644 index 55cf8a636d9..00000000000 --- a/144and351.conf +++ /dev/null @@ -1,11 +0,0 @@ -miner { - server.enabled = true - - client { - enabled = true - delayBetweenBlocks = 3 second - } -} - -transaction.accountTxRateLimit.enabled = false -transaction.accountSlots = 10000 From 693bb5230863978a7b9ec643eabe64dca71b909a Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 21 Jul 2023 13:09:43 +0300 Subject: [PATCH 43/89] Fixed long sync --- .../messages/BlockHeadersResponseMessage.java | 2 +- .../rsk/net/messages/BodyResponseMessage.java | 10 +-- .../java/co/rsk/net/messages/MessageType.java | 4 +- .../net/sync/DownloadingBodiesSyncState.java | 3 +- .../ethereum/core/BlockHeaderExtension.java | 23 +++++-- .../ethereum/core/BlockHeaderExtensionV1.java | 2 +- .../co/rsk/core/BlockHeaderExtensionTest.java | 22 +++---- .../rsk/core/BlockHeaderExtensionV1Test.java | 9 ++- .../java/co/rsk/mine/NetworkUpgrades.java | 17 +++++ .../net/messages/BodyResponseMessageTest.java | 16 ++++- .../java/co/rsk/net/messages/MessageTest.java | 64 +++++++++++-------- .../upgrades/ActivationConfigsForTest.java | 4 +- .../validator/ProofOfWorkRuleTbd600Test.java | 31 +++++++++ .../validator/ProofOfWorkRuleTest.java | 36 +++++++++-- 14 files changed, 180 insertions(+), 63 deletions(-) create mode 100644 rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleTbd600Test.java diff --git a/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java b/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java index d38324cb303..ada7749f442 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java @@ -32,7 +32,7 @@ public BlockHeadersResponseMessage(long id, List headers) { @Override protected byte[] getEncodedMessageWithoutId() { byte[][] rlpHeaders = this.blockHeaders.stream() - .map(BlockHeader::getEncodedCompressed) + .map(BlockHeader::getFullEncoded) .toArray(byte[][]::new); return RLP.encodeList(RLP.encodeList(rlpHeaders)); diff --git a/rskj-core/src/main/java/co/rsk/net/messages/BodyResponseMessage.java b/rskj-core/src/main/java/co/rsk/net/messages/BodyResponseMessage.java index 610aff31b78..b857f262fc9 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/BodyResponseMessage.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/BodyResponseMessage.java @@ -12,10 +12,10 @@ * Created by ajlopez on 25/08/2017. */ public class BodyResponseMessage extends MessageWithId { - private long id; - private List transactions; - private List uncles; - private BlockHeaderExtension blockHeaderExtension; + private final long id; + private final List transactions; + private final List uncles; + private final BlockHeaderExtension blockHeaderExtension; public BodyResponseMessage(long id, List transactions, List uncles, BlockHeaderExtension blockHeaderExtension) { this.id = id; @@ -48,7 +48,7 @@ protected byte[] getEncodedMessageWithoutId() { List elements = Lists.newArrayList(RLP.encodeList(rlpTransactions), RLP.encodeList(rlpUncles)); if (this.blockHeaderExtension != null) { - elements.add(this.blockHeaderExtension.getEncoded()); + elements.add(BlockHeaderExtension.toEncoded(blockHeaderExtension)); } return RLP.encodeList(elements.toArray(new byte[][]{})); diff --git a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java index 17d81035c4a..08d4c972d60 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java @@ -143,7 +143,7 @@ public Message createMessage(BlockFactory blockFactory, RLPList list) { for (int k = 0; k < rlpHeaders.size(); k++) { RLPElement element = rlpHeaders.get(k); - BlockHeader header = blockFactory.decodeHeader(element.getRLPData(), true); + BlockHeader header = blockFactory.decodeHeader(element.getRLPData(), false); headers.add(header); } @@ -233,7 +233,7 @@ public Message createMessage(BlockFactory blockFactory, RLPList list) { } BlockHeaderExtension blockHeaderExtension = message.size() == 3 - ? BlockHeaderExtension.fromEncoded((RLPList) RLP.decode2(message.get(2).getRLPData()).get(0)) + ? BlockHeaderExtension.fromEncoded(message.get(2).getRLPData()) : null; return new BodyResponseMessage(id, transactions, uncles, blockHeaderExtension); diff --git a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java index 1eb1d3e1255..6fd4263eb80 100644 --- a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java +++ b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java @@ -116,7 +116,8 @@ public void newBody(BodyResponseMessage message, Peer peer) { // we already checked that this message was expected BlockHeader header = pendingBodyResponses.remove(requestId).header; - header.setExtension(message.getBlockHeaderExtension()); + // TODO: as returned headers are "fully" encoded with extension data included, there's no need to set an extension here +// header.setExtension(message.getBlockHeaderExtension()); Block block; try { block = blockFactory.newBlock(header, message.getTransactions(), message.getUncles()); diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java index 44b39a49475..b087e4f4afb 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtension.java @@ -1,16 +1,31 @@ package org.ethereum.core; +import org.ethereum.util.RLP; import org.ethereum.util.RLPList; +import java.util.Objects; + public interface BlockHeaderExtension { byte[] getEncoded(); byte[] getHash(); - static BlockHeaderExtension fromEncoded(RLPList encoded) { - byte version = encoded.get(0).getRLPData()[0]; + static byte[] toEncoded(BlockHeaderExtension extension) { + if (!(Objects.requireNonNull(extension) instanceof BlockHeaderExtensionV1)) { + throw new IllegalArgumentException("Unknown extension"); + } + return RLP.encodeList( + RLP.encodeByte((byte) 0x1), + RLP.encodeElement(extension.getEncoded()) + ); + } + + static BlockHeaderExtension fromEncoded(byte[] encoded) { + RLPList rlpList = RLP.decodeList(encoded); + byte[] versionData = rlpList.get(0).getRLPData(); + byte version = versionData == null || versionData.length == 0 ? 0 : versionData[0]; if (version == 0x1) { - return BlockHeaderExtensionV1.fromEncoded(encoded.getRLPData()); + return BlockHeaderExtensionV1.fromEncoded(rlpList.get(1).getRLPData()); } - return null; + throw new IllegalArgumentException("Unknown extension with version: " + version); } } diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java index 05284518872..6f0a0ce2ad2 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeaderExtensionV1.java @@ -39,7 +39,7 @@ public static BlockHeaderExtensionV1 fromEncoded(byte[] encoded) { RLPList rlpExtension = RLP.decodeList(encoded); return new BlockHeaderExtensionV1( rlpExtension.get(0).getRLPData(), - rlpExtension.size() == 2 ? ByteUtil.rlpToShorts(rlpExtension.get(1).getRLPData()): null + rlpExtension.size() == 2 ? ByteUtil.rlpToShorts(rlpExtension.get(1).getRLPRawData()): null ); } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java index 269654725f1..be0d6f5158e 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionTest.java @@ -1,12 +1,10 @@ package co.rsk.core; -import com.google.common.collect.Lists; import org.ethereum.core.BlockHeaderExtension; import org.ethereum.core.BlockHeaderExtensionV1; import org.ethereum.core.Bloom; import org.ethereum.util.ByteUtil; import org.ethereum.util.RLP; -import org.ethereum.util.RLPList; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -14,7 +12,7 @@ class BlockHeaderExtensionTest { @Test public void decodeV1() { byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; - logsBloom[0] = 0x01; + logsBloom[0] = 0x00; logsBloom[1] = 0x02; logsBloom[2] = 0x03; logsBloom[3] = 0x04; @@ -24,7 +22,7 @@ public void decodeV1() { BlockHeaderExtensionV1 extension = new BlockHeaderExtensionV1(logsBloom, edges); BlockHeaderExtension decoded = BlockHeaderExtension.fromEncoded( - RLP.decodeList(extension.getEncoded()) + BlockHeaderExtension.toEncoded(extension) ); Assertions.assertArrayEquals(extension.getHash(), decoded.getHash()); @@ -36,14 +34,14 @@ void invalidDecode() { byte[] logsBloom = new byte[Bloom.BLOOM_BYTES]; short[] edges = { 1, 2, 3, 4 }; - BlockHeaderExtension decoded = BlockHeaderExtension.fromEncoded( - new RLPList(RLP.encodeList( + Assertions.assertThrows(IllegalArgumentException.class, () -> BlockHeaderExtension.fromEncoded( + RLP.encodeList( RLP.encodeByte(version), - RLP.encodeElement(logsBloom), - ByteUtil.shortsToRLP(edges) - ), 0) - ); - - Assertions.assertNull(decoded); + RLP.encodeList( + RLP.encodeElement(logsBloom), + ByteUtil.shortsToRLP(edges) + ) + ) + ), "Unknown extension with version: " + version); } } diff --git a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java index cc240739fed..541de169b31 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockHeaderExtensionV1Test.java @@ -8,7 +8,8 @@ import java.util.Arrays; class BlockHeaderExtensionV1Test { - private static short[] EDGES = new short[] { 1, 2, 3, 4 }; + private static final short[] EDGES = new short[] { 1, 2, 3, 4 }; + private static final short[] NO_EDGES = new short[0]; @Test void createWithLogsBloomAndEdges() { @@ -99,5 +100,11 @@ void encodeDecode() { Assertions.assertArrayEquals(logsBloom, extension.getLogsBloom()); Assertions.assertArrayEquals(EDGES, extension.getTxExecutionSublistsEdges()); + + extension = BlockHeaderExtensionV1.fromEncoded( + new BlockHeaderExtensionV1(logsBloom, NO_EDGES).getEncoded() + ); + + Assertions.assertArrayEquals(NO_EDGES, extension.getTxExecutionSublistsEdges()); } } diff --git a/rskj-core/src/test/java/co/rsk/mine/NetworkUpgrades.java b/rskj-core/src/test/java/co/rsk/mine/NetworkUpgrades.java index 3bded16ed45..f8fd38b36f0 100644 --- a/rskj-core/src/test/java/co/rsk/mine/NetworkUpgrades.java +++ b/rskj-core/src/test/java/co/rsk/mine/NetworkUpgrades.java @@ -65,6 +65,23 @@ public String toString() { public String projectVersionModifier() { return "Orchid"; } + }), + + TBD600_PROPERTIES(new TestSystemProperties() { + @Override + public ActivationConfig getActivationConfig() { + return ActivationConfigsForTest.tbd600(); + } + + @Override + public String toString() { + return "Tbd600"; + } + + @Override + public String projectVersionModifier() { + return "TBD"; + } }); private final TestSystemProperties properties; diff --git a/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java b/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java index 52254ee9e4d..c148c6460f6 100644 --- a/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java +++ b/rskj-core/src/test/java/co/rsk/net/messages/BodyResponseMessageTest.java @@ -1,6 +1,7 @@ package co.rsk.net.messages; import co.rsk.blockchain.utils.BlockGenerator; +import co.rsk.config.TestSystemProperties; import co.rsk.test.builders.AccountBuilder; import co.rsk.test.builders.TransactionBuilder; import org.ethereum.core.*; @@ -15,7 +16,8 @@ import static org.mockito.Mockito.*; class BodyResponseMessageTest { - BodyResponseMessage testCreateMessage(BlockHeaderExtension extension) { + + private BodyResponseMessage testCreateMessage(BlockHeaderExtension extension) { List transactions = new ArrayList<>(); for (int k = 1; k <= 10; k++) @@ -46,15 +48,23 @@ BodyResponseMessage testCreateMessage(BlockHeaderExtension extension) { Assertions.assertNotNull(message.getUncles()); Assertions.assertEquals(uncles.size(), message.getUncles().size()); - for (int k = 0; k < uncles.size(); k++) + for (int k = 0; k < uncles.size(); k++) { Assertions.assertArrayEquals(uncles.get(k).getFullEncoded(), message.getUncles().get(k).getFullEncoded()); + } return message; } + + private BodyResponseMessage encodeAndDecodeMessage(BodyResponseMessage message) { + return (BodyResponseMessage) Message.create(new BlockFactory(new TestSystemProperties().getActivationConfig()), message.getEncoded()); + } + @Test void createMessage() { BodyResponseMessage message = testCreateMessage(null); Assertions.assertNull(message.getBlockHeaderExtension()); + message = encodeAndDecodeMessage(message); + Assertions.assertNull(message.getBlockHeaderExtension()); } @Test @@ -64,6 +74,8 @@ void createMessageWithExtension() { BlockHeaderExtension extension = new BlockHeaderExtensionV1(bloom.getData(), edges); BodyResponseMessage message = testCreateMessage(extension); Assertions.assertArrayEquals(extension.getEncoded(), message.getBlockHeaderExtension().getEncoded()); + message = encodeAndDecodeMessage(message); + Assertions.assertArrayEquals(extension.getEncoded(), message.getBlockHeaderExtension().getEncoded()); } private static Transaction createTransaction(int number) { diff --git a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java index 358f481bb61..8854dc058ab 100644 --- a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java +++ b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java @@ -19,6 +19,8 @@ package co.rsk.net.messages; import co.rsk.blockchain.utils.BlockGenerator; +import co.rsk.blockchain.utils.BlockMiner; +import co.rsk.config.MiningConfig; import co.rsk.core.BlockDifficulty; import co.rsk.net.Status; import co.rsk.net.utils.TransactionUtils; @@ -26,22 +28,17 @@ import co.rsk.test.builders.TransactionBuilder; import org.ethereum.TestUtils; import org.ethereum.config.Constants; +import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; -import org.ethereum.crypto.HashUtil; -import org.ethereum.core.Account; -import org.ethereum.core.Block; -import org.ethereum.core.BlockFactory; -import org.ethereum.core.BlockHeader; -import org.ethereum.core.BlockIdentifier; -import org.ethereum.core.Transaction; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -205,10 +202,6 @@ private BlockHeadersResponseMessage testBlockHeadersResponseMessage(BlockFactory BlockHeadersResponseMessage result = (BlockHeadersResponseMessage) Message.create(blockFactory, encoded); - for (int k = 0; k < headers.size(); k++) { - result.getBlockHeaders().get(k).setExtension(headers.get(k).getExtension()); // identity applying on block header v0 - } - Assertions.assertNotNull(encoded); Assertions.assertNotNull(result); Assertions.assertArrayEquals(encoded, result.getEncoded()); @@ -222,35 +215,54 @@ private BlockHeadersResponseMessage testBlockHeadersResponseMessage(BlockFactory @Test void encodeDecodeBlockHeadersResponseMessageWithoutRSKIP351() { - BlockGenerator blockGenerator = new BlockGenerator(Constants.regtest(), ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)); - BlockFactory blockFactory = new BlockFactory(ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351)); + ActivationConfig activationConfig = ActivationConfigsForTest.allBut(ConsensusRule.RSKIP351); + BlockFactory blockFactory = new BlockFactory(activationConfig); + BlockMiner blockMiner = new BlockMiner(activationConfig); List headers = new ArrayList<>(); - for (int k = 1; k <= 4; k++) - headers.add(blockGenerator.getBlock(k).getHeader()); + for (int k = 1; k <= 4; k++) { + BlockHeader header = blockFactory.getBlockHeaderBuilder() + .setNumber(MiningConfig.REQUIRED_NUMBER_OF_BLOCKS_FOR_FORK_DETECTION_CALCULATION + k) + .setIncludeForkDetectionData(true) + .build(); + Block block = blockFactory.newBlock(header, Collections.emptyList(), Collections.emptyList(), false); + Block minedBlock = blockMiner.mineBlock(block); + headers.add(minedBlock.getHeader()); + } - BlockHeadersResponseMessage newmessage = testBlockHeadersResponseMessage(blockFactory, headers); + BlockHeadersResponseMessage newMessage = testBlockHeadersResponseMessage(blockFactory, headers); for (int k = 0; k < headers.size(); k++) { - Assertions.assertEquals(headers.get(k).getNumber(), newmessage.getBlockHeaders().get(k).getNumber()); - Assertions.assertEquals(headers.get(k).getHash(), newmessage.getBlockHeaders().get(k).getHash()); - Assertions.assertArrayEquals(headers.get(k).getFullEncoded(), newmessage.getBlockHeaders().get(k).getFullEncoded()); + Assertions.assertEquals(headers.get(k).getNumber(), newMessage.getBlockHeaders().get(k).getNumber()); + Assertions.assertEquals(headers.get(k).getHash(), newMessage.getBlockHeaders().get(k).getHash()); + Assertions.assertArrayEquals(headers.get(k).getFullEncoded(), newMessage.getBlockHeaders().get(k).getFullEncoded()); + Assertions.assertArrayEquals(headers.get(k).getMiningForkDetectionData(), newMessage.getBlockHeaders().get(k).getMiningForkDetectionData()); } } @Test void encodeDecodeBlockHeadersResponseMessage() { List headers = new ArrayList<>(); + ActivationConfig activationConfig = ActivationConfigsForTest.all(); + BlockMiner blockMiner = new BlockMiner(activationConfig); + + for (int k = 1; k <= 4; k++) { + BlockHeader header = blockFactory.getBlockHeaderBuilder() + .setNumber(MiningConfig.REQUIRED_NUMBER_OF_BLOCKS_FOR_FORK_DETECTION_CALCULATION + k) + .setIncludeForkDetectionData(true) + .build(); + Block block = blockFactory.newBlock(header, Collections.emptyList(), Collections.emptyList(), false); + Block minedBlock = blockMiner.mineBlock(block); + headers.add(minedBlock.getHeader()); + } - for (int k = 1; k <= 4; k++) - headers.add(blockGenerator.getBlock(k).getHeader()); - - BlockHeadersResponseMessage newmessage = testBlockHeadersResponseMessage(blockFactory, headers); + BlockHeadersResponseMessage newMessage = testBlockHeadersResponseMessage(blockFactory, headers); for (int k = 0; k < headers.size(); k++) { - Assertions.assertEquals(headers.get(k).getNumber(), newmessage.getBlockHeaders().get(k).getNumber()); - Assertions.assertEquals(headers.get(k).getHash(), newmessage.getBlockHeaders().get(k).getHash()); - Assertions.assertArrayEquals(headers.get(k).getFullEncoded(), newmessage.getBlockHeaders().get(k).getFullEncoded()); + Assertions.assertEquals(headers.get(k).getNumber(), newMessage.getBlockHeaders().get(k).getNumber()); + Assertions.assertEquals(headers.get(k).getHash(), newMessage.getBlockHeaders().get(k).getHash()); + Assertions.assertArrayEquals(headers.get(k).getFullEncoded(), newMessage.getBlockHeaders().get(k).getFullEncoded()); + Assertions.assertArrayEquals(headers.get(k).getMiningForkDetectionData(), newMessage.getBlockHeaders().get(k).getMiningForkDetectionData()); } } diff --git a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigsForTest.java b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigsForTest.java index 700adcad4c0..933d8fb2329 100644 --- a/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigsForTest.java +++ b/rskj-core/src/test/java/org/ethereum/config/blockchain/upgrades/ActivationConfigsForTest.java @@ -178,7 +178,9 @@ private static List getFingerroot500Rskips() { private static List getTbd600Rskips() { List rskips = new ArrayList<>(); rskips.addAll(Arrays.asList( - ConsensusRule.RSKIP376 + ConsensusRule.RSKIP376, + ConsensusRule.RSKIP144, + ConsensusRule.RSKIP351 )); return rskips; diff --git a/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleTbd600Test.java b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleTbd600Test.java new file mode 100644 index 00000000000..6bfde0a8802 --- /dev/null +++ b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleTbd600Test.java @@ -0,0 +1,31 @@ +/* + * This file is part of RskJ + * Copyright (C) 2023 RSK Labs Ltd. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package org.ethereum.validator; + +import co.rsk.mine.NetworkUpgrades; +import org.junit.jupiter.api.BeforeEach; + +class ProofOfWorkRuleTbd600Test extends ProofOfWorkRuleTest { + + @BeforeEach + void beforeEach() { + setUp(NetworkUpgrades.TBD600_PROPERTIES.getProperties()); + } + +} diff --git a/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleTest.java b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleTest.java index bb9249b368f..80f0baeae55 100644 --- a/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleTest.java +++ b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleTest.java @@ -21,6 +21,7 @@ import co.rsk.blockchain.utils.BlockGenerator; import co.rsk.blockchain.utils.BlockMiner; +import co.rsk.config.MiningConfig; import co.rsk.config.RskMiningConstants; import co.rsk.config.RskSystemProperties; import co.rsk.config.TestSystemProperties; @@ -28,20 +29,25 @@ import co.rsk.mine.MinerUtils; import co.rsk.util.DifficultyUtils; import co.rsk.validators.ProofOfWorkRule; +import org.bouncycastle.util.encoders.Hex; import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; import org.ethereum.core.Block; import org.ethereum.core.BlockFactory; -import org.junit.jupiter.api.*; +import org.ethereum.core.BlockHeader; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; -import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Arrays; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertTrue; /** @@ -66,7 +72,7 @@ protected void setUp(TestSystemProperties config) { void test_1() { // mined block Block b = new BlockMiner(activationConfig).mineBlock(new BlockGenerator(networkConstants, activationConfig).getBlock(1)); - Assertions.assertTrue(rule.isValid(b)); + assertTrue(rule.isValid(b)); } @Disabled("TODO improve, the mutated block header could be still valid") @@ -94,8 +100,8 @@ void test_RLPEncoding() { byte[] lastField2 = b2.getBitcoinMergedMiningCoinbaseTransaction(); // last field b2.flushRLP();// force re-encode byte[] encoded2 = b2.getEncoded(); - Assertions.assertTrue(Arrays.equals(encoded,encoded2)); - Assertions.assertTrue(Arrays.equals(lastField,lastField2)); + assertTrue(Arrays.equals(encoded,encoded2)); + assertTrue(Arrays.equals(lastField,lastField2)); } @Disabled("stress test") @@ -113,7 +119,7 @@ void test_3() { long total = System.currentTimeMillis() - start; - Assertions.assertTrue(total > 0); + assertTrue(total > 0); System.out.printf("Time: total = %d ms, per block = %.2f ms%n", total, (double) total / iterCnt); } @@ -141,6 +147,22 @@ void test_RSKTagInCoinbaseTransactionTooFar() { Assertions.assertFalse(rule.isValid(b)); } + @Test + void test_validAfterEncodingAndDecoding() { + BlockHeader header = blockFactory.getBlockHeaderBuilder() + .setNumber(MiningConfig.REQUIRED_NUMBER_OF_BLOCKS_FOR_FORK_DETECTION_CALCULATION) + .setIncludeForkDetectionData(true) + .build(); + Block block = blockFactory.newBlock(header, Collections.emptyList(), Collections.emptyList(), false); + Block minedBlock = new BlockMiner(activationConfig).mineBlock(block); + BlockHeader minedBlockHeader = minedBlock.getHeader(); + + byte[] encodedCompressed = minedBlockHeader.getFullEncoded(); + BlockHeader decodedHeader = blockFactory.decodeHeader(encodedCompressed, false); + + assertTrue(rule.isValid(decodedHeader)); + } + @Test void bytesAfterMergedMiningHashAreLessThan128() { RskSystemProperties props = new TestSystemProperties() { @@ -169,7 +191,7 @@ public ActivationConfig getActivationConfig() { Block newBlock1 = new BlockMiner(config).mineBlock(newBlock, bitcoinMergedMiningCoinbaseTransaction); ProofOfWorkRule rule = new ProofOfWorkRule(props); - Assertions.assertTrue(rule.isValid(newBlock1)); + assertTrue(rule.isValid(newBlock1)); } @Test From 9321aa39a39dd6d249fa69c94e4f0806cdef7014 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Tue, 25 Jul 2023 12:47:18 +0300 Subject: [PATCH 44/89] Using compressed header encoding for calculating HashForMergedMining --- .../java/co/rsk/net/messages/BlockHeadersResponseMessage.java | 2 +- rskj-core/src/main/java/co/rsk/net/messages/MessageType.java | 2 +- .../main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java | 3 +-- rskj-core/src/main/java/org/ethereum/core/BlockHeader.java | 2 +- rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java | 4 ++-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java b/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java index ada7749f442..d38324cb303 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/BlockHeadersResponseMessage.java @@ -32,7 +32,7 @@ public BlockHeadersResponseMessage(long id, List headers) { @Override protected byte[] getEncodedMessageWithoutId() { byte[][] rlpHeaders = this.blockHeaders.stream() - .map(BlockHeader::getFullEncoded) + .map(BlockHeader::getEncodedCompressed) .toArray(byte[][]::new); return RLP.encodeList(RLP.encodeList(rlpHeaders)); diff --git a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java index 08d4c972d60..27db4c7e83e 100644 --- a/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java +++ b/rskj-core/src/main/java/co/rsk/net/messages/MessageType.java @@ -143,7 +143,7 @@ public Message createMessage(BlockFactory blockFactory, RLPList list) { for (int k = 0; k < rlpHeaders.size(); k++) { RLPElement element = rlpHeaders.get(k); - BlockHeader header = blockFactory.decodeHeader(element.getRLPData(), false); + BlockHeader header = blockFactory.decodeHeader(element.getRLPData(), true); headers.add(header); } diff --git a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java index 6fd4263eb80..1eb1d3e1255 100644 --- a/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java +++ b/rskj-core/src/main/java/co/rsk/net/sync/DownloadingBodiesSyncState.java @@ -116,8 +116,7 @@ public void newBody(BodyResponseMessage message, Peer peer) { // we already checked that this message was expected BlockHeader header = pendingBodyResponses.remove(requestId).header; - // TODO: as returned headers are "fully" encoded with extension data included, there's no need to set an extension here -// header.setExtension(message.getBlockHeaderExtension()); + header.setExtension(message.getBlockHeaderExtension()); Block block; try { block = blockFactory.newBlock(header, message.getTransactions(), message.getUncles()); diff --git a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java index 57691c89082..dd3b4c69361 100644 --- a/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java +++ b/rskj-core/src/main/java/org/ethereum/core/BlockHeader.java @@ -611,7 +611,7 @@ public byte[] getMiningForkDetectionData() { * @return The computed hash for merged mining */ private byte[] getBaseHashForMergedMining() { - byte[] encodedBlock = getEncoded(false, false, false); + byte[] encodedBlock = getEncoded(false, false, true); byte[] hashForMergedMining = HashUtil.keccak256(encodedBlock); if (isUMMBlock()) { diff --git a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java index 8854dc058ab..d35fb7102c9 100644 --- a/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java +++ b/rskj-core/src/test/java/co/rsk/net/messages/MessageTest.java @@ -235,7 +235,7 @@ void encodeDecodeBlockHeadersResponseMessageWithoutRSKIP351() { for (int k = 0; k < headers.size(); k++) { Assertions.assertEquals(headers.get(k).getNumber(), newMessage.getBlockHeaders().get(k).getNumber()); Assertions.assertEquals(headers.get(k).getHash(), newMessage.getBlockHeaders().get(k).getHash()); - Assertions.assertArrayEquals(headers.get(k).getFullEncoded(), newMessage.getBlockHeaders().get(k).getFullEncoded()); + Assertions.assertArrayEquals(headers.get(k).getEncodedCompressed(), newMessage.getBlockHeaders().get(k).getEncodedCompressed()); Assertions.assertArrayEquals(headers.get(k).getMiningForkDetectionData(), newMessage.getBlockHeaders().get(k).getMiningForkDetectionData()); } } @@ -261,7 +261,7 @@ void encodeDecodeBlockHeadersResponseMessage() { for (int k = 0; k < headers.size(); k++) { Assertions.assertEquals(headers.get(k).getNumber(), newMessage.getBlockHeaders().get(k).getNumber()); Assertions.assertEquals(headers.get(k).getHash(), newMessage.getBlockHeaders().get(k).getHash()); - Assertions.assertArrayEquals(headers.get(k).getFullEncoded(), newMessage.getBlockHeaders().get(k).getFullEncoded()); + Assertions.assertArrayEquals(headers.get(k).getEncodedCompressed(), newMessage.getBlockHeaders().get(k).getEncodedCompressed()); Assertions.assertArrayEquals(headers.get(k).getMiningForkDetectionData(), newMessage.getBlockHeaders().get(k).getMiningForkDetectionData()); } } From c18823887757f2953eec960d91a173351c99779d Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Wed, 6 Sep 2023 18:39:33 +0300 Subject: [PATCH 45/89] Fixed testPush0 unit test --- rskj-core/src/test/resources/dsl/push0test.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rskj-core/src/test/resources/dsl/push0test.txt b/rskj-core/src/test/resources/dsl/push0test.txt index 9ba75e52b73..d5f6560987a 100644 --- a/rskj-core/src/test/resources/dsl/push0test.txt +++ b/rskj-core/src/test/resources/dsl/push0test.txt @@ -21,7 +21,7 @@ transaction_build txCreateNew receiverAddress 00 value 0 data 608060405234801561000f575f80fd5b506101238061001d5f395ff3fe6080604052348015600e575f80fd5b50600436106026575f3560e01c8063771602f714602a575b5f80fd5b60406004803603810190603c91906092565b6054565b604051604b919060d6565b60405180910390f35b5f818301905092915050565b5f80fd5b5f819050919050565b6074816064565b8114607d575f80fd5b50565b5f81359050608c81606d565b92915050565b5f806040838503121560a55760a46060565b5b5f60b0858286016080565b925050602060bf858286016080565b9150509250929050565b60d0816064565b82525050565b5f60208201905060e75f83018460c9565b9291505056fea264697066735822122045ed7bb05dd9f1947b2804a8d76b6b7336f31495e814ca7c00a1c4bf60828d8364736f6c63430008140033 - gas 1200000 + gas 600000 build block_build b01 @@ -62,7 +62,7 @@ transaction_build txCreateOld nonce 2 value 0 data 608060405234801561001057600080fd5b5061012f806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063771602f714602d575b600080fd5b60436004803603810190603f9190609a565b6057565b604051604e919060e0565b60405180910390f35b6000818301905092915050565b600080fd5b6000819050919050565b607a816069565b8114608457600080fd5b50565b6000813590506094816073565b92915050565b6000806040838503121560ae5760ad6064565b5b600060ba858286016087565b925050602060c9858286016087565b9150509250929050565b60da816069565b82525050565b600060208201905060f3600083018460d3565b9291505056fea2646970667358221220b386c5fbaa1f4fa300a7ba86a498ed9bdf2b01bffc7263abc34cc285d973482264736f6c63430008110033 - gas 1200000 + gas 600000 build block_build b03 From 2fb66938933deb5a0a5ec6f5c733577e7b9e8ada Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Mon, 11 Sep 2023 18:23:16 +0300 Subject: [PATCH 46/89] Enabled precompiles in parallel list (with option to disable if needed); Extended logging --- .../src/main/java/co/rsk/RskContext.java | 3 +- .../co/rsk/config/RskSystemProperties.java | 10 + .../co/rsk/core/TransactionListExecutor.java | 10 +- .../java/co/rsk/core/bc/BlockExecutor.java | 39 +-- .../bc/ParallelizeTransactionHandler.java | 2 +- .../co/rsk/core/bc/BlockExecutorTest.java | 3 +- .../bc/ParallelizeTransactionHandlerTest.java | 8 +- .../parallel/ParallelExecutionStateTest.java | 250 ++++++++++++++++-- .../co/rsk/mine/TransactionModuleTest.java | 3 +- .../java/co/rsk/net/SyncProcessorTest.java | 3 +- .../remasc/RemascProcessMinerFeesTest.java | 3 +- .../rsk/remasc/RemascStorageProviderTest.java | 3 +- .../java/co/rsk/remasc/RemascTestRunner.java | 3 +- .../src/test/java/co/rsk/test/World.java | 3 +- .../co/rsk/test/builders/BlockBuilder.java | 3 +- .../rsk/test/builders/BlockChainBuilder.java | 3 +- .../co/rsk/test/dsl/WorldDslProcessor.java | 3 +- .../org/ethereum/core/ImportLightTest.java | 3 +- .../ethereum/jsontestsuite/TestRunner.java | 3 +- .../runners/StateTestRunner.java | 3 +- 20 files changed, 291 insertions(+), 70 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/RskContext.java b/rskj-core/src/main/java/co/rsk/RskContext.java index 8ee676ab828..d202c538226 100644 --- a/rskj-core/src/main/java/co/rsk/RskContext.java +++ b/rskj-core/src/main/java/co/rsk/RskContext.java @@ -476,10 +476,9 @@ public synchronized BlockExecutor getBlockExecutor() { if (blockExecutor == null) { blockExecutor = new BlockExecutor( - getRskSystemProperties().getActivationConfig(), getRepositoryLocator(), getTransactionExecutorFactory(), - getRskSystemProperties().isRemascEnabled() + getRskSystemProperties() ); } diff --git a/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java b/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java index ad411a32043..0603a783714 100644 --- a/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java +++ b/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java @@ -62,6 +62,8 @@ public class RskSystemProperties extends SystemProperties { //TODO: REMOVE THIS WHEN THE LocalBLockTests starts working with REMASC private boolean remascEnabled = true; + private boolean concurrentPrecompiledContractsEnabled = true; + private List moduleDescriptions; public RskSystemProperties(ConfigLoader loader) { @@ -226,6 +228,14 @@ public boolean isRemascEnabled() { return remascEnabled; } + public boolean isConcurrentPrecompiledContractsEnabled() { + return concurrentPrecompiledContractsEnabled; + } + + public void setConcurrentPrecompiledContractsEnabled(boolean enabled) { + this.concurrentPrecompiledContractsEnabled = enabled; + } + //TODO: REMOVE THIS WHEN THE LocalBLockTests starts working with REMASC public void setRemascEnabled(boolean remascEnabled) { this.remascEnabled = remascEnabled; diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 23d4fff7bf8..81e449fbd10 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -35,6 +35,7 @@ public class TransactionListExecutor implements Callable { private long totalGas; private int i; private final boolean registerProgramResults; + private final boolean precompiledContractsAllowed; private Coin totalPaidFees; public TransactionListExecutor( @@ -55,6 +56,7 @@ public TransactionListExecutor( int firstTxIndex, Coin totalPaidFees, boolean remascEnabled, + boolean precompiledContractsAllowed, long sublistGasLimit) { this.block = block; this.transactionExecutorFactory = transactionExecutorFactory; @@ -74,6 +76,7 @@ public TransactionListExecutor( this.i = firstTxIndex; this.totalPaidFees = totalPaidFees; this.remascEnabled = remascEnabled; + this.precompiledContractsAllowed = precompiledContractsAllowed; this.sublistGasLimit = sublistGasLimit; } @@ -100,9 +103,12 @@ public Boolean call() { deletedAccounts, true, sublistGasLimit); - boolean transactionExecuted = txExecutor.executeTransaction(); + boolean transactionSucceeded = txExecutor.executeTransaction(); + if (!this.precompiledContractsAllowed && txExecutor.precompiledContractHasBeenCalled()) { + transactionSucceeded = false; + } - if (!acceptInvalidTransactions && !transactionExecuted) { + if (!acceptInvalidTransactions && !transactionSucceeded) { if (discardIfInvalid(tx, numberOfTransactions, isRemascTransaction)) { return false; } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 84109c9d597..54bdda78a0a 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -18,6 +18,7 @@ package co.rsk.core.bc; +import co.rsk.config.RskSystemProperties; import co.rsk.core.*; import co.rsk.crypto.Keccak256; import co.rsk.db.RepositoryLocator; @@ -29,6 +30,7 @@ import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; +import org.ethereum.util.ByteUtil; import org.ethereum.vm.DataWord; import org.ethereum.vm.GasCost; import org.ethereum.vm.PrecompiledContracts; @@ -59,20 +61,21 @@ public class BlockExecutor { private final RepositoryLocator repositoryLocator; private final TransactionExecutorFactory transactionExecutorFactory; private final ActivationConfig activationConfig; - private boolean remascEnabled; + private final boolean remascEnabled; + private final boolean concurrentPrecompiledContractsEnabled; private final Map transactionResults = new ConcurrentHashMap<>(); private boolean registerProgramResults; public BlockExecutor( - ActivationConfig activationConfig, RepositoryLocator repositoryLocator, TransactionExecutorFactory transactionExecutorFactory, - boolean remascEnabled) { + RskSystemProperties systemProperties) { this.repositoryLocator = repositoryLocator; this.transactionExecutorFactory = transactionExecutorFactory; - this.activationConfig = activationConfig; - this.remascEnabled = remascEnabled; + this.activationConfig = systemProperties.getActivationConfig(); + this.remascEnabled = systemProperties.isRemascEnabled(); + this.concurrentPrecompiledContractsEnabled = systemProperties.isConcurrentPrecompiledContractsEnabled(); } /** @@ -458,6 +461,7 @@ private BlockResult executeParallel( start, Coin.ZERO, remascEnabled, + concurrentPrecompiledContractsEnabled, sublistGasLimit ); completionService.submit(txListExecutor); @@ -475,13 +479,13 @@ private BlockResult executeParallel( return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } } catch (InterruptedException e) { - logger.warn("block: [{}] execution was interrupted", block.getNumber()); + logger.warn("block: [{}]/[{}] execution was interrupted", block.getNumber(), block.getHash()); logger.trace("", e); Thread.currentThread().interrupt(); profiler.stop(metric); return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } catch (ExecutionException e) { - logger.warn("block: [{}] execution failed", block.getNumber()); + logger.warn("block: [{}]/[{}] execution failed", block.getNumber(), block.getHash()); logger.trace("", e); profiler.stop(metric); return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; @@ -490,7 +494,7 @@ private BlockResult executeParallel( // Review collision if (readWrittenKeysTracker.detectCollision()) { - logger.warn("block: [{}] execution failed", block.getNumber()); + logger.warn("block: [{}]/[{}] execution failed. Block data: [{}]", block.getNumber(), block.getHash(), ByteUtil.toHexString(block.getEncoded())); profiler.stop(metric); return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } @@ -533,6 +537,7 @@ private BlockResult executeParallel( start, totalPaidFees, remascEnabled, + true, // precompiled Contracts are always allowed in a sequential list sublistGasLimit ); Boolean success = txListExecutor.call(); @@ -634,7 +639,7 @@ private BlockResult executeForMiningAfterRSKIP144( tx, tx.isRemascTransaction(txindex, transactionsList.size()), txExecutor.getGasConsumed(), - txExecutor.precompiledContractHasBeenCalled()); + !concurrentPrecompiledContractsEnabled && txExecutor.precompiledContractHasBeenCalled()); if (!acceptInvalidTransactions && !sublistGasAccumulated.isPresent()) { if (!discardInvalidTxs) { @@ -742,15 +747,13 @@ private List getTransactionReceipts(Map addTxToSublistAndGetAccumulatedGas(IReadWrittenKeysTracker readWrittenKeysTracker, ParallelizeTransactionHandler parallelizeTransactionHandler, Transaction tx, boolean isRemascTransaction, long gasUsed, boolean precompiledContractHasBeenCalled) { + private Optional addTxToSublistAndGetAccumulatedGas(IReadWrittenKeysTracker readWrittenKeysTracker, ParallelizeTransactionHandler parallelizeTransactionHandler, Transaction tx, boolean isRemascTransaction, long gasUsed, boolean isSequentialSublistRequired) { Optional sublistGasAccumulated; - if (precompiledContractHasBeenCalled) { - if (isRemascTransaction) { - sublistGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, gasUsed); - } else { - sublistGasAccumulated = parallelizeTransactionHandler.addTxSentToPrecompiledContract(tx, gasUsed); - } + if (isRemascTransaction) { + sublistGasAccumulated = parallelizeTransactionHandler.addRemascTransaction(tx, gasUsed); + } else if (isSequentialSublistRequired) { + sublistGasAccumulated = parallelizeTransactionHandler.addTxToSequentialSublist(tx, gasUsed); } else { sublistGasAccumulated = parallelizeTransactionHandler.addTransaction(tx, readWrittenKeysTracker.getThisThreadReadKeys(), readWrittenKeysTracker.getThisThreadWrittenKeys(), gasUsed); } @@ -782,8 +785,8 @@ private TransactionReceipt buildTransactionReceipt(Transaction tx, TransactionEx } private BlockResult getBlockResultAndLogExecutionInterrupted(Block block, Metric metric, Transaction tx) { - logger.warn("block: [{}] execution interrupted because of invalid tx: [{}]", - block.getNumber(), tx.getHash()); + logger.warn("block: [{}]/[{}] execution interrupted because of invalid tx: [{}]", + block.getNumber(), block.getHash(), tx.getHash()); profiler.stop(metric); return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index 8c3ae580d7b..b836a0fc468 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -75,7 +75,7 @@ public Optional addRemascTransaction(Transaction tx, long gasUsedByTx) { // complete the execution. So, there is no way for the ParallelTransactionHandler to track the keys if the execution // fails since the Precompiled contract rolls back the repository. // Therefore, the tx is added to the sequential sublist to avoid possible race conditions. - public Optional addTxSentToPrecompiledContract(Transaction tx, long gasUsedByTx) { + public Optional addTxToSequentialSublist(Transaction tx, long gasUsedByTx) { TransactionSublist sequentialSublist = getSequentialSublist(); if (sublistDoesNotHaveEnoughGas(tx, sequentialSublist)) { diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index 6e16a6e50c3..1d232a7f559 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -1478,7 +1478,6 @@ private static BlockExecutor buildBlockExecutor(TrieStore store, RskSystemProper BlockTxSignatureCache signatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); return new BlockExecutor( - cfg.getActivationConfig(), new RepositoryLocator(store, stateRootHandler), new TransactionExecutorFactory( cfg, @@ -1489,7 +1488,7 @@ private static BlockExecutor buildBlockExecutor(TrieStore store, RskSystemProper new PrecompiledContracts(cfg, bridgeSupportFactory, signatureCache), signatureCache ), - cfg.isRemascEnabled()); + cfg); } public static class TestObjects { diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index 30001bbfcce..bf75bcc2961 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -696,12 +696,12 @@ void aTxDirectedToAPrecompiledContractAddedShouldBeInTheSequentialSublist() { Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional sequentialSublistGasUsedAfterBigTx = handler.addTxSentToPrecompiledContract(bigTx, gasUsedByBigTx); + Optional sequentialSublistGasUsedAfterBigTx = handler.addTxToSequentialSublist(bigTx, gasUsedByBigTx); Assertions.assertTrue(sequentialSublistGasUsedAfterBigTx.isPresent()); Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); Assertions.assertEquals(gasUsedByBigTx, (long) sequentialSublistGasUsedAfterBigTx.get()); - Optional sequentialSublistGasUsedAfterTx = handler.addTxSentToPrecompiledContract(tx, gasUsedByTx); + Optional sequentialSublistGasUsedAfterTx = handler.addTxToSequentialSublist(tx, gasUsedByTx); Assertions.assertFalse(sequentialSublistGasUsedAfterTx.isPresent()); Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); @@ -714,7 +714,7 @@ void aTxDirectedToAPrecompiledContractAddedWithSequentialSublistFullShouldNotBeA long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional sequentialSublistGasUsed = handler.addTxSentToPrecompiledContract(tx, gasUsedByTx); + Optional sequentialSublistGasUsed = handler.addTxToSequentialSublist(tx, gasUsedByTx); Assertions.assertTrue(sequentialSublistGasUsed.isPresent()); Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); @@ -729,7 +729,7 @@ void whenATxCallsAPrecompiledAnotherWithTheSameSenderShouldGoToSequential() { long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional sequentialSublistGasUsed = handler.addTxSentToPrecompiledContract(tx, gasUsedByTx); + Optional sequentialSublistGasUsed = handler.addTxToSequentialSublist(tx, gasUsedByTx); Assertions.assertTrue(sequentialSublistGasUsed.isPresent()); Assertions.assertEquals(gasUsedByTx, handler.getGasUsedIn(sequentialSublistNumber)); diff --git a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java index 39e88bf811a..b751979b831 100644 --- a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java +++ b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java @@ -35,6 +35,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import javax.annotation.Nullable; import java.math.BigInteger; import java.util.List; @@ -47,17 +48,27 @@ class ParallelExecutionStateTest { public static final byte[] FAILED_STATUS = EMPTY_BYTE_ARRAY; private World createWorld(int rskip144) { + return createWorld(rskip144, null); + } + + private World createWorld(int rskip144, @Nullable Boolean concurrentPrecompiledContractsEnabled) { ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); TestSystemProperties config = new TestSystemProperties(rawConfig -> rawConfig.withValue("blockchain.config.consensusRules.rskip144", ConfigValueFactory.fromAnyRef(rskip144)) ); + if (concurrentPrecompiledContractsEnabled != null) { + config.setConcurrentPrecompiledContractsEnabled(concurrentPrecompiledContractsEnabled); + } - World world = new World(receiptStore, config); - return world; + return new World(receiptStore, config); } private World createWorldAndProcess(String dsl, int rskip144ActivationHeight) throws DslProcessorException { - World world = createWorld(rskip144ActivationHeight); + return createWorldAndProcess(dsl, rskip144ActivationHeight, null); + } + + private World createWorldAndProcess(String dsl, int rskip144ActivationHeight, @Nullable Boolean concurrentPrecompiledContractsEnabled) throws DslProcessorException { + World world = createWorld(rskip144ActivationHeight, concurrentPrecompiledContractsEnabled); DslParser parser = new DslParser(dsl); WorldDslProcessor processor = new WorldDslProcessor(world); @@ -833,7 +844,7 @@ void useSequentialForCollisionWithSequentialAndParallel() throws DslProcessorExc } @Test - void whenATxCallsAPrecompiledItShouldGoToSequential() throws DslProcessorException { + void whenATxCallsAPrecompiledItShouldGoToSequentialIfDisabled() throws DslProcessorException { World parallel = this.createWorldAndProcess( "account_new acc1 10000000\n" + "transaction_build tx01\n" + @@ -853,14 +864,41 @@ void whenATxCallsAPrecompiledItShouldGoToSequential() throws DslProcessorExcepti "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0); + "\n", 0, false); Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } @Test - void whenATxSendsValueToAPrecompiledItShouldGoToSequential() throws DslProcessorException { + void whenATxCallsAPrecompiledItShouldGoToParallelIfEnabled() throws DslProcessorException { + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress " + PrecompiledContracts.IDENTITY_ADDR +"\n"+ + " data " + "1234" + "\n" + + " gas 1000000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " gasLimit 7500000\n" + + " transactions tx01\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "\n", 0, true); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenATxSendsValueToAPrecompiledItShouldGoToSequentialIfDisabled() throws DslProcessorException { World parallel = this.createWorldAndProcess( "account_new acc1 10000000\n" + "transaction_build tx01\n" + @@ -880,14 +918,41 @@ void whenATxSendsValueToAPrecompiledItShouldGoToSequential() throws DslProcessor "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0); + "\n", 0, false); Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } @Test - void whenATxCallsAPrecompiledThatThrowsAnExceptionShouldGoToSequential() throws DslProcessorException { + void whenATxSendsValueToAPrecompiledItShouldGoToParallelIfEnabled() throws DslProcessorException { + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress " + PrecompiledContracts.IDENTITY_ADDR +"\n"+ + " value 1000\n" + + " gas 1000000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " gasLimit 7500000\n" + + " transactions tx01\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "\n", 0, true); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenATxCallsAPrecompiledThatThrowsAnExceptionShouldGoToSequentialIfDisabled() throws DslProcessorException { // TX01 throws an exception, though, there is no way to check from out of the execution if the precompiled contract call has failed. World parallel = this.createWorldAndProcess( "account_new acc1 10000000\n" + @@ -908,14 +973,42 @@ void whenATxCallsAPrecompiledThatThrowsAnExceptionShouldGoToSequential() throws "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0); + "\n", 0, false); Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } @Test - void whenATxCallsAContractThatCallsAPrecompiledShouldGoToSequential() throws DslProcessorException { + void whenATxCallsAPrecompiledThatThrowsAnExceptionShouldGoToParallelIfEnabled() throws DslProcessorException { + // TX01 throws an exception, though, there is no way to check from out of the execution if the precompiled contract call has failed. + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress " + PrecompiledContracts.BRIDGE_ADDR +"\n"+ + " data " + "e674f5e80000000000000000000000000000000000000000000000000000000001000006" + "\n" + + " gas 1000000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " gasLimit 7500000\n" + + " transactions tx01\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "\n", 0, true); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenATxCallsAContractThatCallsAPrecompiledShouldGoToSequentialIfDisabled() throws DslProcessorException { World parallel = this.createWorldAndProcess( "account_new acc1 10000000\n" + "transaction_build tx01\n" + @@ -944,14 +1037,50 @@ void whenATxCallsAContractThatCallsAPrecompiledShouldGoToSequential() throws Dsl "assert_best b01\n" + "assert_tx_success tx01\n" + "assert_tx_success tx02\n" + - "\n", 0); + "\n", 0, false); Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } @Test - void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequential() throws DslProcessorException { + void whenATxCallsAContractThatCallsAPrecompiledShouldGoToParallelIfEnabled() throws DslProcessorException { + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress 00\n" + + " data " + PROXY_SMART_CONTRACT_BYTECODE + PrecompiledContracts.IDENTITY_ADDR + "\n" + + " gas 1200000\n" + + " build\n" + + "\n" + + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + "688c62c5000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000145b38da6a701c568545dcfcb03fcb875f56beddc4000000000000000000000000" + "\n" + + " nonce 1\n" + + " gas 1000000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " gasLimit 7500000\n" + + " transactions tx01 tx02\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "assert_tx_success tx02\n" + + "\n", 0, true); + + Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{2}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequentialIfDisabled() throws DslProcessorException { World parallel = this.createWorldAndProcess( "account_new acc1 10000000\n" + "transaction_build tx01\n" + @@ -979,7 +1108,7 @@ void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequential() "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0); + "\n", 0, false); Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(FAILED_STATUS, parallel.getTransactionReceiptByName("tx02").getStatus()); @@ -987,8 +1116,44 @@ void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequential() } @Test - void whenAnInternalTxCallsAContractThatCallsAPrecompiledShouldGoToSequential() throws DslProcessorException { - World parallel = createWorld(0); + void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToParallelIfEnabled() throws DslProcessorException { + World parallel = this.createWorldAndProcess( + "account_new acc1 10000000\n" + + "transaction_build tx01\n" + + " sender acc1\n" + + " receiverAddress 00\n" + + " data " + PROXY_SMART_CONTRACT_BYTECODE + PrecompiledContracts.BRIDGE_ADDR + "\n" + + " gas 1200000\n" + + " build\n" + + "\n" + + "transaction_build tx02\n" + + " sender acc1\n" + + " contract tx01\n" + + " data " + "688c62c500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024e674f5e8000000000000000000000000000000000000000000000000000000000100000600000000000000000000000000000000000000000000000000000000" + "\n" + + " nonce 1\n" + + " gas 1000000\n" + + " build\n" + + "\n" + + "block_build b01\n" + + " parent g00\n" + + " gasLimit 7500000\n" + + " transactions tx01 tx02\n" + + " build\n" + + "\n" + + "block_connect b01\n" + + "\n" + + "assert_best b01\n" + + "assert_tx_success tx01\n" + + "\n", 0, true); + + Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(FAILED_STATUS, parallel.getTransactionReceiptByName("tx02").getStatus()); + Assertions.assertArrayEquals(new short[]{2}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenAnInternalTxCallsAContractThatCallsAPrecompiledShouldGoToSequentialIfDisabled() throws DslProcessorException { + World parallel = createWorld(0, false); WorldDslProcessor processor = new WorldDslProcessor(parallel); String dsl = deployProxyTo(PrecompiledContracts.IDENTITY_ADDR); DslParser parser = new DslParser(dsl); @@ -1012,8 +1177,33 @@ void whenAnInternalTxCallsAContractThatCallsAPrecompiledShouldGoToSequential() t } @Test - void whenAnInternalTxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequential() throws DslProcessorException { - World parallel = createWorld(0); + void whenAnInternalTxCallsAContractThatCallsAPrecompiledShouldGoToParallelIfEnabled() throws DslProcessorException { + World parallel = createWorld(0, true); + WorldDslProcessor processor = new WorldDslProcessor(parallel); + String dsl = deployProxyTo(PrecompiledContracts.IDENTITY_ADDR); + DslParser parser = new DslParser(dsl); + processor.processCommands(parser); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + + RskAddress proxyContract = new RskAddress(HashUtil.calcNewAddr(parallel.getAccountByName("acc1").getAddress().getBytes(), BigInteger.ZERO.toByteArray())); + String dataInputToCallProxyToProxyThatReverts = "688c62c5000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000145fd6eb55d12e759a21c09ef703fe0cba1dc9d88d000000000000000000000000"; + String transactionAssertions = "assert_tx_success tx02\n" + + "assert_tx_success tx03\n" + + "\n"; + String dslNext = + deployProxyToProxyCallAContractAndAssert(proxyContract, dataInputToCallProxyToProxyThatReverts, transactionAssertions); + + DslParser parserNext = new DslParser(dslNext); + processor.processCommands(parserNext); + Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{2}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + + @Test + void whenAnInternalTxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequentialIfDisabled() throws DslProcessorException { + World parallel = createWorld(0, false); WorldDslProcessor processor = new WorldDslProcessor(parallel); String dsl = deployProxyTo(PrecompiledContracts.BRIDGE_ADDR); DslParser parser = new DslParser(dsl); @@ -1037,6 +1227,32 @@ void whenAnInternalTxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequ Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); } + @Test + void whenAnInternalTxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToParallelIfEnabled() throws DslProcessorException { + World parallel = createWorld(0, true); + WorldDslProcessor processor = new WorldDslProcessor(parallel); + String dsl = deployProxyTo(PrecompiledContracts.BRIDGE_ADDR); + DslParser parser = new DslParser(dsl); + processor.processCommands(parser); + + Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + + RskAddress proxyContract = new RskAddress(HashUtil.calcNewAddr(parallel.getAccountByName("acc1").getAddress().getBytes(), BigInteger.ZERO.toByteArray())); + String dataInputToCallProxyToProxy = "688c62c500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024e674f5e8000000000000000000000000000000000000000000000000000000000100000600000000000000000000000000000000000000000000000000000000"; + String transactionAssertions = "assert_tx_success tx02\n" + + "\n"; + + String dslNext = + deployProxyToProxyCallAContractAndAssert(proxyContract, dataInputToCallProxyToProxy, transactionAssertions); + + DslParser parserNext = new DslParser(dslNext); + processor.processCommands(parserNext); + Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); + Assertions.assertArrayEquals(FAILED_STATUS, parallel.getTransactionReceiptByName("tx03").getStatus()); + Assertions.assertArrayEquals(new short[]{2}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); + } + private static String deployProxyToProxyCallAContractAndAssert(RskAddress proxyContract, String dataInputToCallProxyToProxyThatReverts, String transactionAssertions) { return "transaction_build tx02\n" + " sender acc1\n" + diff --git a/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java b/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java index 47805b2cfb4..7ebfcd3d44f 100644 --- a/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java +++ b/rskj-core/src/test/java/co/rsk/mine/TransactionModuleTest.java @@ -584,10 +584,9 @@ private Web3Impl internalCreateEnvironment(Blockchain blockchain, this.transactionExecutorFactory = transactionExecutorFactory; MiningConfig miningConfig = ConfigUtils.getDefaultMiningConfig(); BlockExecutor blockExecutor = new BlockExecutor( - config.getActivationConfig(), repositoryLocator, this.transactionExecutorFactory, - config.isRemascEnabled()); + config); MinerServer minerServer = new MinerServerImpl( config, diff --git a/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java b/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java index 7c30068c2f0..04a330f661c 100644 --- a/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java +++ b/rskj-core/src/test/java/co/rsk/net/SyncProcessorTest.java @@ -804,7 +804,6 @@ void processBodyResponseWithTransactionAddsToBlockchain() { config.getActivationConfig(), signatureCache); BlockExecutor blockExecutor = new BlockExecutor( - config.getActivationConfig(), new RepositoryLocator(blockChainBuilder.getTrieStore(), stateRootHandler), new TransactionExecutorFactory( config, @@ -815,7 +814,7 @@ void processBodyResponseWithTransactionAddsToBlockchain() { new PrecompiledContracts(config, bridgeSupportFactory, signatureCache), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) ), - config.isRemascEnabled()); + config); Assertions.assertEquals(1, block.getTransactionsList().size()); blockExecutor.executeAndFillAll(block, genesis.getHeader()); Assertions.assertEquals(21000, block.getFeesPaidToMiner().asBigInteger().intValueExact()); diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascProcessMinerFeesTest.java b/rskj-core/src/test/java/co/rsk/remasc/RemascProcessMinerFeesTest.java index 197ef648547..3ad371d9b27 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascProcessMinerFeesTest.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascProcessMinerFeesTest.java @@ -1014,7 +1014,6 @@ private BlockExecutor buildBlockExecutor(RepositoryLocator repositoryLocator, Bl config.getActivationConfig(), signatureCache); return new BlockExecutor( - config.getActivationConfig(), repositoryLocator, new TransactionExecutorFactory( config, @@ -1025,6 +1024,6 @@ private BlockExecutor buildBlockExecutor(RepositoryLocator repositoryLocator, Bl new PrecompiledContracts(config, bridgeSupportFactory, signatureCache), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) ), - config.isRemascEnabled()); + config); } } diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascStorageProviderTest.java b/rskj-core/src/test/java/co/rsk/remasc/RemascStorageProviderTest.java index beeea19a045..8d22fdcd5f1 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascStorageProviderTest.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascStorageProviderTest.java @@ -444,7 +444,6 @@ void paysOnlyBlocksWithEnoughBalanceAccumulatedAfterRFS() throws IOException { config.getNetworkConstants().getBridgeConstants(), config.getActivationConfig(), signatureCache); BlockExecutor blockExecutor = new BlockExecutor( - config.getActivationConfig(), repositoryLocator, new TransactionExecutorFactory( config, @@ -455,7 +454,7 @@ void paysOnlyBlocksWithEnoughBalanceAccumulatedAfterRFS() throws IOException { new PrecompiledContracts(config, bridgeSupportFactory, signatureCache), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) ), - config.isRemascEnabled()); + config); for (Block b : blocks) { blockExecutor.executeAndFillAll(b, blockchain.getBestBlock().getHeader()); diff --git a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java index 416d8d6a646..be2ace22948 100644 --- a/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java +++ b/rskj-core/src/test/java/co/rsk/remasc/RemascTestRunner.java @@ -137,7 +137,6 @@ public void start() { builder.getConfig().getActivationConfig(), blockTxSignatureCache); PrecompiledContracts precompiledContracts = new PrecompiledContracts(builder.getConfig(), bridgeSupportFactory, blockTxSignatureCache); BlockExecutor blockExecutor = new BlockExecutor( - builder.getConfig().getActivationConfig(), builder.getRepositoryLocator(), new TransactionExecutorFactory( builder.getConfig(), @@ -148,7 +147,7 @@ public void start() { precompiledContracts, blockTxSignatureCache ), - builder.getConfig().isRemascEnabled()); + builder.getConfig()); Random random = new Random(RemascTestRunner.class.hashCode()); for(int i = 0; i <= this.initialHeight; i++) { diff --git a/rskj-core/src/test/java/co/rsk/test/World.java b/rskj-core/src/test/java/co/rsk/test/World.java index 8aacc45698e..94694fd3bb5 100644 --- a/rskj-core/src/test/java/co/rsk/test/World.java +++ b/rskj-core/src/test/java/co/rsk/test/World.java @@ -179,7 +179,6 @@ public BlockExecutor getBlockExecutor() { if (this.blockExecutor == null) { this.blockExecutor = new BlockExecutor( - config.getActivationConfig(), new RepositoryLocator(getTrieStore(), stateRootHandler), new TransactionExecutorFactory( config, @@ -190,7 +189,7 @@ public BlockExecutor getBlockExecutor() { new PrecompiledContracts(config, bridgeSupportFactory, blockTxSignatureCache), blockTxSignatureCache ), - config.isRemascEnabled()); + config); } return this.blockExecutor; diff --git a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java index d53b60acdd1..0d3f95ae8bc 100644 --- a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java +++ b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java @@ -118,7 +118,6 @@ public Block build(final RskSystemProperties config) { if (blockChain != null) { StateRootHandler stateRootHandler = new StateRootHandler(config.getActivationConfig(), new StateRootsStoreImpl(new HashMapDB())); BlockExecutor executor = new BlockExecutor( - config.getActivationConfig(), new RepositoryLocator(trieStore, stateRootHandler), new TransactionExecutorFactory( config, @@ -129,7 +128,7 @@ public Block build(final RskSystemProperties config) { new PrecompiledContracts(config, bridgeSupportFactory, new BlockTxSignatureCache(new ReceivedTxSignatureCache())), new BlockTxSignatureCache(new ReceivedTxSignatureCache()) ), - config.isRemascEnabled()); + config); executor.executeAndFill(block, parent.getHeader()); } diff --git a/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java b/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java index fc6e2f53a84..ad30260fb2f 100644 --- a/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java +++ b/rskj-core/src/test/java/co/rsk/test/builders/BlockChainBuilder.java @@ -247,10 +247,9 @@ public BlockChainImpl build() { config, repositoryLocator, this.blockStore, blockFactory, new TestCompositeEthereumListener(), transactionExecutorFactory, new ReceivedTxSignatureCache(), 10, 100, Mockito.mock(TxQuotaChecker.class), Mockito.mock(GasPriceTracker.class)); BlockExecutor blockExecutor = new BlockExecutor( - config.getActivationConfig(), repositoryLocator, transactionExecutorFactory, - config.isRemascEnabled()); + config); BlockChainImpl blockChain = new BlockChainLoader( blockStore, receiptStore, diff --git a/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java b/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java index 146a8f7dfee..8c4f09722a2 100644 --- a/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java +++ b/rskj-core/src/test/java/co/rsk/test/dsl/WorldDslProcessor.java @@ -316,7 +316,6 @@ private void processBlockChainCommand(DslCommand cmd) { final TestSystemProperties config = new TestSystemProperties(); StateRootHandler stateRootHandler = new StateRootHandler(config.getActivationConfig(), new StateRootsStoreImpl(new HashMapDB())); BlockExecutor executor = new BlockExecutor( - config.getActivationConfig(), new RepositoryLocator(world.getTrieStore(), stateRootHandler), new TransactionExecutorFactory( config, @@ -327,7 +326,7 @@ private void processBlockChainCommand(DslCommand cmd) { null, world.getBlockTxSignatureCache() ), - config.isRemascEnabled()); + config); executor.executeAndFill(block, parent.getHeader()); world.saveBlock(name, block); parent = block; diff --git a/rskj-core/src/test/java/org/ethereum/core/ImportLightTest.java b/rskj-core/src/test/java/org/ethereum/core/ImportLightTest.java index 13fb9a10edc..3d1489a4680 100644 --- a/rskj-core/src/test/java/org/ethereum/core/ImportLightTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/ImportLightTest.java @@ -86,10 +86,9 @@ public static BlockChainImpl createBlockchain( listener, new DummyBlockValidator(), new BlockExecutor( - config.getActivationConfig(), repositoryLocator, transactionExecutorFactory, - config.isRemascEnabled()), + config), stateRootHandler ); diff --git a/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java b/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java index c2f89d43b11..f19d5df451e 100644 --- a/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java +++ b/rskj-core/src/test/java/org/ethereum/jsontestsuite/TestRunner.java @@ -163,10 +163,9 @@ public List runTestCase(BlockTestingCase testCase) { null, new DummyBlockValidator(), new BlockExecutor( - config.getActivationConfig(), new RepositoryLocator(trieStore, stateRootHandler), transactionExecutorFactory, - config.isRemascEnabled()), + config), stateRootHandler ); diff --git a/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java b/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java index 7b20e0ba7ff..c9faca813f2 100644 --- a/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java +++ b/rskj-core/src/test/java/org/ethereum/jsontestsuite/runners/StateTestRunner.java @@ -152,7 +152,6 @@ public List runImpl() { null, null, new BlockExecutor( - config.getActivationConfig(), new RepositoryLocator(trieStore, stateRootHandler), new TransactionExecutorFactory( config, @@ -163,7 +162,7 @@ public List runImpl() { precompiledContracts, new BlockTxSignatureCache(new ReceivedTxSignatureCache()) ), - config.isRemascEnabled()), + config), stateRootHandler ); From 28bf9959a6ded911ec8685447b2a235c351c7220 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Wed, 13 Sep 2023 18:49:05 +0300 Subject: [PATCH 47/89] Added posibility to specify in code which precompiles are allowed to run in parallel --- .../co/rsk/config/RskSystemProperties.java | 16 +++-- .../co/rsk/core/TransactionListExecutor.java | 8 +-- .../java/co/rsk/core/bc/BlockExecutor.java | 10 +-- .../ethereum/core/TransactionExecutor.java | 13 ++-- .../src/main/java/org/ethereum/vm/VM.java | 1 - .../java/org/ethereum/vm/program/Program.java | 11 ++-- .../PrecompiledContractHasBeenCalledTest.java | 64 +++++++++---------- .../parallel/ParallelExecutionStateTest.java | 40 ++++++------ .../src/test/java/org/ethereum/vm/VMTest.java | 6 +- 9 files changed, 89 insertions(+), 80 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java b/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java index 0603a783714..5dd9e28a388 100644 --- a/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java +++ b/rskj-core/src/main/java/co/rsk/config/RskSystemProperties.java @@ -20,6 +20,7 @@ import co.rsk.core.RskAddress; import co.rsk.rpc.ModuleDescription; +import com.google.common.annotations.VisibleForTesting; import com.typesafe.config.Config; import com.typesafe.config.ConfigObject; import com.typesafe.config.ConfigValue; @@ -29,7 +30,9 @@ import org.ethereum.core.Account; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.HashUtil; +import org.ethereum.vm.PrecompiledContracts; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.nio.charset.StandardCharsets; import java.time.Duration; @@ -62,7 +65,9 @@ public class RskSystemProperties extends SystemProperties { //TODO: REMOVE THIS WHEN THE LocalBLockTests starts working with REMASC private boolean remascEnabled = true; - private boolean concurrentPrecompiledContractsEnabled = true; + private Set concurrentContractsDisallowed = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + PrecompiledContracts.REMASC_ADDR, PrecompiledContracts.BRIDGE_ADDR + ))); private List moduleDescriptions; @@ -228,12 +233,13 @@ public boolean isRemascEnabled() { return remascEnabled; } - public boolean isConcurrentPrecompiledContractsEnabled() { - return concurrentPrecompiledContractsEnabled; + public Set concurrentContractsDisallowed() { + return concurrentContractsDisallowed; } - public void setConcurrentPrecompiledContractsEnabled(boolean enabled) { - this.concurrentPrecompiledContractsEnabled = enabled; + @VisibleForTesting + public void setConcurrentContractsDisallowed(@Nonnull Set disallowed) { + this.concurrentContractsDisallowed = Collections.unmodifiableSet(new HashSet<>(disallowed)); } //TODO: REMOVE THIS WHEN THE LocalBLockTests starts working with REMASC diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 81e449fbd10..9347f9fb170 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -35,7 +35,7 @@ public class TransactionListExecutor implements Callable { private long totalGas; private int i; private final boolean registerProgramResults; - private final boolean precompiledContractsAllowed; + private final Set concurrentContractsDisallowed; private Coin totalPaidFees; public TransactionListExecutor( @@ -56,7 +56,7 @@ public TransactionListExecutor( int firstTxIndex, Coin totalPaidFees, boolean remascEnabled, - boolean precompiledContractsAllowed, + Set concurrentContractsDisallowed, long sublistGasLimit) { this.block = block; this.transactionExecutorFactory = transactionExecutorFactory; @@ -76,7 +76,7 @@ public TransactionListExecutor( this.i = firstTxIndex; this.totalPaidFees = totalPaidFees; this.remascEnabled = remascEnabled; - this.precompiledContractsAllowed = precompiledContractsAllowed; + this.concurrentContractsDisallowed = Collections.unmodifiableSet(new HashSet<>(concurrentContractsDisallowed)); this.sublistGasLimit = sublistGasLimit; } @@ -104,7 +104,7 @@ public Boolean call() { true, sublistGasLimit); boolean transactionSucceeded = txExecutor.executeTransaction(); - if (!this.precompiledContractsAllowed && txExecutor.precompiledContractHasBeenCalled()) { + if (!this.concurrentContractsDisallowed.isEmpty() && txExecutor.precompiledContractsCalled().stream().anyMatch(this.concurrentContractsDisallowed::contains)) { transactionSucceeded = false; } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 54bdda78a0a..3536c68a6dd 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -62,7 +62,7 @@ public class BlockExecutor { private final TransactionExecutorFactory transactionExecutorFactory; private final ActivationConfig activationConfig; private final boolean remascEnabled; - private final boolean concurrentPrecompiledContractsEnabled; + private final Set concurrentContractsDisallowed; private final Map transactionResults = new ConcurrentHashMap<>(); private boolean registerProgramResults; @@ -75,7 +75,7 @@ public BlockExecutor( this.transactionExecutorFactory = transactionExecutorFactory; this.activationConfig = systemProperties.getActivationConfig(); this.remascEnabled = systemProperties.isRemascEnabled(); - this.concurrentPrecompiledContractsEnabled = systemProperties.isConcurrentPrecompiledContractsEnabled(); + this.concurrentContractsDisallowed = Collections.unmodifiableSet(new HashSet<>(systemProperties.concurrentContractsDisallowed())); } /** @@ -461,7 +461,7 @@ private BlockResult executeParallel( start, Coin.ZERO, remascEnabled, - concurrentPrecompiledContractsEnabled, + concurrentContractsDisallowed, sublistGasLimit ); completionService.submit(txListExecutor); @@ -537,7 +537,7 @@ private BlockResult executeParallel( start, totalPaidFees, remascEnabled, - true, // precompiled Contracts are always allowed in a sequential list + Collections.emptySet(), // precompiled contracts are always allowed in a sequential list, as there's no concurrency in it sublistGasLimit ); Boolean success = txListExecutor.call(); @@ -639,7 +639,7 @@ private BlockResult executeForMiningAfterRSKIP144( tx, tx.isRemascTransaction(txindex, transactionsList.size()), txExecutor.getGasConsumed(), - !concurrentPrecompiledContractsEnabled && txExecutor.precompiledContractHasBeenCalled()); + txExecutor.precompiledContractsCalled().stream().anyMatch(this.concurrentContractsDisallowed::contains)); if (!acceptInvalidTransactions && !sublistGasAccumulated.isPresent()) { if (!discardInvalidTxs) { diff --git a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java index 77dcae4fb1f..cc75a149584 100644 --- a/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java +++ b/rskj-core/src/main/java/org/ethereum/core/TransactionExecutor.java @@ -44,6 +44,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; import java.math.BigInteger; import java.util.*; @@ -101,7 +102,8 @@ public class TransactionExecutor { private final long sublistGasLimit; private boolean localCall = false; - private boolean precompiledContractHasBeenCalledFlag = false; + + private final Set precompiledContractsCalled = new HashSet<>(); private final boolean postponeFeePayment; @@ -313,7 +315,7 @@ private void call() { this.subtraces = new ArrayList<>(); if (precompiledContract != null) { - this.precompiledContractHasBeenCalledFlag = true; + this.precompiledContractsCalled.add(targetAddress); Metric metric = profiler.start(Profiler.PROFILING_TYPE.PRECOMPILED_CONTRACT_INIT); precompiledContract.init(tx, executionBlock, track, blockStore, receiptStore, result.getLogInfoList()); profiler.stop(metric); @@ -434,7 +436,7 @@ private void go() { // This line checks whether the invoked smart contract calls a Precompiled contract. // This flag is then taken by the Parallel transaction handler, if the tx calls a precompiled contract, // it should be executed sequentially. - precompiledContractHasBeenCalledFlag |= program.precompiledContractHasBeenCalled(); + this.precompiledContractsCalled.addAll(program.precompiledContractsCalled()); result = program.getResult(); gasLeftover = GasCost.subtract(GasCost.toGas(tx.getGasLimit()), program.getResult().getGasUsed()); @@ -688,7 +690,8 @@ public long getGasConsumed() { public Coin getPaidFees() { return paidFees; } - public boolean precompiledContractHasBeenCalled() { - return this.precompiledContractHasBeenCalledFlag; + @Nonnull + public Set precompiledContractsCalled() { + return this.precompiledContractsCalled.isEmpty() ? Collections.emptySet() : new HashSet<>(this.precompiledContractsCalled); } } diff --git a/rskj-core/src/main/java/org/ethereum/vm/VM.java b/rskj-core/src/main/java/org/ethereum/vm/VM.java index babe9160899..407733cdf7b 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/VM.java +++ b/rskj-core/src/main/java/org/ethereum/vm/VM.java @@ -1501,7 +1501,6 @@ protected void doCALL(){ PrecompiledContracts.PrecompiledContract precompiledContract = precompiledContracts.getContractForAddress(activations, codeAddress); if (precompiledContract != null) { - program.callToPrecompiledAddress(msg, precompiledContract); } else { program.callToAddress(msg); diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java index 6e0c027e7a2..a4d5013b381 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -112,7 +112,7 @@ public class Program { private final Set deletedAccountsInBlock; private final SignatureCache signatureCache; - private boolean precompiledContractHasBeenCalled = false; + private final Set precompiledContractsCalled = new HashSet<>(); public Program( VmConfig config, @@ -835,7 +835,7 @@ private boolean executeCode( getTrace().merge(program.getTrace()); getResult().merge(childResult); - this.precompiledContractHasBeenCalled |= program.precompiledContractHasBeenCalled; + this.precompiledContractsCalled.addAll(program.precompiledContractsCalled()); boolean childCallSuccessful = true; @@ -1333,7 +1333,7 @@ public int verifyJumpDest(DataWord nextPC) { } public void callToPrecompiledAddress(MessageCall msg, PrecompiledContract contract) { - this.precompiledContractHasBeenCalled = true; + this.precompiledContractsCalled.add(new RskAddress(msg.getCodeAddress())); if (getCallDeep() == getMaxDepth()) { stackPushZero(); this.refundGas(msg.getGas().longValue(), " call deep limit reach"); @@ -1488,8 +1488,9 @@ private boolean byTestingSuite() { return invoke.byTestingSuite(); } - public boolean precompiledContractHasBeenCalled() { - return this.precompiledContractHasBeenCalled; + @Nonnull + public Set precompiledContractsCalled() { + return precompiledContractsCalled.isEmpty() ? Collections.emptySet() : new HashSet<>(this.precompiledContractsCalled); } public interface ProgramOutListener { diff --git a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java index ec0d006b599..7bf5223d6a8 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java @@ -68,40 +68,40 @@ public void setUp() { void whenATxCallsAPrecompiledContractPrecompiledContractHasBeenCalledFlagShouldBeTrue() { byte[] arbitraryData = Hex.decode("00112233"); long value = 0L; - boolean precompiledHasBeenCalled = true; + Set precompiledContractsCalled = Collections.singleton(PrecompiledContracts.IDENTITY_ADDR); boolean hasRevert = false; boolean threwAnException = false; RskAddress destination = PrecompiledContracts.IDENTITY_ADDR; - executeATransactionAndAssert(getABlockWithATx(track, config, arbitraryData, value, destination, sender), txIndex, precompiledHasBeenCalled, hasRevert, threwAnException); + executeATransactionAndAssert(getABlockWithATx(track, config, arbitraryData, value, destination, sender), txIndex, precompiledContractsCalled, hasRevert, threwAnException); } @Test void whenATxSendsValueToAPrecompiledContractPrecompiledContractHasBeenCalledFlagShouldBeTrue() { int value = 1; - boolean precompiledHasBeenCalled = true; + Set precompiledContractsCalled = Collections.singleton(PrecompiledContracts.IDENTITY_ADDR); boolean hasRevert = false; boolean threwAnException = false; RskAddress destination = PrecompiledContracts.IDENTITY_ADDR; - executeATransactionAndAssert(getABlockWithATx(track, config, null, value, destination, sender), txIndex, precompiledHasBeenCalled, hasRevert, threwAnException); + executeATransactionAndAssert(getABlockWithATx(track, config, null, value, destination, sender), txIndex, precompiledContractsCalled, hasRevert, threwAnException); } @Test void whenATxCallsAPrecompiledContractAndThrowsAnExceptionPrecompiledContractHasBeenCalledFlagShouldBeTrue() { byte[] dataThatThrowsAnException = Hex.decode("e674f5e80000000000000000000000000000000000000000000000000000000001000006"); int value = 0; - boolean precompiledHasBeenCalled = true; + Set precompiledContractsCalled = Collections.singleton(PrecompiledContracts.BRIDGE_ADDR); boolean hasRevert = false; boolean threwAnException = true; RskAddress destination = PrecompiledContracts.BRIDGE_ADDR; - executeATransactionAndAssert(getABlockWithATx(track, config, dataThatThrowsAnException, value, destination, sender), txIndex, precompiledHasBeenCalled, hasRevert, threwAnException); + executeATransactionAndAssert(getABlockWithATx(track, config, dataThatThrowsAnException, value, destination, sender), txIndex, precompiledContractsCalled, hasRevert, threwAnException); } @Test void ifAnAccountSendsValueToAnAccountPrecompiledContractHasBeenCalledFlagShouldBeFalse() { Coin balance_before = track.getBalance(receiver.getAddress()); - executeATransactionAndAssert(getABlockWithATx(track, config, null, 1, receiver.getAddress(), sender), txIndex, false, false, false); - Assertions.assertEquals(balance_before.add(Coin.valueOf(1)), track.getBalance(receiver.getAddress()));; + executeATransactionAndAssert(getABlockWithATx(track, config, null, 1, receiver.getAddress(), sender), txIndex, Collections.emptySet(), false, false); + Assertions.assertEquals(balance_before.add(Coin.valueOf(1)), track.getBalance(receiver.getAddress())); } @Test @@ -121,8 +121,8 @@ void whenATxCallsANonPrecompiledContractPrecompiledContractHasBeenCalledFlagShou txs.add(tx2); Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, Collections.emptySet(), false, false); } @Test @@ -142,8 +142,8 @@ void whenATxCallsANonPrecompiledContractAndRevertsPrecompiledContractHasBeenCall txs.add(tx2); Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, false, true, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, Collections.emptySet(), true, false); } @Test @@ -163,8 +163,8 @@ void whenATxCallsAContractThatCallsAPrecompiledContractPrecompiledContractHasBee txs.add(tx2); Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, true, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, Collections.singleton(PrecompiledContracts.IDENTITY_ADDR), false, false); } @Test @@ -183,9 +183,9 @@ void whenATxCallsAContractThatCallsAPrecompiledContractAndThrowsExceptionPrecomp txs.add(tx1); Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); // As the Precompiled throws an exception, the contract hits the require, and thus reverts. - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, true, true, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, Collections.singleton(PrecompiledContracts.BRIDGE_ADDR), true, false); } @Test @@ -211,9 +211,9 @@ void whenAnInternalTxCallsANonPrecompiledContractPrecompiledContractHasBeenCalle txs.add(tx2); Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, false, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, Collections.emptySet(), false, false); } @Test @@ -239,9 +239,9 @@ void whenAnInternalTxCallsANonPrecompiledContractAndRevertsPrecompiledContractHa txs.add(tx2); Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, false, true, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, Collections.emptySet(), true, false); } @Test @@ -268,9 +268,9 @@ void whenAnInternalTxCallsAPrecompiledContractPrecompiledContractHasBeenCalledFl Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, true, false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, Collections.singleton(PrecompiledContracts.IDENTITY_ADDR), false, false); } @Test @@ -296,9 +296,9 @@ void whenAnInternalTxCallsAPrecompiledContractAndThrowsAnExceptionPrecompiledCon txs.add(tx2); Block aBlockWithATxToAPrecompiledContract = new BlockGenerator(Constants.regtest(), config.getActivationConfig()).createChildBlock(getGenesisBlock(config, track), txs, uncles, difficulty, minGasPrice); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, false, false, false); - executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, true, true, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex++, Collections.emptySet(), false, false); + executeATransactionAndAssert(aBlockWithATxToAPrecompiledContract, txIndex, Collections.singleton(PrecompiledContracts.BRIDGE_ADDR), true, false); } private static TransactionExecutorFactory getTransactionExecutorFactory(TestSystemProperties config) { @@ -318,12 +318,12 @@ private static TransactionExecutorFactory getTransactionExecutorFactory(TestSyst ); } - private void executeATransactionAndAssert(Block aBlock, int transactionIndex, boolean precompiledHasBeenCalled, boolean hasRevert, boolean threwAnException) { + private void executeATransactionAndAssert(Block aBlock, int transactionIndex, Set precompiledContractsCalled, boolean hasRevert, boolean threwAnException) { TransactionExecutor transactionExecutor = transactionExecutorFactory.newInstance(aBlock.getTransactionsList().get(transactionIndex), transactionIndex, aBlock.getCoinbase(), track, aBlock, 0L); boolean txExecuted = transactionExecutor.executeTransaction(); - assertExecutionFlagAndIfRevertedOrThrewAnException(transactionExecutor, txExecuted, precompiledHasBeenCalled, hasRevert, threwAnException); + assertExecutionFlagAndIfRevertedOrThrewAnException(transactionExecutor, txExecuted, precompiledContractsCalled, hasRevert, threwAnException); } private static BigInteger incrementNonce(BigInteger nonce) { @@ -341,10 +341,10 @@ public RskAddress getContractAddress(Account aSender, BigInteger nonce) { return new RskAddress(HashUtil.calcNewAddr(aSender.getAddress().getBytes(), nonce.toByteArray())); } - private static void assertExecutionFlagAndIfRevertedOrThrewAnException(TransactionExecutor txExecutor, boolean txExecuted, boolean precompiledHasBeenCalled, boolean hasRevert, boolean threwAnException) { + private static void assertExecutionFlagAndIfRevertedOrThrewAnException(TransactionExecutor txExecutor, boolean txExecuted, Set precompiledContractsCalled, boolean hasRevert, boolean threwAnException) { Assertions.assertTrue(txExecuted); ProgramResult transactionResult = txExecutor.getResult(); - Assertions.assertEquals(precompiledHasBeenCalled, txExecutor.precompiledContractHasBeenCalled()); + Assertions.assertEquals(precompiledContractsCalled, txExecutor.precompiledContractsCalled()); Exception exception = transactionResult.getException(); if (threwAnException) { Assertions.assertNotNull(exception); diff --git a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java index b751979b831..bcc19ae0620 100644 --- a/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java +++ b/rskj-core/src/test/java/co/rsk/core/parallel/ParallelExecutionStateTest.java @@ -37,7 +37,7 @@ import javax.annotation.Nullable; import java.math.BigInteger; -import java.util.List; +import java.util.*; import static co.rsk.core.bc.transactionexecutor.PrecompiledContractHasBeenCalledTest.PROXY_SMART_CONTRACT_BYTECODE; import static co.rsk.core.bc.transactionexecutor.PrecompiledContractHasBeenCalledTest.PROXY_TO_PROXY_SMART_CONTRACT_BYTECODE; @@ -51,13 +51,13 @@ private World createWorld(int rskip144) { return createWorld(rskip144, null); } - private World createWorld(int rskip144, @Nullable Boolean concurrentPrecompiledContractsEnabled) { + private World createWorld(int rskip144, @Nullable Set concurrentContractsDisallowed) { ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); TestSystemProperties config = new TestSystemProperties(rawConfig -> rawConfig.withValue("blockchain.config.consensusRules.rskip144", ConfigValueFactory.fromAnyRef(rskip144)) ); - if (concurrentPrecompiledContractsEnabled != null) { - config.setConcurrentPrecompiledContractsEnabled(concurrentPrecompiledContractsEnabled); + if (concurrentContractsDisallowed != null) { + config.setConcurrentContractsDisallowed(concurrentContractsDisallowed); } return new World(receiptStore, config); @@ -67,8 +67,8 @@ private World createWorldAndProcess(String dsl, int rskip144ActivationHeight) th return createWorldAndProcess(dsl, rskip144ActivationHeight, null); } - private World createWorldAndProcess(String dsl, int rskip144ActivationHeight, @Nullable Boolean concurrentPrecompiledContractsEnabled) throws DslProcessorException { - World world = createWorld(rskip144ActivationHeight, concurrentPrecompiledContractsEnabled); + private World createWorldAndProcess(String dsl, int rskip144ActivationHeight, @Nullable Set concurrentContractsDisallowed) throws DslProcessorException { + World world = createWorld(rskip144ActivationHeight, concurrentContractsDisallowed); DslParser parser = new DslParser(dsl); WorldDslProcessor processor = new WorldDslProcessor(world); @@ -864,7 +864,7 @@ void whenATxCallsAPrecompiledItShouldGoToSequentialIfDisabled() throws DslProces "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0, false); + "\n", 0, Collections.singleton(PrecompiledContracts.IDENTITY_ADDR)); Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); @@ -891,7 +891,7 @@ void whenATxCallsAPrecompiledItShouldGoToParallelIfEnabled() throws DslProcessor "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0, true); + "\n", 0, Collections.emptySet()); Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); @@ -918,7 +918,7 @@ void whenATxSendsValueToAPrecompiledItShouldGoToSequentialIfDisabled() throws Ds "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0, false); + "\n", 0, Collections.singleton(PrecompiledContracts.IDENTITY_ADDR)); Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); @@ -945,7 +945,7 @@ void whenATxSendsValueToAPrecompiledItShouldGoToParallelIfEnabled() throws DslPr "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0, true); + "\n", 0, Collections.emptySet()); Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); @@ -973,7 +973,7 @@ void whenATxCallsAPrecompiledThatThrowsAnExceptionShouldGoToSequentialIfDisabled "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0, false); + "\n", 0, Collections.singleton(PrecompiledContracts.BRIDGE_ADDR)); Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); @@ -1001,7 +1001,7 @@ void whenATxCallsAPrecompiledThatThrowsAnExceptionShouldGoToParallelIfEnabled() "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0, true); + "\n", 0, Collections.emptySet()); Assertions.assertEquals(1, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); @@ -1037,7 +1037,7 @@ void whenATxCallsAContractThatCallsAPrecompiledShouldGoToSequentialIfDisabled() "assert_best b01\n" + "assert_tx_success tx01\n" + "assert_tx_success tx02\n" + - "\n", 0, false); + "\n", 0, Collections.singleton(PrecompiledContracts.IDENTITY_ADDR)); Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{1}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); @@ -1073,7 +1073,7 @@ void whenATxCallsAContractThatCallsAPrecompiledShouldGoToParallelIfEnabled() thr "assert_best b01\n" + "assert_tx_success tx01\n" + "assert_tx_success tx02\n" + - "\n", 0, true); + "\n", 0, Collections.emptySet()); Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(new short[]{2}, parallel.getBlockChain().getBestBlock().getHeader().getTxExecutionSublistsEdges()); @@ -1108,7 +1108,7 @@ void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequentialIfD "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0, false); + "\n", 0, Collections.singleton(PrecompiledContracts.BRIDGE_ADDR)); Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(FAILED_STATUS, parallel.getTransactionReceiptByName("tx02").getStatus()); @@ -1144,7 +1144,7 @@ void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToParallelIfEna "\n" + "assert_best b01\n" + "assert_tx_success tx01\n" + - "\n", 0, true); + "\n", 0, Collections.emptySet()); Assertions.assertEquals(2, parallel.getBlockChain().getBestBlock().getTransactionsList().size()); Assertions.assertArrayEquals(FAILED_STATUS, parallel.getTransactionReceiptByName("tx02").getStatus()); @@ -1153,7 +1153,7 @@ void whenATxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToParallelIfEna @Test void whenAnInternalTxCallsAContractThatCallsAPrecompiledShouldGoToSequentialIfDisabled() throws DslProcessorException { - World parallel = createWorld(0, false); + World parallel = createWorld(0, Collections.singleton(PrecompiledContracts.IDENTITY_ADDR)); WorldDslProcessor processor = new WorldDslProcessor(parallel); String dsl = deployProxyTo(PrecompiledContracts.IDENTITY_ADDR); DslParser parser = new DslParser(dsl); @@ -1178,7 +1178,7 @@ void whenAnInternalTxCallsAContractThatCallsAPrecompiledShouldGoToSequentialIfDi @Test void whenAnInternalTxCallsAContractThatCallsAPrecompiledShouldGoToParallelIfEnabled() throws DslProcessorException { - World parallel = createWorld(0, true); + World parallel = createWorld(0, Collections.emptySet()); WorldDslProcessor processor = new WorldDslProcessor(parallel); String dsl = deployProxyTo(PrecompiledContracts.IDENTITY_ADDR); DslParser parser = new DslParser(dsl); @@ -1203,7 +1203,7 @@ void whenAnInternalTxCallsAContractThatCallsAPrecompiledShouldGoToParallelIfEnab @Test void whenAnInternalTxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequentialIfDisabled() throws DslProcessorException { - World parallel = createWorld(0, false); + World parallel = createWorld(0, Collections.singleton(PrecompiledContracts.BRIDGE_ADDR)); WorldDslProcessor processor = new WorldDslProcessor(parallel); String dsl = deployProxyTo(PrecompiledContracts.BRIDGE_ADDR); DslParser parser = new DslParser(dsl); @@ -1229,7 +1229,7 @@ void whenAnInternalTxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToSequ @Test void whenAnInternalTxCallsAContractThatCallsAPrecompiledAndRevertsShouldGoToParallelIfEnabled() throws DslProcessorException { - World parallel = createWorld(0, true); + World parallel = createWorld(0, Collections.emptySet()); WorldDslProcessor processor = new WorldDslProcessor(parallel); String dsl = deployProxyTo(PrecompiledContracts.BRIDGE_ADDR); DslParser parser = new DslParser(dsl); diff --git a/rskj-core/src/test/java/org/ethereum/vm/VMTest.java b/rskj-core/src/test/java/org/ethereum/vm/VMTest.java index f570dff7aeb..23113c046e8 100644 --- a/rskj-core/src/test/java/org/ethereum/vm/VMTest.java +++ b/rskj-core/src/test/java/org/ethereum/vm/VMTest.java @@ -3369,7 +3369,7 @@ void testScriptVersion3() { @Test void whenProgramIsInitializedPrecompiledCalledShouldBeFalse() { Program program = getProgram(new byte[]{}); - Assertions.assertFalse(program.precompiledContractHasBeenCalled()); + Assertions.assertTrue(program.precompiledContractsCalled().isEmpty()); } @Test @@ -3386,7 +3386,7 @@ void ifATxCallsAPrecompiledContractPrecompiledContractHasBeenCalledShouldBeTrue( " CALL" )); vm.steps(program, Long.MAX_VALUE); - Assertions.assertTrue(program.precompiledContractHasBeenCalled()); + Assertions.assertFalse(program.precompiledContractsCalled().isEmpty()); Assertions.assertFalse(program.getResult().isRevert()); } @@ -3406,7 +3406,7 @@ void ifATxCallsANonPrecompiledContractPrecompiledContractHasBeenCalledShouldBeFa " CALL" )); vm.steps(program, Long.MAX_VALUE); - Assertions.assertFalse(program.precompiledContractHasBeenCalled()); + Assertions.assertTrue(program.precompiledContractsCalled().isEmpty()); Assertions.assertFalse(program.getResult().isRevert()); } From 306f80e822e6f71b658febea5d78aa02cad43822 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 15 Sep 2023 16:25:50 +0300 Subject: [PATCH 48/89] Implemented reusable thread pools; Added flag to force tx list executor stop --- .../co/rsk/core/TransactionListExecutor.java | 15 +- .../java/co/rsk/core/bc/BlockExecutor.java | 143 +++++++++++++++--- 2 files changed, 134 insertions(+), 24 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java index 9347f9fb170..208da85832d 100644 --- a/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/TransactionListExecutor.java @@ -38,6 +38,8 @@ public class TransactionListExecutor implements Callable { private final Set concurrentContractsDisallowed; private Coin totalPaidFees; + private volatile boolean stopped; + public TransactionListExecutor( List transactions, Block block, @@ -82,8 +84,11 @@ public TransactionListExecutor( @Override public Boolean call() { - long totalGasUsed = 0; + if (stopped) { + return false; + } + long totalGasUsed = 0; for (Transaction tx : transactions) { int numberOfTransactions = block.getTransactionsList().size(); @@ -104,6 +109,10 @@ public Boolean call() { true, sublistGasLimit); boolean transactionSucceeded = txExecutor.executeTransaction(); + if (stopped) { + return false; + } + if (!this.concurrentContractsDisallowed.isEmpty() && txExecutor.precompiledContractsCalled().stream().anyMatch(this.concurrentContractsDisallowed::contains)) { transactionSucceeded = false; } @@ -234,4 +243,8 @@ public Coin getTotalFees() { public long getTotalGas() { return this.totalGas; } + + public void stop() { + this.stopped = true; + } } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 3536c68a6dd..7632c23270a 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -19,7 +19,10 @@ package co.rsk.core.bc; import co.rsk.config.RskSystemProperties; -import co.rsk.core.*; +import co.rsk.core.Coin; +import co.rsk.core.RskAddress; +import co.rsk.core.TransactionExecutorFactory; +import co.rsk.core.TransactionListExecutor; import co.rsk.crypto.Keccak256; import co.rsk.db.RepositoryLocator; import co.rsk.metrics.profilers.Metric; @@ -39,9 +42,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP126; import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIP85; @@ -67,6 +72,29 @@ public class BlockExecutor { private final Map transactionResults = new ConcurrentHashMap<>(); private boolean registerProgramResults; + private final ExecutionServiceFactory defaultFactory = new ExecutionServiceFactory() { + private static final long KEEP_ALIVE_TIME_IN_SECS = 15*60L; /* 15 minutes */ + @Nonnull + @Override + protected ExecutorService createExecutionService() { + ThreadPoolExecutorImpl executorService = new ThreadPoolExecutorImpl(KEEP_ALIVE_TIME_IN_SECS, "DefaultBlockExecutorWorker"); + logger.info("Created 'Default' ExecutorService with max pool size: [{}]", executorService.getMaximumPoolSize()); + + return executorService; + } + }; + private final ExecutionServiceFactory traceBlockFactory = new ExecutionServiceFactory() { + private static final long KEEP_ALIVE_TIME_IN_SECS = 60L; /* 1 minute */ + @Nonnull + @Override + protected ExecutorService createExecutionService() { + ThreadPoolExecutorImpl executorService = new ThreadPoolExecutorImpl(KEEP_ALIVE_TIME_IN_SECS, "TraceBlockExecutorWorker"); + logger.info("Created 'Trace' ExecutorService with max pool size: [{}]", executorService.getMaximumPoolSize()); + + return executorService; + } + }; + public BlockExecutor( RepositoryLocator repositoryLocator, TransactionExecutorFactory transactionExecutorFactory, @@ -272,16 +300,14 @@ public BlockResult executeForMining(Block block, BlockHeader parent, boolean dis /** * Execute a block while saving the execution trace in the trace processor */ - public void traceBlock( - ProgramTraceProcessor programTraceProcessor, - int vmTraceOptions, - Block block, - BlockHeader parent, - boolean discardInvalidTxs, - boolean ignoreReadyToExecute) { - execute( - Objects.requireNonNull(programTraceProcessor), vmTraceOptions, block, parent, discardInvalidTxs, ignoreReadyToExecute, false - ); + public void traceBlock(ProgramTraceProcessor programTraceProcessor, + int vmTraceOptions, + Block block, + BlockHeader parent, + boolean discardInvalidTxs, + boolean ignoreReadyToExecute) { + execute(Objects.requireNonNull(programTraceProcessor), vmTraceOptions, block, parent, discardInvalidTxs, + ignoreReadyToExecute, false, traceBlockFactory); } public BlockResult execute( @@ -291,13 +317,23 @@ public BlockResult execute( BlockHeader parent, boolean discardInvalidTxs, boolean acceptInvalidTransactions, - boolean saveState - ) { - if (activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber())) { - return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); - } else { - return executeInternal(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); - } + boolean saveState) { + return execute(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState, defaultFactory); + } + + public BlockResult execute(@Nullable ProgramTraceProcessor programTraceProcessor, + int vmTraceOptions, + Block block, + BlockHeader parent, + boolean discardInvalidTxs, + boolean acceptInvalidTransactions, + boolean saveState, + @Nonnull ExecutionServiceFactory factory) { + if (activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber())) { + return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState, factory); + } else { + return executeInternal(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); + } } // executes the block before RSKIP 144 @@ -410,7 +446,8 @@ private BlockResult executeParallel( BlockHeader parent, boolean discardInvalidTxs, boolean acceptInvalidTransactions, - boolean saveState) { + boolean saveState, + @Nonnull ExecutionServiceFactory factory) { boolean vmTrace = programTraceProcessor != null; logger.trace("Start executeParallel."); loggingApplyBlock(block); @@ -434,8 +471,7 @@ private BlockResult executeParallel( maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); readWrittenKeysTracker.clear(); - ExecutorService executorService = Executors.newFixedThreadPool(Constants.getTransactionExecutionThreads()); - ExecutorCompletionService completionService = new ExecutorCompletionService<>(executorService); + ExecutorCompletionService completionService = new ExecutorCompletionService<>(factory.getOrCreateExecutionService()); List transactionListExecutors = new ArrayList<>(); long sublistGasLimit = getSublistGasLimit(block); @@ -468,13 +504,12 @@ private BlockResult executeParallel( transactionListExecutors.add(txListExecutor); start = end; } - executorService.shutdown(); for (int i = 0; i < transactionListExecutors.size(); i++) { try { Future success = completionService.take(); if (!Boolean.TRUE.equals(success.get())) { - executorService.shutdownNow(); + transactionListExecutors.forEach(TransactionListExecutor::stop); profiler.stop(metric); return BlockResult.INTERRUPTED_EXECUTION_BLOCK_RESULT; } @@ -824,4 +859,66 @@ public void setRegisterProgramResults(boolean value) { this.registerProgramResults = value; this.transactionResults.clear(); } + + private abstract static class ExecutionServiceFactory { + + private volatile ExecutorService executorService; + + @Nonnull + protected abstract ExecutorService createExecutionService(); + + ExecutorService getOrCreateExecutionService() { // Double-Checked Locking + if (executorService != null) { + return executorService; + } + synchronized (this) { + if (executorService == null) { + executorService = Objects.requireNonNull(createExecutionService()); + } + } + return executorService; + } + } + + private static final class ThreadPoolExecutorImpl extends ThreadPoolExecutor { + + static final int MAX_POOL_SIZE = Math.min(Constants.getTransactionExecutionThreads(), Runtime.getRuntime().availableProcessors()); + + public ThreadPoolExecutorImpl(long keepAliveTimeInSecs, String threadNamePrefix) { + super(0, MAX_POOL_SIZE, keepAliveTimeInSecs, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), new ThreadFactoryImpl(threadNamePrefix)); + } + + @Override + protected void beforeExecute(Thread t, Runnable r) { + logger.debug("[{}]: before execution", t); + super.beforeExecute(t, r); + } + + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + if (t == null) { + logger.debug("[{}]: after execution", Thread.currentThread()); + } else { + logger.warn("[{}]: failed execution", Thread.currentThread(), t); + } + } + } + + private static final class ThreadFactoryImpl implements ThreadFactory { + private final AtomicInteger cnt = new AtomicInteger(0); + private final String namePrefix; + + ThreadFactoryImpl(String namePrefix) { + this.namePrefix = namePrefix; + } + + @Override + public Thread newThread(@Nonnull Runnable r) { + String threadName = namePrefix + "-" + cnt.getAndIncrement(); + logger.info("New block execution thread '{}' has been created", threadName); + return new Thread(r, threadName); + } + } } From 82ba6836d40b8c5959367d0baa60b1b1cd5e5de4 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Mon, 18 Sep 2023 16:14:53 +0300 Subject: [PATCH 49/89] Reworked thread scheduling for parallel tx execution --- .../java/co/rsk/core/bc/BlockExecutor.java | 112 +++++++----------- 1 file changed, 42 insertions(+), 70 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 7632c23270a..d04f3fc3988 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -72,28 +72,13 @@ public class BlockExecutor { private final Map transactionResults = new ConcurrentHashMap<>(); private boolean registerProgramResults; - private final ExecutionServiceFactory defaultFactory = new ExecutionServiceFactory() { - private static final long KEEP_ALIVE_TIME_IN_SECS = 15*60L; /* 15 minutes */ - @Nonnull - @Override - protected ExecutorService createExecutionService() { - ThreadPoolExecutorImpl executorService = new ThreadPoolExecutorImpl(KEEP_ALIVE_TIME_IN_SECS, "DefaultBlockExecutorWorker"); - logger.info("Created 'Default' ExecutorService with max pool size: [{}]", executorService.getMaximumPoolSize()); - - return executorService; - } - }; - private final ExecutionServiceFactory traceBlockFactory = new ExecutionServiceFactory() { - private static final long KEEP_ALIVE_TIME_IN_SECS = 60L; /* 1 minute */ - @Nonnull - @Override - protected ExecutorService createExecutionService() { - ThreadPoolExecutorImpl executorService = new ThreadPoolExecutorImpl(KEEP_ALIVE_TIME_IN_SECS, "TraceBlockExecutorWorker"); - logger.info("Created 'Trace' ExecutorService with max pool size: [{}]", executorService.getMaximumPoolSize()); - - return executorService; - } - }; + /** + * An array of ExecutorService's of size `Constants.getTransactionExecutionThreads()`. Each parallel list uses an executor + * at specific index of this array, so that threads chosen by thread pools cannot be "reused" for executing parallel + * lists from same block. Otherwise, that could lead to non-determinism, when trie keys collision may not be detected + * on some circumstances. + */ + private final ExecutorService[] execServices; public BlockExecutor( RepositoryLocator repositoryLocator, @@ -104,6 +89,12 @@ public BlockExecutor( this.activationConfig = systemProperties.getActivationConfig(); this.remascEnabled = systemProperties.isRemascEnabled(); this.concurrentContractsDisallowed = Collections.unmodifiableSet(new HashSet<>(systemProperties.concurrentContractsDisallowed())); + + int numOfParallelList = Constants.getTransactionExecutionThreads(); + this.execServices = new ExecutorService[numOfParallelList]; + for (int i = 0; i < numOfParallelList; i++) { + execServices[i] = new ThreadPoolExecutorImpl(i); + } } /** @@ -307,18 +298,7 @@ public void traceBlock(ProgramTraceProcessor programTraceProcessor, boolean discardInvalidTxs, boolean ignoreReadyToExecute) { execute(Objects.requireNonNull(programTraceProcessor), vmTraceOptions, block, parent, discardInvalidTxs, - ignoreReadyToExecute, false, traceBlockFactory); - } - - public BlockResult execute( - @Nullable ProgramTraceProcessor programTraceProcessor, - int vmTraceOptions, - Block block, - BlockHeader parent, - boolean discardInvalidTxs, - boolean acceptInvalidTransactions, - boolean saveState) { - return execute(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState, defaultFactory); + ignoreReadyToExecute, false); } public BlockResult execute(@Nullable ProgramTraceProcessor programTraceProcessor, @@ -327,10 +307,9 @@ public BlockResult execute(@Nullable ProgramTraceProcessor programTraceProcessor BlockHeader parent, boolean discardInvalidTxs, boolean acceptInvalidTransactions, - boolean saveState, - @Nonnull ExecutionServiceFactory factory) { + boolean saveState) { if (activationConfig.isActive(ConsensusRule.RSKIP144, block.getHeader().getNumber())) { - return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState, factory); + return executeParallel(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); } else { return executeInternal(programTraceProcessor, vmTraceOptions, block, parent, discardInvalidTxs, acceptInvalidTransactions, saveState); } @@ -446,8 +425,7 @@ private BlockResult executeParallel( BlockHeader parent, boolean discardInvalidTxs, boolean acceptInvalidTransactions, - boolean saveState, - @Nonnull ExecutionServiceFactory factory) { + boolean saveState) { boolean vmTrace = programTraceProcessor != null; logger.trace("Start executeParallel."); loggingApplyBlock(block); @@ -471,7 +449,14 @@ private BlockResult executeParallel( maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); readWrittenKeysTracker.clear(); - ExecutorCompletionService completionService = new ExecutorCompletionService<>(factory.getOrCreateExecutionService()); + ExecutorCompletionService completionService = new ExecutorCompletionService<>(new Executor() { + private final AtomicInteger parallelListIndex = new AtomicInteger(0); // 'parallelListIndex' should not exceed Constants.getTransactionExecutionThreads() + + @Override + public void execute(@Nonnull Runnable command) { + execServices[parallelListIndex.getAndIncrement()].execute(command); + } + }); List transactionListExecutors = new ArrayList<>(); long sublistGasLimit = getSublistGasLimit(block); @@ -860,33 +845,17 @@ public void setRegisterProgramResults(boolean value) { this.transactionResults.clear(); } - private abstract static class ExecutionServiceFactory { - - private volatile ExecutorService executorService; - - @Nonnull - protected abstract ExecutorService createExecutionService(); - - ExecutorService getOrCreateExecutionService() { // Double-Checked Locking - if (executorService != null) { - return executorService; - } - synchronized (this) { - if (executorService == null) { - executorService = Objects.requireNonNull(createExecutionService()); - } - } - return executorService; - } - } - + /** + * This implementation mimics a thread pool returned by {@link Executors#newCachedThreadPool()}, meaning that + * its core pool size is zero and maximum pool size is unbounded, while each thead created has keep-alive time - 15 mins. + */ private static final class ThreadPoolExecutorImpl extends ThreadPoolExecutor { + private static final long KEEP_ALIVE_TIME_IN_SECS = 15*60L; /* 15 minutes */ - static final int MAX_POOL_SIZE = Math.min(Constants.getTransactionExecutionThreads(), Runtime.getRuntime().availableProcessors()); - - public ThreadPoolExecutorImpl(long keepAliveTimeInSecs, String threadNamePrefix) { - super(0, MAX_POOL_SIZE, keepAliveTimeInSecs, TimeUnit.SECONDS, - new LinkedBlockingQueue<>(), new ThreadFactoryImpl(threadNamePrefix)); + public ThreadPoolExecutorImpl(int parallelListIndex) { + super(0, Integer.MAX_VALUE, + KEEP_ALIVE_TIME_IN_SECS, TimeUnit.SECONDS, + new SynchronousQueue<>(), new ThreadFactoryImpl(parallelListIndex)); } @Override @@ -906,18 +875,21 @@ protected void afterExecute(Runnable r, Throwable t) { } } + /** + * A utility class that helps to specify a proper thread name in the form of `BlockExecutorWorker--`. + */ private static final class ThreadFactoryImpl implements ThreadFactory { private final AtomicInteger cnt = new AtomicInteger(0); - private final String namePrefix; + private final int parallelListIndex; - ThreadFactoryImpl(String namePrefix) { - this.namePrefix = namePrefix; + ThreadFactoryImpl(int parallelListIndex) { + this.parallelListIndex = parallelListIndex; } @Override public Thread newThread(@Nonnull Runnable r) { - String threadName = namePrefix + "-" + cnt.getAndIncrement(); - logger.info("New block execution thread '{}' has been created", threadName); + String threadName = "BlockExecutorWorker-" + parallelListIndex + "-" + cnt.getAndIncrement(); + logger.info("New block execution thread [{}] for parallel list [{}] has been created", threadName, parallelListIndex); return new Thread(r, threadName); } } From 228a26647a2f9fe41506acd98407ac2bcf3f1b61 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Tue, 19 Sep 2023 15:00:09 +0300 Subject: [PATCH 50/89] Used in-line tx execution if num of edges is less than 2 --- .../src/main/java/co/rsk/core/bc/BlockExecutor.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index d04f3fc3988..5276ac41696 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -449,20 +449,24 @@ private BlockResult executeParallel( maintainPrecompiledContractStorageRoots(track, activationConfig.forBlock(block.getNumber())); readWrittenKeysTracker.clear(); - ExecutorCompletionService completionService = new ExecutorCompletionService<>(new Executor() { + short[] txExecutionEdges = block.getHeader().getTxExecutionSublistsEdges(); + + // if the number of parallel lists is less than 2, then there's no need to execute in another thread. The work can + // be done in the same thread (in-line) without any threads switching. + ExecutorCompletionService completionService = new ExecutorCompletionService<>(txExecutionEdges.length > 1 ? new Executor() { private final AtomicInteger parallelListIndex = new AtomicInteger(0); // 'parallelListIndex' should not exceed Constants.getTransactionExecutionThreads() @Override public void execute(@Nonnull Runnable command) { execServices[parallelListIndex.getAndIncrement()].execute(command); } - }); + } : Runnable::run); List transactionListExecutors = new ArrayList<>(); long sublistGasLimit = getSublistGasLimit(block); short start = 0; - for (short end : block.getHeader().getTxExecutionSublistsEdges()) { + for (short end : txExecutionEdges) { List sublist = block.getTransactionsList().subList(start, end); TransactionListExecutor txListExecutor = new TransactionListExecutor( sublist, @@ -574,7 +578,7 @@ public void execute(@Nonnull Runnable command) { block, new LinkedList<>(executedTransactions.values()), new LinkedList<>(receipts.values()), - block.getHeader().getTxExecutionSublistsEdges(), + txExecutionEdges, totalGasUsed, totalBlockPaidFees, vmTrace ? null : track.getTrie() From 027ee5b100ef33c39190c1baeb7e1f9b67e60198 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Thu, 19 Oct 2023 12:20:22 +0300 Subject: [PATCH 51/89] Added tx gas limit validation before adding to mempool --- .../java/co/rsk/core/bc/BlockExecutor.java | 8 +- .../main/java/co/rsk/core/bc/BlockUtils.java | 5 + .../rsk/net/handler/TxPendingValidator.java | 13 +- .../java/co/rsk/core/bc/BlockUtilsTest.java | 105 ++++++----- .../net/handler/TxPendingValidatorTest.java | 172 ++++++++++++++++++ 5 files changed, 247 insertions(+), 56 deletions(-) create mode 100644 rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 5276ac41696..b02fbb4d993 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -462,7 +462,7 @@ public void execute(@Nonnull Runnable command) { } } : Runnable::run); List transactionListExecutors = new ArrayList<>(); - long sublistGasLimit = getSublistGasLimit(block); + long sublistGasLimit = BlockUtils.getSublistGasLimit(block); short start = 0; @@ -626,7 +626,7 @@ private BlockResult executeForMiningAfterRSKIP144( int txindex = 0; int transactionExecutionThreads = Constants.getTransactionExecutionThreads(); - long sublistGasLimit = getSublistGasLimit(block); + long sublistGasLimit = BlockUtils.getSublistGasLimit(block); ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) transactionExecutionThreads, sublistGasLimit); for (Transaction tx : transactionsList) { @@ -726,10 +726,6 @@ private BlockResult executeForMiningAfterRSKIP144( return result; } - private static long getSublistGasLimit(Block block) { - return GasCost.toGas(block.getGasLimit()) / (Constants.getTransactionExecutionThreads()+1); - } - private void registerExecutedTx(ProgramTraceProcessor programTraceProcessor, boolean vmTrace, List executedTransactions, Transaction tx, TransactionExecutor txExecutor) { executedTransactions.add(tx); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java index afe66fd0a4e..98ac63c1a60 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java @@ -20,10 +20,12 @@ import co.rsk.crypto.Keccak256; import co.rsk.net.NetBlockStore; +import org.ethereum.config.Constants; import org.ethereum.core.Block; import org.ethereum.core.BlockHeader; import org.ethereum.core.Blockchain; import org.ethereum.db.BlockInformation; +import org.ethereum.vm.GasCost; import java.util.*; import java.util.stream.Collectors; @@ -116,4 +118,7 @@ public static List sortBlocksByNumber(List blocks) { .collect(Collectors.toList()); } + public static long getSublistGasLimit(Block block) { + return GasCost.toGas(block.getGasLimit()) / (Constants.getTransactionExecutionThreads() + 1); + } } diff --git a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java index 74ab6e38636..f6148a8a7d8 100644 --- a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java +++ b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java @@ -18,11 +18,13 @@ package co.rsk.net.handler; import co.rsk.core.Coin; +import co.rsk.core.bc.BlockUtils; import co.rsk.net.TransactionValidationResult; import co.rsk.net.handler.txvalidator.*; import org.bouncycastle.util.BigIntegers; import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.AccountState; import org.ethereum.core.Block; import org.ethereum.core.SignatureCache; @@ -69,10 +71,13 @@ public TxPendingValidator(Constants constants, ActivationConfig activationConfig } public TransactionValidationResult isValid(Transaction tx, Block executionBlock, @Nullable AccountState state) { - BigInteger blockGasLimit = BigIntegers.fromUnsignedByteArray(executionBlock.getGasLimit()); + long executionBlockNumber = executionBlock.getNumber(); + ActivationConfig.ForBlock activations = activationConfig.forBlock(executionBlockNumber); + BigInteger gasLimit = activations.isActive(ConsensusRule.RSKIP144) + ? BigInteger.valueOf(BlockUtils.getSublistGasLimit(executionBlock)) + : BigIntegers.fromUnsignedByteArray(executionBlock.getGasLimit()); Coin minimumGasPrice = executionBlock.getMinimumGasPrice(); - long bestBlockNumber = executionBlock.getNumber(); - long basicTxCost = tx.transactionCost(constants, activationConfig.forBlock(bestBlockNumber), signatureCache); + long basicTxCost = tx.transactionCost(constants, activations, signatureCache); if (state == null && basicTxCost != 0) { if (logger.isTraceEnabled()) { @@ -86,7 +91,7 @@ public TransactionValidationResult isValid(Transaction tx, Block executionBlock, } for (TxValidatorStep step : validatorSteps) { - TransactionValidationResult validationResult = step.validate(tx, state, blockGasLimit, minimumGasPrice, bestBlockNumber, basicTxCost == 0); + TransactionValidationResult validationResult = step.validate(tx, state, gasLimit, minimumGasPrice, executionBlockNumber, basicTxCost == 0); if (!validationResult.transactionIsValid()) { logger.info("[tx={}] validation failed with error: {}", tx.getHash(), validationResult.getErrorMessage()); return validationResult; diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java index fef07ce4437..4e3af633788 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java @@ -23,8 +23,8 @@ import co.rsk.net.NetBlockStore; import co.rsk.test.builders.BlockBuilder; import co.rsk.test.builders.BlockChainBuilder; +import org.ethereum.config.Constants; import org.ethereum.core.*; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.math.BigInteger; @@ -32,6 +32,10 @@ import java.util.List; import java.util.Set; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + /** * Created by ajlopez on 19/08/2016. */ @@ -53,11 +57,11 @@ void blockInSomeBlockChain() { blockChain.tryToConnect(block1); blockChain.tryToConnect(block1b); - Assertions.assertTrue(BlockUtils.blockInSomeBlockChain(genesis, blockChain)); - Assertions.assertTrue(BlockUtils.blockInSomeBlockChain(block1, blockChain)); - Assertions.assertTrue(BlockUtils.blockInSomeBlockChain(block1b, blockChain)); - Assertions.assertFalse(BlockUtils.blockInSomeBlockChain(block2, blockChain)); - Assertions.assertTrue(BlockUtils.blockInSomeBlockChain(block3, blockChain)); + assertTrue(BlockUtils.blockInSomeBlockChain(genesis, blockChain)); + assertTrue(BlockUtils.blockInSomeBlockChain(block1, blockChain)); + assertTrue(BlockUtils.blockInSomeBlockChain(block1b, blockChain)); + assertFalse(BlockUtils.blockInSomeBlockChain(block2, blockChain)); + assertTrue(BlockUtils.blockInSomeBlockChain(block3, blockChain)); } @Test @@ -74,37 +78,37 @@ void unknowAncestorsHashes() { store.saveBlock(block3); - Assertions.assertEquals(ImportResult.IMPORTED_BEST, blockChain.tryToConnect(block1)); - Assertions.assertEquals(ImportResult.IMPORTED_NOT_BEST, blockChain.tryToConnect(block1b)); + assertEquals(ImportResult.IMPORTED_BEST, blockChain.tryToConnect(block1)); + assertEquals(ImportResult.IMPORTED_NOT_BEST, blockChain.tryToConnect(block1b)); Set hashes = BlockUtils.unknownAncestorsHashes(genesis.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); - Assertions.assertTrue(hashes.isEmpty()); + assertNotNull(hashes); + assertTrue(hashes.isEmpty()); hashes = BlockUtils.unknownAncestorsHashes(block1.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); - Assertions.assertTrue(hashes.isEmpty()); + assertNotNull(hashes); + assertTrue(hashes.isEmpty()); hashes = BlockUtils.unknownAncestorsHashes(block1b.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); - Assertions.assertTrue(hashes.isEmpty()); + assertNotNull(hashes); + assertTrue(hashes.isEmpty()); hashes = BlockUtils.unknownAncestorsHashes(block2.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); - Assertions.assertFalse(hashes.isEmpty()); - Assertions.assertEquals(1, hashes.size()); - Assertions.assertTrue(hashes.contains(block2.getHash())); + assertNotNull(hashes); + assertFalse(hashes.isEmpty()); + assertEquals(1, hashes.size()); + assertTrue(hashes.contains(block2.getHash())); hashes = BlockUtils.unknownAncestorsHashes(block3.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); - Assertions.assertFalse(hashes.isEmpty()); - Assertions.assertEquals(1, hashes.size()); - Assertions.assertTrue(hashes.contains(block2.getHash())); + assertNotNull(hashes); + assertFalse(hashes.isEmpty()); + assertEquals(1, hashes.size()); + assertTrue(hashes.contains(block2.getHash())); } @Test @@ -136,46 +140,55 @@ void unknowAncestorsHashesUsingUncles() { Set hashes = BlockUtils.unknownAncestorsHashes(genesis.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); - Assertions.assertTrue(hashes.isEmpty()); + assertNotNull(hashes); + assertTrue(hashes.isEmpty()); hashes = BlockUtils.unknownAncestorsHashes(block1.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); - Assertions.assertTrue(hashes.isEmpty()); + assertNotNull(hashes); + assertTrue(hashes.isEmpty()); hashes = BlockUtils.unknownAncestorsHashes(block1b.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); + assertNotNull(hashes); - Assertions.assertTrue(hashes.isEmpty()); + assertTrue(hashes.isEmpty()); hashes = BlockUtils.unknownAncestorsHashes(block2.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); - Assertions.assertFalse(hashes.isEmpty()); - Assertions.assertEquals(1, hashes.size()); - Assertions.assertTrue(hashes.contains(block2.getHash())); + assertNotNull(hashes); + assertFalse(hashes.isEmpty()); + assertEquals(1, hashes.size()); + assertTrue(hashes.contains(block2.getHash())); hashes = BlockUtils.unknownAncestorsHashes(block3.getHash(), blockChain, store); - Assertions.assertNotNull(hashes); - Assertions.assertFalse(hashes.isEmpty()); - Assertions.assertEquals(3, hashes.size()); - Assertions.assertTrue(hashes.contains(block2.getHash())); - Assertions.assertTrue(hashes.contains(uncle1.getHash())); - Assertions.assertTrue(hashes.contains(uncle2.getHash())); + assertNotNull(hashes); + assertFalse(hashes.isEmpty()); + assertEquals(3, hashes.size()); + assertTrue(hashes.contains(block2.getHash())); + assertTrue(hashes.contains(uncle1.getHash())); + assertTrue(hashes.contains(uncle2.getHash())); } @Test void tooMuchProcessTime() { - Assertions.assertFalse(BlockUtils.tooMuchProcessTime(0)); - Assertions.assertFalse(BlockUtils.tooMuchProcessTime(1000)); - Assertions.assertFalse(BlockUtils.tooMuchProcessTime(1_000_000L)); - Assertions.assertFalse(BlockUtils.tooMuchProcessTime(1_000_000_000L)); - Assertions.assertFalse(BlockUtils.tooMuchProcessTime(60_000_000_000L)); - - Assertions.assertTrue(BlockUtils.tooMuchProcessTime(60_000_000_001L)); - Assertions.assertTrue(BlockUtils.tooMuchProcessTime(1_000_000_000_000L)); + assertFalse(BlockUtils.tooMuchProcessTime(0)); + assertFalse(BlockUtils.tooMuchProcessTime(1000)); + assertFalse(BlockUtils.tooMuchProcessTime(1_000_000L)); + assertFalse(BlockUtils.tooMuchProcessTime(1_000_000_000L)); + assertFalse(BlockUtils.tooMuchProcessTime(60_000_000_000L)); + + assertTrue(BlockUtils.tooMuchProcessTime(60_000_000_001L)); + assertTrue(BlockUtils.tooMuchProcessTime(1_000_000_000_000L)); + } + + @Test + void sublistGasLimit_ShouldReturnCorrectValue() { + Block block = mock(Block.class); + when(block.getGasLimit()).thenReturn(BigInteger.valueOf(10_000_000L).toByteArray()); + + long expectedLimit = 10_000_000L / (Constants.getTransactionExecutionThreads() + 1); + assertEquals(expectedLimit, BlockUtils.getSublistGasLimit(block)); } } diff --git a/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java b/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java new file mode 100644 index 00000000000..05f0a1119af --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java @@ -0,0 +1,172 @@ +/* + * This file is part of RskJ + * Copyright (C) 2023 RSK Labs Ltd. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package co.rsk.net.handler; + +import co.rsk.config.TestSystemProperties; +import co.rsk.core.Coin; +import co.rsk.core.bc.BlockUtils; +import co.rsk.net.TransactionValidationResult; +import org.ethereum.config.blockchain.upgrades.ActivationConfig; +import org.ethereum.config.blockchain.upgrades.ConsensusRule; +import org.ethereum.core.*; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class TxPendingValidatorTest { + + @Test + void isValid_ShouldBeValid_WhenRSKIP144IsNotActivated() { + // before RSKIP144: block gas limit == 10 and tx gas limit == 10 - should be valid + + Block executionBlock = mock(Block.class); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(10L).toByteArray()); + when(executionBlock.getMinimumGasPrice()).thenReturn(Coin.valueOf(1L)); + + Transaction tx = mock(Transaction.class); + when(tx.getGasLimitAsInteger()).thenReturn(BigInteger.valueOf(10L)); + when(tx.getNonce()).thenReturn(BigInteger.valueOf(1L).toByteArray()); + when(tx.getNonceAsInteger()).thenReturn(BigInteger.valueOf(1L)); + when(tx.getGasPrice()).thenReturn(Coin.valueOf(1L)); + + AccountState state = mock(AccountState.class); + when(state.getNonce()).thenReturn(BigInteger.valueOf(1L)); + + TestSystemProperties config = new TestSystemProperties(); + + ActivationConfig.ForBlock forBlock = mock(ActivationConfig.ForBlock.class); + when(forBlock.isActive(ConsensusRule.RSKIP144)).thenReturn(false); + + ActivationConfig activationConfig = spy(config.getActivationConfig()); + when(activationConfig.forBlock(anyLong())).thenReturn(forBlock); + + SignatureCache signatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); + TxPendingValidator validator = new TxPendingValidator(config.getNetworkConstants(), activationConfig, config.getNumOfAccountSlots(), signatureCache); + + TransactionValidationResult result = validator.isValid(tx, executionBlock, state); + + assertTrue(result.transactionIsValid()); + } + + @Test + void isValid_ShouldBeInvalid_WhenRSKIP144IsNotActivated() { + // before RSKIP144: block gas limit == 10 and tx gas limit == 11 - should be invalid + + Block executionBlock = mock(Block.class); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(10L).toByteArray()); + when(executionBlock.getMinimumGasPrice()).thenReturn(Coin.valueOf(1L)); + + Transaction tx = mock(Transaction.class); + when(tx.getGasLimitAsInteger()).thenReturn(BigInteger.valueOf(11L)); + when(tx.getNonce()).thenReturn(BigInteger.valueOf(1L).toByteArray()); + when(tx.getNonceAsInteger()).thenReturn(BigInteger.valueOf(1L)); + when(tx.getGasPrice()).thenReturn(Coin.valueOf(1L)); + + AccountState state = mock(AccountState.class); + when(state.getNonce()).thenReturn(BigInteger.valueOf(1L)); + + TestSystemProperties config = new TestSystemProperties(); + + ActivationConfig.ForBlock forBlock = mock(ActivationConfig.ForBlock.class); + when(forBlock.isActive(ConsensusRule.RSKIP144)).thenReturn(false); + + ActivationConfig activationConfig = spy(config.getActivationConfig()); + when(activationConfig.forBlock(anyLong())).thenReturn(forBlock); + + SignatureCache signatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); + TxPendingValidator validator = new TxPendingValidator(config.getNetworkConstants(), activationConfig, config.getNumOfAccountSlots(), signatureCache); + + TransactionValidationResult result = validator.isValid(tx, executionBlock, state); + + assertFalse(result.transactionIsValid()); + } + + @Test + void isValid_ShouldBeValid_WhenRSKIP144IsAlreadyActivated() { + // after RSKIP144: block gas limit == 10 and tx gas limit == BlockUtils.getSublistGasLimit(executionBlock) - should be valid + + Block executionBlock = mock(Block.class); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(10L).toByteArray()); + when(executionBlock.getMinimumGasPrice()).thenReturn(Coin.valueOf(1L)); + + long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock); + + Transaction tx = mock(Transaction.class); + when(tx.getGasLimitAsInteger()).thenReturn(BigInteger.valueOf(sublistGasLimit)); + when(tx.getNonce()).thenReturn(BigInteger.valueOf(1L).toByteArray()); + when(tx.getNonceAsInteger()).thenReturn(BigInteger.valueOf(1L)); + when(tx.getGasPrice()).thenReturn(Coin.valueOf(1L)); + + AccountState state = mock(AccountState.class); + when(state.getNonce()).thenReturn(BigInteger.valueOf(1L)); + + TestSystemProperties config = new TestSystemProperties(); + + ActivationConfig.ForBlock forBlock = mock(ActivationConfig.ForBlock.class); + when(forBlock.isActive(ConsensusRule.RSKIP144)).thenReturn(true); + + ActivationConfig activationConfig = spy(config.getActivationConfig()); + when(activationConfig.forBlock(anyLong())).thenReturn(forBlock); + + SignatureCache signatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); + TxPendingValidator validator = new TxPendingValidator(config.getNetworkConstants(), activationConfig, config.getNumOfAccountSlots(), signatureCache); + + TransactionValidationResult result = validator.isValid(tx, executionBlock, state); + + assertTrue(result.transactionIsValid()); + } + + @Test + void isValid_ShouldBeInvalid_WhenRSKIP144IsAlreadyActivated() { + // after RSKIP144: block gas limit == 10 and tx gas limit == BlockUtils.getSublistGasLimit(executionBlock) + 1 - should be invalid + + Block executionBlock = mock(Block.class); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(10L).toByteArray()); + when(executionBlock.getMinimumGasPrice()).thenReturn(Coin.valueOf(1L)); + + long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock); + + Transaction tx = mock(Transaction.class); + when(tx.getGasLimitAsInteger()).thenReturn(BigInteger.valueOf(sublistGasLimit + 1L)); + when(tx.getNonce()).thenReturn(BigInteger.valueOf(1L).toByteArray()); + when(tx.getNonceAsInteger()).thenReturn(BigInteger.valueOf(1L)); + when(tx.getGasPrice()).thenReturn(Coin.valueOf(1L)); + + AccountState state = mock(AccountState.class); + when(state.getNonce()).thenReturn(BigInteger.valueOf(1L)); + + TestSystemProperties config = new TestSystemProperties(); + + ActivationConfig.ForBlock forBlock = mock(ActivationConfig.ForBlock.class); + when(forBlock.isActive(ConsensusRule.RSKIP144)).thenReturn(true); + + ActivationConfig activationConfig = spy(config.getActivationConfig()); + when(activationConfig.forBlock(anyLong())).thenReturn(forBlock); + + SignatureCache signatureCache = new BlockTxSignatureCache(new ReceivedTxSignatureCache()); + TxPendingValidator validator = new TxPendingValidator(config.getNetworkConstants(), activationConfig, config.getNumOfAccountSlots(), signatureCache); + + TransactionValidationResult result = validator.isValid(tx, executionBlock, state); + + assertFalse(result.transactionIsValid()); + } +} \ No newline at end of file From 33590c671394ed69d2be8a16ba6d921ac2e75964 Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Fri, 2 Feb 2024 08:33:58 -0400 Subject: [PATCH 52/89] Refactor PTE gas distribution logic --- .../java/co/rsk/core/bc/BlockExecutor.java | 10 ++- .../main/java/co/rsk/core/bc/BlockUtils.java | 17 ++++- .../bc/ParallelizeTransactionHandler.java | 7 +- .../rsk/net/handler/TxPendingValidator.java | 2 +- .../java/co/rsk/core/bc/BlockUtilsTest.java | 4 +- .../bc/ParallelizeTransactionHandlerTest.java | 68 ++++++++++++------- .../net/handler/TxPendingValidatorTest.java | 4 +- 7 files changed, 70 insertions(+), 42 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 44476182c0b..25b6db992e6 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -461,7 +461,6 @@ public void execute(@Nonnull Runnable command) { } } : Runnable::run); List transactionListExecutors = new ArrayList<>(); - long sublistGasLimit = BlockUtils.getSublistGasLimit(block); short start = 0; @@ -486,7 +485,7 @@ public void execute(@Nonnull Runnable command) { Coin.ZERO, remascEnabled, concurrentContractsDisallowed, - sublistGasLimit + BlockUtils.getSublistGasLimit(block, false) ); completionService.submit(txListExecutor); transactionListExecutors.add(txListExecutor); @@ -561,7 +560,7 @@ public void execute(@Nonnull Runnable command) { totalPaidFees, remascEnabled, Collections.emptySet(), // precompiled contracts are always allowed in a sequential list, as there's no concurrency in it - sublistGasLimit + BlockUtils.getSublistGasLimit(block, true) ); Boolean success = txListExecutor.call(); if (!Boolean.TRUE.equals(success)) { @@ -625,8 +624,7 @@ private BlockResult executeForMiningAfterRSKIP144( int txindex = 0; int transactionExecutionThreads = Constants.getTransactionExecutionThreads(); - long sublistGasLimit = BlockUtils.getSublistGasLimit(block); - ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) transactionExecutionThreads, sublistGasLimit); + ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) transactionExecutionThreads, block); for (Transaction tx : transactionsList) { loggingApplyBlockToTx(block, i); @@ -642,7 +640,7 @@ private BlockResult executeForMiningAfterRSKIP144( 0, deletedAccounts, true, - sublistGasLimit + BlockUtils.getSublistGasLimit(block, false) ); boolean transactionExecuted = txExecutor.executeTransaction(); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java index 98ac63c1a60..33eef57cf54 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java @@ -35,6 +35,7 @@ */ public class BlockUtils { private static final long MAX_BLOCK_PROCESS_TIME_NANOSECONDS = 60_000_000_000L; + public static final long SEQUENTIAL_SET_GAS_LIMIT = 6_800_000L; private BlockUtils() { } @@ -118,7 +119,19 @@ public static List sortBlocksByNumber(List blocks) { .collect(Collectors.toList()); } - public static long getSublistGasLimit(Block block) { - return GasCost.toGas(block.getGasLimit()) / (Constants.getTransactionExecutionThreads() + 1); + public static long getSublistGasLimit(Block block, boolean forSequentialTxSet) { + long blockGasLimit = GasCost.toGas(block.getGasLimit()); + + if (blockGasLimit <= SEQUENTIAL_SET_GAS_LIMIT) { + if (forSequentialTxSet) { + return blockGasLimit; + } + return 0; + } + + if (forSequentialTxSet) { + return SEQUENTIAL_SET_GAS_LIMIT; + } + return (blockGasLimit - SEQUENTIAL_SET_GAS_LIMIT) / (Constants.getTransactionExecutionThreads()); } } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index b836a0fc468..6a83d8adbfe 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -19,6 +19,7 @@ package co.rsk.core.bc; import co.rsk.core.RskAddress; +import org.ethereum.core.Block; import org.ethereum.core.Transaction; import org.ethereum.db.ByteArrayWrapper; import org.ethereum.vm.GasCost; @@ -31,15 +32,15 @@ public class ParallelizeTransactionHandler { private final Map sublistOfSender; private final ArrayList sublists; - public ParallelizeTransactionHandler(short numberOfSublists, long sublistGasLimit) { + public ParallelizeTransactionHandler(short numberOfSublists, Block block) { this.sublistOfSender = new HashMap<>(); this.sublistsHavingWrittenToKey = new HashMap<>(); this.sublistsHavingReadFromKey = new HashMap<>(); this.sublists = new ArrayList<>(); for (short i = 0; i < numberOfSublists; i++){ - this.sublists.add(new TransactionSublist(sublistGasLimit, false)); + this.sublists.add(new TransactionSublist(BlockUtils.getSublistGasLimit(block, false), false)); } - this.sublists.add(new TransactionSublist(sublistGasLimit, true)); + this.sublists.add(new TransactionSublist(BlockUtils.getSublistGasLimit(block, true), true)); } public Optional addTransaction(Transaction tx, Set newReadKeys, Set newWrittenKeys, long gasUsedByTx) { diff --git a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java index f6148a8a7d8..0c6eb7dc33d 100644 --- a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java +++ b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java @@ -74,7 +74,7 @@ public TransactionValidationResult isValid(Transaction tx, Block executionBlock, long executionBlockNumber = executionBlock.getNumber(); ActivationConfig.ForBlock activations = activationConfig.forBlock(executionBlockNumber); BigInteger gasLimit = activations.isActive(ConsensusRule.RSKIP144) - ? BigInteger.valueOf(BlockUtils.getSublistGasLimit(executionBlock)) + ? BigInteger.valueOf(Math.max(BlockUtils.getSublistGasLimit(executionBlock, true), BlockUtils.getSublistGasLimit(executionBlock, false))) : BigIntegers.fromUnsignedByteArray(executionBlock.getGasLimit()); Coin minimumGasPrice = executionBlock.getMinimumGasPrice(); long basicTxCost = tx.transactionCost(constants, activations, signatureCache); diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java index 4e3af633788..f265ef477c9 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java @@ -188,7 +188,7 @@ void sublistGasLimit_ShouldReturnCorrectValue() { Block block = mock(Block.class); when(block.getGasLimit()).thenReturn(BigInteger.valueOf(10_000_000L).toByteArray()); - long expectedLimit = 10_000_000L / (Constants.getTransactionExecutionThreads() + 1); - assertEquals(expectedLimit, BlockUtils.getSublistGasLimit(block)); + long expectedLimit = (10_000_000L - BlockUtils.SEQUENTIAL_SET_GAS_LIMIT) / (Constants.getTransactionExecutionThreads()); + assertEquals(expectedLimit, BlockUtils.getSublistGasLimit(block, false)); } } diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index bf75bcc2961..aa77468a7d7 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -21,6 +21,7 @@ import co.rsk.test.builders.AccountBuilder; import co.rsk.test.builders.TransactionBuilder; import org.ethereum.core.Account; +import org.ethereum.core.Block; import org.ethereum.core.Transaction; import org.ethereum.db.ByteArrayWrapper; import org.ethereum.vm.GasCost; @@ -31,6 +32,9 @@ import java.math.BigInteger; import java.util.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + class ParallelizeTransactionHandlerTest { @@ -43,30 +47,36 @@ class ParallelizeTransactionHandlerTest { private ByteArrayWrapper aDifferentWrappedKey; private Transaction bigTx; private Transaction bigTx2; + private Transaction bigSequentialTx; private short sequentialSublistNumber; @BeforeEach public void setup() { + long blockGasLimit = 8_160_000L; + Block executionBlock = mock(Block.class); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(blockGasLimit).toByteArray()); Account sender = new AccountBuilder().name("sender").build(); Account sender2 = new AccountBuilder().name("sender2").build(); Account sender3 = new AccountBuilder().name("sender3").build(); Account sender4 = new AccountBuilder().name("sender4").build(); Account sender5 = new AccountBuilder().name("sender5").build(); + Account sender6 = new AccountBuilder().name("sender6").build(); byte[] aKey = {1, 2, 3}; byte[] aDifferentKey = {1, 2, 3, 4}; - int sublistGasLimit = 3400000; long gasUsedByTx = 16000; - long biggestGasLimitPossibleInSublists = sublistGasLimit - 1; + long biggestGasLimitPossibleInParallelSublists = BlockUtils.getSublistGasLimit(executionBlock, false) - 1; + long biggestGasLimitPossibleInSequentialSublist = BlockUtils.getSublistGasLimit(executionBlock, true) - 1; aWrappedKey = new ByteArrayWrapper(aKey); sublists = 2; sequentialSublistNumber = sublists; - handler = new ParallelizeTransactionHandler(sublists, sublistGasLimit); + handler = new ParallelizeTransactionHandler(sublists, executionBlock); tx = new TransactionBuilder().nonce(1).sender(sender).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); tx2 = new TransactionBuilder().nonce(1).sender(sender2).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); tx3 = new TransactionBuilder().nonce(1).sender(sender3).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); - bigTx = new TransactionBuilder().nonce(1).sender(sender4).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInSublists)).value(BigInteger.valueOf(1)).build(); - bigTx2 = new TransactionBuilder().nonce(1).sender(sender5).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInSublists)).value(BigInteger.valueOf(1)).build(); + bigTx = new TransactionBuilder().nonce(1).sender(sender4).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInParallelSublists)).value(BigInteger.valueOf(1)).build(); + bigTx2 = new TransactionBuilder().nonce(1).sender(sender5).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInParallelSublists)).value(BigInteger.valueOf(1)).build(); + bigSequentialTx = new TransactionBuilder().nonce(1).sender(sender6).gasLimit(BigInteger.valueOf(biggestGasLimitPossibleInSequentialSublist)).value(BigInteger.valueOf(1)).build(); aDifferentWrappedKey = new ByteArrayWrapper(aDifferentKey); } @@ -564,19 +574,20 @@ void ifATxCollidesByTheSenderAndAKeyWithTwoTxsShouldBeAddedIntoTheSequential() { void ifANewTxComesAndAllThePossibleSublistAreFullTheTxShouldNotBeAdded() { long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); + long gasUsedByBigSequentialTx = GasCost.toGas(bigSequentialTx.getGasLimit()); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); short[] expectedTransactionEdgeList = new short[]{1,2}; List expectedListOfTxs = new ArrayList<>(); expectedListOfTxs.add(bigTx); expectedListOfTxs.add(bigTx2); - expectedListOfTxs.add(bigTx); + expectedListOfTxs.add(bigSequentialTx); Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); Optional sublistGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional sublistGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional sublistGasUsed3 = handler.addTransaction(bigSequentialTx, new HashSet<>(), new HashSet<>(), gasUsedByBigSequentialTx); Optional sublistGasUsed4 = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), gasUsedByTx); Assertions.assertFalse(sublistGasUsed4.isPresent()); @@ -584,8 +595,8 @@ void ifANewTxComesAndAllThePossibleSublistAreFullTheTxShouldNotBeAdded() { Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); Assertions.assertEquals(gasUsedByBigTx2, (long) sublistGasUsed2.get()); - Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed3.get()); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByBigSequentialTx, (long) sublistGasUsed3.get()); + Assertions.assertEquals(gasUsedByBigSequentialTx, handler.getGasUsedIn(sequentialSublistNumber)); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); Assertions.assertArrayEquals(expectedTransactionEdgeList, handler.getTransactionsPerSublistInOrder()); } @@ -622,22 +633,23 @@ void ifSublistsAreFullAndAnIndependentTxComesShouldBeAddedInTheSequential() { @Test void ifAllTheSublistsAreFullTheNewIndependentTxShouldNotBeIncluded() { short[] expectedTransactionEdgeList = new short[]{1,2}; - List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, bigTx); + List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, bigSequentialTx); long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); + long gasUsedByBigSequentialTx = GasCost.toGas(bigSequentialTx.getGasLimit()); Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); Optional sublistGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional sublistGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional sublistGasUsed3 = handler.addTransaction(bigSequentialTx, new HashSet<>(), new HashSet<>(), gasUsedByBigSequentialTx); Assertions.assertTrue(sublistGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed3.get()); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByBigSequentialTx, (long) sublistGasUsed3.get()); + Assertions.assertEquals(gasUsedByBigSequentialTx, handler.getGasUsedIn(sequentialSublistNumber)); Optional emptySublist = handler.addTransaction(tx, new HashSet<>(), new HashSet<>(), GasCost.toGas(tx.getGasLimit())); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByBigSequentialTx, handler.getGasUsedIn(sequentialSublistNumber)); Assertions.assertFalse(emptySublist.isPresent()); Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); @@ -649,23 +661,24 @@ void ifAllTheSublistsAreFullTheNewIndependentTxShouldNotBeIncluded() { @Test void ifAllTheSublistsAreFullTheNewDependentTxShouldNotBeIncluded() { short[] expectedTransactionEdgeList = new short[]{1,2}; - List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, bigTx); + List expectedListOfTxs = Arrays.asList(bigTx, bigTx2, bigSequentialTx); long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); long gasUsedByBigTx2 = GasCost.toGas(bigTx2.getGasLimit()); + long gasUsedByBigSequentialTx = GasCost.toGas(bigSequentialTx.getGasLimit()); HashSet writtenKeys = createASetAndAddKeys(aWrappedKey); Optional sublistGasUsed = handler.addTransaction(bigTx, new HashSet<>(), writtenKeys, gasUsedByBigTx); Optional sublistGasUsed2 = handler.addTransaction(bigTx2, new HashSet<>(), new HashSet<>(), gasUsedByBigTx2); Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional sublistGasUsed3 = handler.addTransaction(bigTx, new HashSet<>(), new HashSet<>(), gasUsedByBigTx); + Optional sublistGasUsed3 = handler.addTransaction(bigSequentialTx, new HashSet<>(), new HashSet<>(), gasUsedByBigSequentialTx); Assertions.assertTrue(sublistGasUsed3.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed3.get()); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByBigSequentialTx, (long) sublistGasUsed3.get()); + Assertions.assertEquals(gasUsedByBigSequentialTx, handler.getGasUsedIn(sequentialSublistNumber)); Optional emptySublist = handler.addTransaction(tx, new HashSet<>(), writtenKeys, GasCost.toGas(tx.getGasLimit())); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByBigSequentialTx, handler.getGasUsedIn(sequentialSublistNumber)); Assertions.assertFalse(emptySublist.isPresent()); Assertions.assertTrue(sublistGasUsed.isPresent() && sublistGasUsed2.isPresent()); Assertions.assertEquals(gasUsedByBigTx, (long) sublistGasUsed.get()); @@ -690,21 +703,21 @@ void aRemascTxAddedShouldBeInTheSequentialSublist() { @Test void aTxDirectedToAPrecompiledContractAddedShouldBeInTheSequentialSublist() { - List expectedListOfTxs = Collections.singletonList(bigTx); + List expectedListOfTxs = Collections.singletonList(bigSequentialTx); long gasUsedByTx = GasCost.toGas(tx.getGasLimit()); - long gasUsedByBigTx = GasCost.toGas(bigTx.getGasLimit()); + long gasUsedByBigSequentialTx = GasCost.toGas(bigSequentialTx.getGasLimit()); Assertions.assertEquals(0, handler.getGasUsedIn(sequentialSublistNumber)); - Optional sequentialSublistGasUsedAfterBigTx = handler.addTxToSequentialSublist(bigTx, gasUsedByBigTx); + Optional sequentialSublistGasUsedAfterBigTx = handler.addTxToSequentialSublist(bigSequentialTx, gasUsedByBigSequentialTx); Assertions.assertTrue(sequentialSublistGasUsedAfterBigTx.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); - Assertions.assertEquals(gasUsedByBigTx, (long) sequentialSublistGasUsedAfterBigTx.get()); + Assertions.assertEquals(gasUsedByBigSequentialTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByBigSequentialTx, (long) sequentialSublistGasUsedAfterBigTx.get()); Optional sequentialSublistGasUsedAfterTx = handler.addTxToSequentialSublist(tx, gasUsedByTx); Assertions.assertFalse(sequentialSublistGasUsedAfterTx.isPresent()); - Assertions.assertEquals(gasUsedByBigTx, handler.getGasUsedIn(sequentialSublistNumber)); + Assertions.assertEquals(gasUsedByBigSequentialTx, handler.getGasUsedIn(sequentialSublistNumber)); Assertions.assertEquals(expectedListOfTxs, handler.getTransactionsInOrder()); } @@ -855,11 +868,14 @@ void writeSequentialAfterTwoParallelReadsAndAWriteShouldGoToSequential() { @Test void writeAfterWriteToSequentialForOutOfGasShouldGoToSequential() { + Block executionBlock = mock(Block.class); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6_804_000L).toByteArray()); + HashSet setWithX = createASetAndAddKeys(aWrappedKey); AccountBuilder accountBuilder = new AccountBuilder(); - ParallelizeTransactionHandler handler = new ParallelizeTransactionHandler((short) 2, 1000); + ParallelizeTransactionHandler handler = new ParallelizeTransactionHandler((short) 2, executionBlock); // write X with 800 handler.addTransaction( diff --git a/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java b/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java index 05f0a1119af..19a62f2245f 100644 --- a/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java +++ b/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java @@ -108,7 +108,7 @@ void isValid_ShouldBeValid_WhenRSKIP144IsAlreadyActivated() { when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(10L).toByteArray()); when(executionBlock.getMinimumGasPrice()).thenReturn(Coin.valueOf(1L)); - long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock); + long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock, true); Transaction tx = mock(Transaction.class); when(tx.getGasLimitAsInteger()).thenReturn(BigInteger.valueOf(sublistGasLimit)); @@ -143,7 +143,7 @@ void isValid_ShouldBeInvalid_WhenRSKIP144IsAlreadyActivated() { when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(10L).toByteArray()); when(executionBlock.getMinimumGasPrice()).thenReturn(Coin.valueOf(1L)); - long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock); + long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock, true); Transaction tx = mock(Transaction.class); when(tx.getGasLimitAsInteger()).thenReturn(BigInteger.valueOf(sublistGasLimit + 1L)); From a214bff4fb6cffb2af0e0211467b13b8870dfd31 Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Tue, 6 Feb 2024 14:03:22 -0400 Subject: [PATCH 53/89] Change refactor approach and update affected tests --- .../java/co/rsk/core/bc/BlockExecutor.java | 10 +++--- .../main/java/co/rsk/core/bc/BlockUtils.java | 24 ++++++++------ .../bc/ParallelizeTransactionHandler.java | 6 ++-- .../rsk/net/handler/TxPendingValidator.java | 2 +- .../java/org/ethereum/config/Constants.java | 30 +++++++++++++----- .../co/rsk/core/bc/BlockExecutorTest.java | 31 +++++++++---------- .../java/co/rsk/core/bc/BlockUtilsTest.java | 21 ++++++++++--- .../bc/ParallelizeTransactionHandlerTest.java | 25 ++++++++------- .../net/handler/TxPendingValidatorTest.java | 13 ++++++-- 9 files changed, 103 insertions(+), 59 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java index 25b6db992e6..afff40d822b 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockExecutor.java @@ -70,6 +70,7 @@ public class BlockExecutor { private final Map transactionResults = new ConcurrentHashMap<>(); private boolean registerProgramResults; + private long minSequentialSetGasLimit; /** * An array of ExecutorService's of size `Constants.getTransactionExecutionThreads()`. Each parallel list uses an executor @@ -88,6 +89,7 @@ public BlockExecutor( this.activationConfig = systemProperties.getActivationConfig(); this.remascEnabled = systemProperties.isRemascEnabled(); this.concurrentContractsDisallowed = Collections.unmodifiableSet(new HashSet<>(systemProperties.concurrentContractsDisallowed())); + this.minSequentialSetGasLimit = systemProperties.getNetworkConstants().getMinSequentialSetGasLimit(); int numOfParallelList = Constants.getTransactionExecutionThreads(); this.execServices = new ExecutorService[numOfParallelList]; @@ -485,7 +487,7 @@ public void execute(@Nonnull Runnable command) { Coin.ZERO, remascEnabled, concurrentContractsDisallowed, - BlockUtils.getSublistGasLimit(block, false) + BlockUtils.getSublistGasLimit(block, false, minSequentialSetGasLimit) ); completionService.submit(txListExecutor); transactionListExecutors.add(txListExecutor); @@ -560,7 +562,7 @@ public void execute(@Nonnull Runnable command) { totalPaidFees, remascEnabled, Collections.emptySet(), // precompiled contracts are always allowed in a sequential list, as there's no concurrency in it - BlockUtils.getSublistGasLimit(block, true) + BlockUtils.getSublistGasLimit(block, true, minSequentialSetGasLimit) ); Boolean success = txListExecutor.call(); if (!Boolean.TRUE.equals(success)) { @@ -624,7 +626,7 @@ private BlockResult executeForMiningAfterRSKIP144( int txindex = 0; int transactionExecutionThreads = Constants.getTransactionExecutionThreads(); - ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) transactionExecutionThreads, block); + ParallelizeTransactionHandler parallelizeTransactionHandler = new ParallelizeTransactionHandler((short) transactionExecutionThreads, block, minSequentialSetGasLimit); for (Transaction tx : transactionsList) { loggingApplyBlockToTx(block, i); @@ -640,7 +642,7 @@ private BlockResult executeForMiningAfterRSKIP144( 0, deletedAccounts, true, - BlockUtils.getSublistGasLimit(block, false) + Math.max(BlockUtils.getSublistGasLimit(block, true, minSequentialSetGasLimit), BlockUtils.getSublistGasLimit(block, false, minSequentialSetGasLimit)) ); boolean transactionExecuted = txExecutor.executeTransaction(); diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java index 33eef57cf54..410fa938a72 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java @@ -35,7 +35,6 @@ */ public class BlockUtils { private static final long MAX_BLOCK_PROCESS_TIME_NANOSECONDS = 60_000_000_000L; - public static final long SEQUENTIAL_SET_GAS_LIMIT = 6_800_000L; private BlockUtils() { } @@ -119,19 +118,24 @@ public static List sortBlocksByNumber(List blocks) { .collect(Collectors.toList()); } - public static long getSublistGasLimit(Block block, boolean forSequentialTxSet) { + public static long getSublistGasLimit(Block block, boolean forSequentialTxSet, long minSequentialSetGasLimit) { long blockGasLimit = GasCost.toGas(block.getGasLimit()); + int transactionExecutionThreadCount = Constants.getTransactionExecutionThreads(); + + if((transactionExecutionThreadCount + 1) * minSequentialSetGasLimit <= blockGasLimit) { + return blockGasLimit / (transactionExecutionThreadCount + 1); + } else { + if (blockGasLimit <= minSequentialSetGasLimit) { + if (forSequentialTxSet) { + return blockGasLimit; + } + return 0; + } - if (blockGasLimit <= SEQUENTIAL_SET_GAS_LIMIT) { if (forSequentialTxSet) { - return blockGasLimit; + return minSequentialSetGasLimit; } - return 0; - } - - if (forSequentialTxSet) { - return SEQUENTIAL_SET_GAS_LIMIT; + return (blockGasLimit - minSequentialSetGasLimit) / (transactionExecutionThreadCount); } - return (blockGasLimit - SEQUENTIAL_SET_GAS_LIMIT) / (Constants.getTransactionExecutionThreads()); } } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java index 6a83d8adbfe..814554f3e33 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/ParallelizeTransactionHandler.java @@ -32,15 +32,15 @@ public class ParallelizeTransactionHandler { private final Map sublistOfSender; private final ArrayList sublists; - public ParallelizeTransactionHandler(short numberOfSublists, Block block) { + public ParallelizeTransactionHandler(short numberOfSublists, Block block, long minSequentialSetGasLimit) { this.sublistOfSender = new HashMap<>(); this.sublistsHavingWrittenToKey = new HashMap<>(); this.sublistsHavingReadFromKey = new HashMap<>(); this.sublists = new ArrayList<>(); for (short i = 0; i < numberOfSublists; i++){ - this.sublists.add(new TransactionSublist(BlockUtils.getSublistGasLimit(block, false), false)); + this.sublists.add(new TransactionSublist(BlockUtils.getSublistGasLimit(block, false, minSequentialSetGasLimit), false)); } - this.sublists.add(new TransactionSublist(BlockUtils.getSublistGasLimit(block, true), true)); + this.sublists.add(new TransactionSublist(BlockUtils.getSublistGasLimit(block, true, minSequentialSetGasLimit), true)); } public Optional addTransaction(Transaction tx, Set newReadKeys, Set newWrittenKeys, long gasUsedByTx) { diff --git a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java index 0c6eb7dc33d..798361abf10 100644 --- a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java +++ b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java @@ -74,7 +74,7 @@ public TransactionValidationResult isValid(Transaction tx, Block executionBlock, long executionBlockNumber = executionBlock.getNumber(); ActivationConfig.ForBlock activations = activationConfig.forBlock(executionBlockNumber); BigInteger gasLimit = activations.isActive(ConsensusRule.RSKIP144) - ? BigInteger.valueOf(Math.max(BlockUtils.getSublistGasLimit(executionBlock, true), BlockUtils.getSublistGasLimit(executionBlock, false))) + ? BigInteger.valueOf(Math.max(BlockUtils.getSublistGasLimit(executionBlock, true, constants.getMinSequentialSetGasLimit()), BlockUtils.getSublistGasLimit(executionBlock, false, constants.getMinSequentialSetGasLimit()))) : BigIntegers.fromUnsignedByteArray(executionBlock.getGasLimit()); Coin minimumGasPrice = executionBlock.getMinimumGasPrice(); long basicTxCost = tx.transactionCost(constants, activations, signatureCache); diff --git a/rskj-core/src/main/java/org/ethereum/config/Constants.java b/rskj-core/src/main/java/org/ethereum/config/Constants.java index d8efab75255..3c15ec88fbb 100644 --- a/rskj-core/src/main/java/org/ethereum/config/Constants.java +++ b/rskj-core/src/main/java/org/ethereum/config/Constants.java @@ -60,6 +60,7 @@ public class Constants { private final int newBlockMaxSecondsInTheFuture; public final BridgeConstants bridgeConstants; private final ActivationConfig activationConfig; + private final long minSequentialSetGasLimit; public Constants( byte chainId, @@ -71,7 +72,8 @@ public Constants( int newBlockMaxSecondsInTheFuture, BridgeConstants bridgeConstants, ActivationConfig activationConfig, - BlockDifficulty minimumDifficultyForRskip290) { + BlockDifficulty minimumDifficultyForRskip290, + long minSequentialSetGasLimit) { this.chainId = chainId; this.seedCowAccounts = seedCowAccounts; this.durationLimit = durationLimit; @@ -82,6 +84,7 @@ public Constants( this.bridgeConstants = bridgeConstants; this.activationConfig = activationConfig; this.minimumDifficultyForRskip290 = minimumDifficultyForRskip290; + this.minSequentialSetGasLimit = minSequentialSetGasLimit; } public Constants( @@ -93,7 +96,8 @@ public Constants( BigInteger difficultyBoundDivisor, int newBlockMaxSecondsInTheFuture, BridgeConstants bridgeConstants, - BlockDifficulty minimumDifficultyForRskip290) { + BlockDifficulty minimumDifficultyForRskip290, + long minSequentialSetGasLimit) { this(chainId, seedCowAccounts, durationLimit, @@ -103,7 +107,8 @@ public Constants( newBlockMaxSecondsInTheFuture, bridgeConstants, null, - minimumDifficultyForRskip290 + minimumDifficultyForRskip290, + minSequentialSetGasLimit ); } @@ -228,6 +233,10 @@ public static int getMaxBitcoinMergedMiningMerkleProofLength() { public static int getTransactionExecutionThreads() { return TX_EXECUTION_THREADS; } + public long getMinSequentialSetGasLimit() { + return minSequentialSetGasLimit; + } + public static Constants mainnet() { return new Constants( MAINNET_CHAIN_ID, @@ -238,7 +247,8 @@ public static Constants mainnet() { BigInteger.valueOf(50), 60, BridgeMainNetConstants.getInstance(), - new BlockDifficulty(new BigInteger("550000000")) + new BlockDifficulty(new BigInteger("550000000")), + 6_800_000L ); } @@ -252,7 +262,8 @@ public static Constants devnetWithFederation(List federationPublicKeys BigInteger.valueOf(50), 540, new BridgeDevNetConstants(federationPublicKeys), - new BlockDifficulty(new BigInteger("550000000")) + new BlockDifficulty(new BigInteger("550000000")), + 6_800_000L ); } @@ -267,7 +278,8 @@ public static Constants testnet(ActivationConfig activationConfig) { 540, BridgeTestNetConstants.getInstance(), activationConfig, - new BlockDifficulty(new BigInteger("550000000")) + new BlockDifficulty(new BigInteger("550000000")), + 6_800_000L ); } @@ -281,7 +293,8 @@ public static Constants regtest() { BigInteger.valueOf(2048), 0, BridgeRegTestConstants.getInstance(), - new BlockDifficulty(new BigInteger("550000000")) + new BlockDifficulty(new BigInteger("550000000")), + 1_000_000L ); } @@ -295,7 +308,8 @@ public static Constants regtestWithFederation(List genesisFederationPu BigInteger.valueOf(2048), 0, new BridgeRegTestConstants(genesisFederationPublicKeys), - new BlockDifficulty(new BigInteger("550000000")) + new BlockDifficulty(new BigInteger("550000000")), + 1_000_000L ); } } diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index 1d232a7f559..e885098b890 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -57,7 +57,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import java.math.BigInteger; @@ -80,6 +79,7 @@ public class BlockExecutorTest { private static final ActivationConfig activationConfig = spy(CONFIG.getActivationConfig()); private static final BlockFactory BLOCK_FACTORY = new BlockFactory(activationConfig); public static final boolean RSKIP_126_IS_ACTIVE = true; + private final long MIN_SEQUENTIAL_SET_GAS_LIMIT = Constants.regtest().getMinSequentialSetGasLimit(); @TempDir public Path tempDir; @@ -562,9 +562,10 @@ void gasUsedShouldNeverSurprassBlockGasLimit(boolean activeRskip144) { BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); int gasLimit = 21000; - int transactionNumberToFillParallelSublist = (int) (getSublistGaslimit(parent) / gasLimit); - int totalNumberOfSublists = Constants.getTransactionExecutionThreads() + 1; - int totalTxs = (transactionNumberToFillParallelSublist) * totalNumberOfSublists + 1; + int transactionNumberToFillSequentialSublist = (int) (BlockUtils.getSublistGasLimit(parent, true, MIN_SEQUENTIAL_SET_GAS_LIMIT) / gasLimit); + int transactionNumberToFillParallelSublist = (int) (BlockUtils.getSublistGasLimit(parent, false, MIN_SEQUENTIAL_SET_GAS_LIMIT) / gasLimit); + int totalNumberOfParallelSublists = Constants.getTransactionExecutionThreads(); + int totalTxs = (transactionNumberToFillParallelSublist * totalNumberOfParallelSublists) + transactionNumberToFillSequentialSublist + 1; Block block = getBlockWithNIndependentTransactions(totalTxs, BigInteger.valueOf(gasLimit), false); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); Assertions.assertFalse(block.getGasUsed() > GasCost.toGas(block.getGasLimit())); @@ -580,7 +581,7 @@ void whenParallelSublistsAreFullTheLastTxShouldGoToSequential(boolean activeRski BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); int gasLimit = 21000; - int numberOfTransactions = (int) (getSublistGaslimit(parent)/gasLimit); + int numberOfTransactions = (int) (BlockUtils.getSublistGasLimit(parent, false, MIN_SEQUENTIAL_SET_GAS_LIMIT) / gasLimit); short[] expectedEdges = new short[]{(short) numberOfTransactions, (short) (numberOfTransactions*2), (short) (numberOfTransactions*3), (short) (numberOfTransactions*4)}; int transactionsInSequential = 1; Block block = getBlockWithNIndependentTransactions(numberOfTransactions * Constants.getTransactionExecutionThreads() + transactionsInSequential, BigInteger.valueOf(gasLimit), false); @@ -619,7 +620,7 @@ void executeATxInSequentialAndBlockResultShouldTrackTheGasUsedInTheBlock(boolean BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); int gasLimit = 21000; - int transactionNumberToFillParallelSublist = (int) (getSublistGaslimit(parent) / gasLimit); + int transactionNumberToFillParallelSublist = (int) (BlockUtils.getSublistGasLimit(parent, false, MIN_SEQUENTIAL_SET_GAS_LIMIT) / gasLimit); int transactionsInSequential = 1; int totalTxsNumber = transactionNumberToFillParallelSublist * Constants.getTransactionExecutionThreads() + transactionsInSequential; Block block = getBlockWithNIndependentTransactions(totalTxsNumber, BigInteger.valueOf(gasLimit), false); @@ -638,9 +639,10 @@ void withTheSublistsFullTheLastTransactionShouldNotFit(boolean activeRskip144) { BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); int gasLimit = 21000; - int transactionNumberToFillParallelSublist = (int) (getSublistGaslimit(parent) / gasLimit); - int totalNumberOfSublists = Constants.getTransactionExecutionThreads() + 1; - int totalTxs = (transactionNumberToFillParallelSublist) * totalNumberOfSublists + 1; + int transactionNumberToFillSequentialSublist = (int) (BlockUtils.getSublistGasLimit(parent, true, MIN_SEQUENTIAL_SET_GAS_LIMIT) / gasLimit); + int transactionNumberToFillParallelSublist = (int) (BlockUtils.getSublistGasLimit(parent, false, MIN_SEQUENTIAL_SET_GAS_LIMIT) / gasLimit); + int totalNumberOfSublists = Constants.getTransactionExecutionThreads(); + int totalTxs = (transactionNumberToFillParallelSublist * totalNumberOfSublists) + transactionNumberToFillSequentialSublist + 1; Block block = getBlockWithNIndependentTransactions(totalTxs, BigInteger.valueOf(gasLimit), false); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); Assertions.assertEquals(totalTxs - 1, blockResult.getExecutedTransactions().size()); @@ -657,9 +659,10 @@ void withSequentialSublistFullRemascTxShouldFit(boolean activeRskip144) { BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); int gasLimit = 21000; - int transactionNumberToFillASublist = (int) (getSublistGaslimit(parent) / gasLimit); - int totalNumberOfSublists = Constants.getTransactionExecutionThreads() + 1; - int expectedNumberOfTx = transactionNumberToFillASublist * totalNumberOfSublists + 1; + int transactionNumberToFillSequentialSublist = (int) (BlockUtils.getSublistGasLimit(parent, true, MIN_SEQUENTIAL_SET_GAS_LIMIT) / gasLimit); + int transactionNumberToFillParallelSublist = (int) (BlockUtils.getSublistGasLimit(parent, false, MIN_SEQUENTIAL_SET_GAS_LIMIT) / gasLimit); + int totalNumberOfParallelSublists = Constants.getTransactionExecutionThreads(); + int expectedNumberOfTx = (transactionNumberToFillParallelSublist * totalNumberOfParallelSublists) + transactionNumberToFillSequentialSublist + 1; Block block = getBlockWithNIndependentTransactions(expectedNumberOfTx, BigInteger.valueOf(gasLimit), true); BlockResult blockResult = executor.executeAndFill(block, parent.getHeader()); @@ -1013,10 +1016,6 @@ void invalidBlockBadLogsBloom(boolean activeRskip144) { Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); } - private static long getSublistGaslimit(Block parent) { - return GasCost.toGas(parent.getGasLimit()) / (Constants.getTransactionExecutionThreads() + 1); - } - private static TestObjects generateBlockWithOneTransaction(Boolean activeRskip144, boolean rskip126IsActive) { TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Repository repository = new MutableRepository(trieStore, new Trie(trieStore)); diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java index f265ef477c9..f55c866cd17 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java @@ -184,11 +184,24 @@ void tooMuchProcessTime() { } @Test - void sublistGasLimit_ShouldReturnCorrectValue() { + void sublistGasLimit_ShouldDivideGasLimitEquallyAmongAllSets() { + long minSequentialSetGasLimit = Constants.regtest().getMinSequentialSetGasLimit(); + long mockedBlockGasLimit = 10_000_000L; Block block = mock(Block.class); - when(block.getGasLimit()).thenReturn(BigInteger.valueOf(10_000_000L).toByteArray()); + when(block.getGasLimit()).thenReturn(BigInteger.valueOf(mockedBlockGasLimit).toByteArray()); - long expectedLimit = (10_000_000L - BlockUtils.SEQUENTIAL_SET_GAS_LIMIT) / (Constants.getTransactionExecutionThreads()); - assertEquals(expectedLimit, BlockUtils.getSublistGasLimit(block, false)); + long expectedLimit = mockedBlockGasLimit / (Constants.getTransactionExecutionThreads() + 1); + assertEquals(expectedLimit, BlockUtils.getSublistGasLimit(block, false, minSequentialSetGasLimit)); + } + + @Test + void sublistGasLimit_ShouldAssignLessGasLimitToParallelSets() { + long minSequentialSetGasLimit = 6_800_000L; + long mockedBlockGasLimit = 10_000_000L; + Block block = mock(Block.class); + when(block.getGasLimit()).thenReturn(BigInteger.valueOf(mockedBlockGasLimit).toByteArray()); + + long expectedLimit = (mockedBlockGasLimit - minSequentialSetGasLimit) / (Constants.getTransactionExecutionThreads()); + assertEquals(expectedLimit, BlockUtils.getSublistGasLimit(block, false, minSequentialSetGasLimit)); } } diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index aa77468a7d7..d4ef5ae0f81 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -20,6 +20,7 @@ import co.rsk.test.builders.AccountBuilder; import co.rsk.test.builders.TransactionBuilder; +import org.ethereum.config.Constants; import org.ethereum.core.Account; import org.ethereum.core.Block; import org.ethereum.core.Transaction; @@ -49,6 +50,7 @@ class ParallelizeTransactionHandlerTest { private Transaction bigTx2; private Transaction bigSequentialTx; private short sequentialSublistNumber; + private long minSequentialSetGasLimit; @BeforeEach public void setup() { @@ -64,13 +66,14 @@ public void setup() { byte[] aKey = {1, 2, 3}; byte[] aDifferentKey = {1, 2, 3, 4}; long gasUsedByTx = 16000; - long biggestGasLimitPossibleInParallelSublists = BlockUtils.getSublistGasLimit(executionBlock, false) - 1; - long biggestGasLimitPossibleInSequentialSublist = BlockUtils.getSublistGasLimit(executionBlock, true) - 1; + minSequentialSetGasLimit = Constants.regtest().getMinSequentialSetGasLimit(); + long biggestGasLimitPossibleInParallelSublists = BlockUtils.getSublistGasLimit(executionBlock, false, minSequentialSetGasLimit) - 1; + long biggestGasLimitPossibleInSequentialSublist = BlockUtils.getSublistGasLimit(executionBlock, true, minSequentialSetGasLimit) - 1; aWrappedKey = new ByteArrayWrapper(aKey); sublists = 2; sequentialSublistNumber = sublists; - handler = new ParallelizeTransactionHandler(sublists, executionBlock); + handler = new ParallelizeTransactionHandler(sublists, executionBlock, minSequentialSetGasLimit); tx = new TransactionBuilder().nonce(1).sender(sender).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); tx2 = new TransactionBuilder().nonce(1).sender(sender2).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); tx3 = new TransactionBuilder().nonce(1).sender(sender3).value(BigInteger.valueOf(1)).gasLimit(BigInteger.valueOf(gasUsedByTx)).build(); @@ -869,30 +872,30 @@ void writeSequentialAfterTwoParallelReadsAndAWriteShouldGoToSequential() { @Test void writeAfterWriteToSequentialForOutOfGasShouldGoToSequential() { Block executionBlock = mock(Block.class); - when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(6_804_000L).toByteArray()); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(5_000_000L).toByteArray()); HashSet setWithX = createASetAndAddKeys(aWrappedKey); AccountBuilder accountBuilder = new AccountBuilder(); - ParallelizeTransactionHandler handler = new ParallelizeTransactionHandler((short) 2, executionBlock); + ParallelizeTransactionHandler handler = new ParallelizeTransactionHandler((short) 2, executionBlock, minSequentialSetGasLimit); // write X with 800 handler.addTransaction( new TransactionBuilder() .sender(accountBuilder.name("sender1").build()) - .gasLimit(BigInteger.valueOf(800)) + .gasLimit(BigInteger.valueOf(800000)) .build(), - new HashSet<>(), setWithX, 800 + new HashSet<>(), setWithX, 800000 ); // write X with 300 handler.addTransaction( new TransactionBuilder() .sender(accountBuilder.name("sender2").build()) - .gasLimit(BigInteger.valueOf(300)) + .gasLimit(BigInteger.valueOf(300000)) .build(), - new HashSet<>(), setWithX, 300 + new HashSet<>(), setWithX, 300000 ); // last write of X is in sequential because of out of gas. 800 + 300 > 1000 @@ -902,9 +905,9 @@ void writeAfterWriteToSequentialForOutOfGasShouldGoToSequential() { handler.addTransaction( new TransactionBuilder() .sender(accountBuilder.name("sender3").build()) - .gasLimit(BigInteger.valueOf(100)) + .gasLimit(BigInteger.valueOf(100000)) .build(), - setWithX, new HashSet<>(), 100 + setWithX, new HashSet<>(), 100000 ); // should go to sequential diff --git a/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java b/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java index 19a62f2245f..4c1aaee1d9e 100644 --- a/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java +++ b/rskj-core/src/test/java/co/rsk/net/handler/TxPendingValidatorTest.java @@ -22,9 +22,11 @@ import co.rsk.core.Coin; import co.rsk.core.bc.BlockUtils; import co.rsk.net.TransactionValidationResult; +import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.math.BigInteger; @@ -34,6 +36,13 @@ class TxPendingValidatorTest { + private long minSequentialSetGasLimit; + + @BeforeEach + public void setup() { + minSequentialSetGasLimit = Constants.regtest().getMinSequentialSetGasLimit(); + } + @Test void isValid_ShouldBeValid_WhenRSKIP144IsNotActivated() { // before RSKIP144: block gas limit == 10 and tx gas limit == 10 - should be valid @@ -108,7 +117,7 @@ void isValid_ShouldBeValid_WhenRSKIP144IsAlreadyActivated() { when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(10L).toByteArray()); when(executionBlock.getMinimumGasPrice()).thenReturn(Coin.valueOf(1L)); - long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock, true); + long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock, true, minSequentialSetGasLimit); Transaction tx = mock(Transaction.class); when(tx.getGasLimitAsInteger()).thenReturn(BigInteger.valueOf(sublistGasLimit)); @@ -143,7 +152,7 @@ void isValid_ShouldBeInvalid_WhenRSKIP144IsAlreadyActivated() { when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(10L).toByteArray()); when(executionBlock.getMinimumGasPrice()).thenReturn(Coin.valueOf(1L)); - long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock, true); + long sublistGasLimit = BlockUtils.getSublistGasLimit(executionBlock, true, minSequentialSetGasLimit); Transaction tx = mock(Transaction.class); when(tx.getGasLimitAsInteger()).thenReturn(BigInteger.valueOf(sublistGasLimit + 1L)); From e5f90869a64857b4cb3fb7713220afaba57cbcc5 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Wed, 7 Feb 2024 18:12:32 +0200 Subject: [PATCH 54/89] Disabled PTE feature by default --- rskj-core/src/main/resources/reference.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rskj-core/src/main/resources/reference.conf b/rskj-core/src/main/resources/reference.conf index d55474f3fe7..7c55e9ae160 100644 --- a/rskj-core/src/main/resources/reference.conf +++ b/rskj-core/src/main/resources/reference.conf @@ -64,8 +64,8 @@ blockchain = { rskip293 = hop400 rskip294 = hop400 rskip297 = hop400 - rskip351 = arrowhead600 - rskip144 = arrowhead600 + rskip351 = -1 + rskip144 = -1 rskip326 = fingerroot500 rskip353 = hop401 rskip357 = hop401 From 2f35b8fb520f22dcdd8b588c7e48610fa643f644 Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Thu, 8 Feb 2024 09:22:52 -0400 Subject: [PATCH 55/89] Update tx execution threads constant value --- .../main/java/org/ethereum/config/Constants.java | 2 +- .../java/co/rsk/core/bc/BlockExecutorTest.java | 16 ++++++++-------- .../bc/ParallelizeTransactionHandlerTest.java | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/config/Constants.java b/rskj-core/src/main/java/org/ethereum/config/Constants.java index 3c15ec88fbb..6b98cf5574f 100644 --- a/rskj-core/src/main/java/org/ethereum/config/Constants.java +++ b/rskj-core/src/main/java/org/ethereum/config/Constants.java @@ -48,7 +48,7 @@ public class Constants { private static final long DEFAULT_MAX_TIMESTAMPS_DIFF_IN_SECS = 5L * 60; // 5 mins private static final long TESTNET_MAX_TIMESTAMPS_DIFF_IN_SECS = 120L * 60; // 120 mins - public static final int TX_EXECUTION_THREADS = 4; + public static final int TX_EXECUTION_THREADS = 2; private final byte chainId; private final boolean seedCowAccounts; diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index e885098b890..f7fb559554e 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -520,7 +520,7 @@ void executeSequentiallyTenIndependentTxsAndThemShouldGoInBothSublists(boolean a doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); long txGasLimit = 21000L; - short[] expectedEdges = new short[]{3, 6, 9, 12}; + short[] expectedEdges = new short[]{6, 12}; Block parent = blockchain.getBestBlock(); int numberOfTxs = 12; @@ -582,7 +582,7 @@ void whenParallelSublistsAreFullTheLastTxShouldGoToSequential(boolean activeRski Block parent = blockchain.getBestBlock(); int gasLimit = 21000; int numberOfTransactions = (int) (BlockUtils.getSublistGasLimit(parent, false, MIN_SEQUENTIAL_SET_GAS_LIMIT) / gasLimit); - short[] expectedEdges = new short[]{(short) numberOfTransactions, (short) (numberOfTransactions*2), (short) (numberOfTransactions*3), (short) (numberOfTransactions*4)}; + short[] expectedEdges = new short[]{(short) numberOfTransactions, (short) (numberOfTransactions*2)}; int transactionsInSequential = 1; Block block = getBlockWithNIndependentTransactions(numberOfTransactions * Constants.getTransactionExecutionThreads() + transactionsInSequential, BigInteger.valueOf(gasLimit), false); List transactionsList = block.getTransactionsList(); @@ -680,7 +680,7 @@ void executeParallelBlocksWithDifferentSubsets(boolean activeRskip144) { doReturn(true).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); - Block block1 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); + Block block1 = getBlockWithTenTransactions(new short[]{4, 8}); BlockResult result1 = executor.execute(null, 0, block1, parent.getHeader(), true, false, true); @@ -700,7 +700,7 @@ void executeParallelBlockAgainstSequentialBlock(boolean activeRskip144) { doReturn(true).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); - Block pBlock = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); + Block pBlock = getBlockWithTenTransactions(new short[]{4, 8}); BlockResult parallelResult = executor.execute(null, 0, pBlock, parent.getHeader(), true, false, true); @@ -799,11 +799,11 @@ void executeParallelBlockTwice(boolean activeRskip144) { doReturn(true).when(activationConfig).isActive(eq(ConsensusRule.RSKIP144), anyLong()); BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, RSKIP_126_IS_ACTIVE); Block parent = blockchain.getBestBlock(); - Block block1 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); + Block block1 = getBlockWithTenTransactions(new short[]{4, 8}); BlockResult result1 = executor.execute(null, 0, block1, parent.getHeader(), true, false, true); - Block block2 = getBlockWithTenTransactions(new short[]{2, 4, 6, 8}); + Block block2 = getBlockWithTenTransactions(new short[]{4, 8}); BlockResult result2 = executor.execute(null, 0, block2, parent.getHeader(), true, false, true); Assertions.assertArrayEquals(result2.getFinalState().getHash().getBytes(), result1.getFinalState().getHash().getBytes()); @@ -846,14 +846,14 @@ void blockWithOneTxRemascShouldGoToSequentialSublist (boolean activeRskip144) { @ValueSource(booleans = {true, false}) void blockWithManyTxsRemascShouldGoToSequentialSublist (boolean activeRskip144) { if (!activeRskip144) return; - testBlockWithTxTxEdgesMatchAndRemascTxIsAtLastPosition(3, new short[]{ 1, 2, 3 }, activeRskip144); + testBlockWithTxTxEdgesMatchAndRemascTxIsAtLastPosition(2, new short[]{ 1, 2 }, activeRskip144); } @ParameterizedTest @ValueSource(booleans = {true, false}) void blockWithMoreThanThreadsTxsRemascShouldGoToSequentialSublist (boolean activeRskip144) { if (!activeRskip144) return; - testBlockWithTxTxEdgesMatchAndRemascTxIsAtLastPosition(5, new short[]{ 2, 3, 4, 5 }, activeRskip144); + testBlockWithTxTxEdgesMatchAndRemascTxIsAtLastPosition(3, new short[]{ 2, 3 }, activeRskip144); } @ParameterizedTest diff --git a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java index d4ef5ae0f81..ff324b676b0 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/ParallelizeTransactionHandlerTest.java @@ -872,7 +872,7 @@ void writeSequentialAfterTwoParallelReadsAndAWriteShouldGoToSequential() { @Test void writeAfterWriteToSequentialForOutOfGasShouldGoToSequential() { Block executionBlock = mock(Block.class); - when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(5_000_000L).toByteArray()); + when(executionBlock.getGasLimit()).thenReturn(BigInteger.valueOf(3_000_000L).toByteArray()); HashSet setWithX = createASetAndAddKeys(aWrappedKey); From cb03dd4f69e71db00f44f669ff9e19ef888896d1 Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Thu, 8 Feb 2024 09:55:39 -0400 Subject: [PATCH 56/89] Fix failing test --- .../co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java index 6796ac836d3..54dc5955450 100644 --- a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java +++ b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java @@ -53,7 +53,7 @@ void blockWithRSKIP144Deactivated() { @Test void blockWithValidEdges() { - mockGetTxExecutionListsEdges(new short[]{2, 5, 6}, true); + mockGetTxExecutionListsEdges(new short[]{2, 5}, true); Assertions.assertTrue(rule.isValid(block)); } From ef9fdfb13d3ceb112bda5c12ec7d261f311f4f33 Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Thu, 8 Feb 2024 10:25:56 -0400 Subject: [PATCH 57/89] Code cleaning --- .../co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java index 54dc5955450..4a508e25102 100644 --- a/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java +++ b/rskj-core/src/test/java/co/rsk/validators/ValidTxExecutionSublistsEdgesTest.java @@ -12,9 +12,6 @@ import java.util.LinkedList; import java.util.List; -import static org.mockito.AdditionalMatchers.geq; -import static org.mockito.ArgumentMatchers.any; - class ValidTxExecutionSublistsEdgesTest { private BlockHeader blockHeader; From 30ebfb8328573ce8a5b46c470c5ccc0f6557c396 Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Fri, 16 Feb 2024 11:16:34 -0400 Subject: [PATCH 58/89] Cover cases with uneven division in getSublistGasLimit --- .../main/java/co/rsk/core/bc/BlockUtils.java | 15 +++++++++--- .../java/co/rsk/core/bc/BlockUtilsTest.java | 23 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java index 410fa938a72..b044e96dc3e 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java @@ -123,7 +123,12 @@ public static long getSublistGasLimit(Block block, boolean forSequentialTxSet, l int transactionExecutionThreadCount = Constants.getTransactionExecutionThreads(); if((transactionExecutionThreadCount + 1) * minSequentialSetGasLimit <= blockGasLimit) { - return blockGasLimit / (transactionExecutionThreadCount + 1); + long parallelTxSetGasLimit = blockGasLimit / (transactionExecutionThreadCount + 1); + + if (forSequentialTxSet) { + return blockGasLimit - (transactionExecutionThreadCount * parallelTxSetGasLimit); + } + return parallelTxSetGasLimit; } else { if (blockGasLimit <= minSequentialSetGasLimit) { if (forSequentialTxSet) { @@ -132,10 +137,14 @@ public static long getSublistGasLimit(Block block, boolean forSequentialTxSet, l return 0; } + long additionalGasLimit = (blockGasLimit - minSequentialSetGasLimit); + long parallelTxSetGasLimit = additionalGasLimit / (transactionExecutionThreadCount); + if (forSequentialTxSet) { - return minSequentialSetGasLimit; + long extraGasLimit = additionalGasLimit - (parallelTxSetGasLimit * transactionExecutionThreadCount); + return minSequentialSetGasLimit + extraGasLimit; } - return (blockGasLimit - minSequentialSetGasLimit) / (transactionExecutionThreadCount); + return parallelTxSetGasLimit; } } } diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java index f55c866cd17..3235c99bc97 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockUtilsTest.java @@ -186,12 +186,13 @@ void tooMuchProcessTime() { @Test void sublistGasLimit_ShouldDivideGasLimitEquallyAmongAllSets() { long minSequentialSetGasLimit = Constants.regtest().getMinSequentialSetGasLimit(); - long mockedBlockGasLimit = 10_000_000L; + long mockedBlockGasLimit = 9_000_000L; Block block = mock(Block.class); when(block.getGasLimit()).thenReturn(BigInteger.valueOf(mockedBlockGasLimit).toByteArray()); long expectedLimit = mockedBlockGasLimit / (Constants.getTransactionExecutionThreads() + 1); assertEquals(expectedLimit, BlockUtils.getSublistGasLimit(block, false, minSequentialSetGasLimit)); + assertEquals(expectedLimit, BlockUtils.getSublistGasLimit(block, true, minSequentialSetGasLimit)); } @Test @@ -201,7 +202,23 @@ void sublistGasLimit_ShouldAssignLessGasLimitToParallelSets() { Block block = mock(Block.class); when(block.getGasLimit()).thenReturn(BigInteger.valueOf(mockedBlockGasLimit).toByteArray()); - long expectedLimit = (mockedBlockGasLimit - minSequentialSetGasLimit) / (Constants.getTransactionExecutionThreads()); - assertEquals(expectedLimit, BlockUtils.getSublistGasLimit(block, false, minSequentialSetGasLimit)); + assertEquals(minSequentialSetGasLimit, BlockUtils.getSublistGasLimit(block, true, minSequentialSetGasLimit)); + + long expectedParallelLimit = (mockedBlockGasLimit - minSequentialSetGasLimit) / (Constants.getTransactionExecutionThreads()); + assertEquals(expectedParallelLimit, BlockUtils.getSublistGasLimit(block, false, minSequentialSetGasLimit)); + } + + @Test + void sublistGasLimit_ShouldAssignExtraGasLimitToSequentialSet() { + long minSequentialSetGasLimit = Constants.regtest().getMinSequentialSetGasLimit(); + long mockedBlockGasLimit = 10_000_010L; + Block block = mock(Block.class); + when(block.getGasLimit()).thenReturn(BigInteger.valueOf(mockedBlockGasLimit).toByteArray()); + + long expectedSequentialLimit = 3_333_338L; + assertEquals(expectedSequentialLimit, BlockUtils.getSublistGasLimit(block, true, minSequentialSetGasLimit)); + + long expectedParallelLimit = 3_333_336L; + assertEquals(expectedParallelLimit, BlockUtils.getSublistGasLimit(block, false, minSequentialSetGasLimit)); } } From 5161f65dbdc4ff8c7f2bb47475b39f12a213087d Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Fri, 16 Feb 2024 12:49:20 -0400 Subject: [PATCH 59/89] Add javadoc and inner comments --- .../main/java/co/rsk/core/bc/BlockUtils.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java index b044e96dc3e..f8a305799f2 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/BlockUtils.java @@ -118,28 +118,72 @@ public static List sortBlocksByNumber(List blocks) { .collect(Collectors.toList()); } + /** + * Calculate the gas limit of a sublist, depending on the sublist type (sequential and parallel), from the block + * gas limit. The distribution can be performed one of two ways: + * + * 1. The block gas limit is divided equally among all sublists. If the division was not even (results in a decimal + * number) then the extra gas limit gets added to the sequential sublist. + * + * 2. The sequential sublist gets assigned a fixed value, determined by minSequentialSetGasLimit and additional + * gas limit is calculated by subtracting minSequentialSetGasLimit form block gas limit, the result is then divided + * by the amount of transaction execution threads. If the division for the parallel sublists was not even (results + * in a decimal number) then the extra gas limit gets added to the sequential sublist. + * + * + * @param block the block to get the gas limit from + * @param forSequentialTxSet a boolean the indicates if the gas limit beign calculated is for a sequential + * sublist or a paralle one. + * @param minSequentialSetGasLimit The minimum gas limit value the sequential sublist can have, configured by + * network in {@link Constants}. + * + * @return set of ancestors block hashes + */ public static long getSublistGasLimit(Block block, boolean forSequentialTxSet, long minSequentialSetGasLimit) { long blockGasLimit = GasCost.toGas(block.getGasLimit()); int transactionExecutionThreadCount = Constants.getTransactionExecutionThreads(); + /* + This if determines which distribution approach will be performed. If the result of multiplying the minSequentialSetGasLimit + by transactionExecutionThreadCount + 1 (where transactionExecutionThreadCount is the parallel sublist count and + the + 1 represents the sequential sublist) is less than the block gas limit then the equal division approach is performed, + otherwise the second approach, where the parallel sublists get less gas limit than the sequential sublist, is executed. + */ if((transactionExecutionThreadCount + 1) * minSequentialSetGasLimit <= blockGasLimit) { long parallelTxSetGasLimit = blockGasLimit / (transactionExecutionThreadCount + 1); if (forSequentialTxSet) { + /* + Subtract the total parallel sublist gas limit (parallelTxSetGasLimit) from the block gas limit to get + the sequential sublist gas limit + the possible extra gas limit and return it. + */ return blockGasLimit - (transactionExecutionThreadCount * parallelTxSetGasLimit); } + return parallelTxSetGasLimit; } else { + // Check if the block gas limit is less than the sequential gas limit. if (blockGasLimit <= minSequentialSetGasLimit) { + /* + If this method execution is for a sequential sublist then return the total block gas limit. This will + skip the parallel sublist gas limit calculation since there will not be any gas limit left. + */ if (forSequentialTxSet) { return blockGasLimit; } + + // If this method execution is NOT for a sequential sublist then return 0. return 0; } long additionalGasLimit = (blockGasLimit - minSequentialSetGasLimit); long parallelTxSetGasLimit = additionalGasLimit / (transactionExecutionThreadCount); + /* + If this method execution is for a sequential sublist then calculate the possible extra gas limit by subtracting + the total parallel sublist gas limit (parallelTxSetGasLimit) from additionalGasLimit and add the result to + minSequentialSetGasLimit + */ if (forSequentialTxSet) { long extraGasLimit = additionalGasLimit - (parallelTxSetGasLimit * transactionExecutionThreadCount); return minSequentialSetGasLimit + extraGasLimit; From 8f9ed2a2d975acc5b199d9ea8b2c851eee5fa72c Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Tue, 16 Apr 2024 11:58:21 +0300 Subject: [PATCH 60/89] Fixed bridgeDelegateCall unit test --- rskj-core/src/test/resources/dsl/bridgeDelegateCall.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rskj-core/src/test/resources/dsl/bridgeDelegateCall.txt b/rskj-core/src/test/resources/dsl/bridgeDelegateCall.txt index 610b47b2ad1..369ddb28311 100644 --- a/rskj-core/src/test/resources/dsl/bridgeDelegateCall.txt +++ b/rskj-core/src/test/resources/dsl/bridgeDelegateCall.txt @@ -5,7 +5,7 @@ transaction_build tx01 receiverAddress 00 value 0 data 5f600060006000600073000000000000000000000000000000000100000663005b8d80f1 - gas 1200000 + gas 600000 build transaction_build tx02 @@ -13,7 +13,7 @@ transaction_build tx02 receiverAddress 00 value 0 data 5f600060006000600073000000000000000000000000000000000100000663005b8d80f4 - gas 1200000 + gas 600000 nonce 1 build @@ -22,7 +22,7 @@ transaction_build tx03 receiverAddress 00 value 0 data 5f600060006000600073000000000000000000000000000000000100000663005b8d80f2 - gas 1200000 + gas 600000 nonce 2 build @@ -31,7 +31,7 @@ transaction_build tx04 receiverAddress 00 value 0 data 5f600060006000600073000000000000000000000000000000000100000663005b8d80fa - gas 1200000 + gas 600000 nonce 3 build From 79547e00a62fdba2ae439ad9124d41c6fa68e89d Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Mon, 27 May 2024 15:20:46 +0300 Subject: [PATCH 61/89] Fixed sonar complains / Removed unused imports --- rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java | 5 +---- rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java | 1 - .../PrecompiledContractHasBeenCalledTest.java | 1 - rskj-core/src/test/java/co/rsk/peg/RskForksBridgeTest.java | 6 +++--- .../src/test/java/co/rsk/test/builders/BlockBuilder.java | 3 --- 5 files changed, 4 insertions(+), 12 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java b/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java index da2850794ac..c1e5a1b7905 100644 --- a/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java +++ b/rskj-core/src/test/java/co/rsk/cli/tools/CliToolsTest.java @@ -43,7 +43,6 @@ import com.fasterxml.jackson.databind.SerializationFeature; import com.typesafe.config.ConfigValueFactory; import org.ethereum.TestUtils; -import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.Block; import org.ethereum.core.BlockFactory; @@ -79,9 +78,7 @@ import static co.rsk.core.BlockDifficulty.ZERO; import static org.ethereum.TestUtils.generateBytesFromRandom; import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.mockito.Mockito.*; diff --git a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java index 3057c183fa4..ca0000fe990 100644 --- a/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java +++ b/rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java @@ -31,7 +31,6 @@ import org.ethereum.crypto.HashUtil; import org.ethereum.util.RLP; import org.ethereum.util.RLPList; -import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java index 7bf5223d6a8..2a60ee1b099 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/transactionexecutor/PrecompiledContractHasBeenCalledTest.java @@ -19,7 +19,6 @@ import org.ethereum.datasource.HashMapDB; import org.ethereum.db.BlockStoreDummy; import org.ethereum.db.MutableRepository; -import org.ethereum.vm.GasCost; import org.ethereum.vm.PrecompiledContracts; import org.ethereum.vm.program.ProgramResult; import org.ethereum.vm.program.invoke.ProgramInvokeFactoryImpl; diff --git a/rskj-core/src/test/java/co/rsk/peg/RskForksBridgeTest.java b/rskj-core/src/test/java/co/rsk/peg/RskForksBridgeTest.java index 219108f687f..a3eeaa2af8c 100644 --- a/rskj-core/src/test/java/co/rsk/peg/RskForksBridgeTest.java +++ b/rskj-core/src/test/java/co/rsk/peg/RskForksBridgeTest.java @@ -18,14 +18,14 @@ package co.rsk.peg; -import co.rsk.bitcoinj.core.*; -import co.rsk.bitcoinj.params.RegTestParams; -import co.rsk.peg.constants.BridgeRegTestConstants; +import co.rsk.bitcoinj.core.AddressFormatException; +import co.rsk.bitcoinj.core.Coin; import co.rsk.config.TestSystemProperties; import co.rsk.core.RskAddress; import co.rsk.core.TransactionExecutorFactory; import co.rsk.core.bc.BlockChainImpl; import co.rsk.db.RepositoryLocator; +import co.rsk.peg.constants.BridgeRegTestConstants; import co.rsk.test.World; import co.rsk.test.builders.BlockBuilder; import co.rsk.trie.TrieStore; diff --git a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java index 0d3f95ae8bc..073ca769464 100644 --- a/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java +++ b/rskj-core/src/test/java/co/rsk/test/builders/BlockBuilder.java @@ -29,9 +29,6 @@ import co.rsk.peg.BridgeSupportFactory; import co.rsk.trie.TrieStore; import org.bouncycastle.util.BigIntegers; -import org.ethereum.config.Constants; -import org.ethereum.config.blockchain.upgrades.ActivationConfigsForTest; -import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.*; import org.ethereum.datasource.HashMapDB; import org.ethereum.db.BlockStore; From f71431615d8d7a4c85d93fc85afef6a5a7ca255f Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Tue, 11 Jun 2024 09:12:29 -0300 Subject: [PATCH 62/89] Adding custom config --- .../resources/pte-integration-test-rskj.conf | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf diff --git a/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf b/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf new file mode 100644 index 00000000000..231d9f58c0d --- /dev/null +++ b/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf @@ -0,0 +1,43 @@ +miner { + server.enabled = true + client { + enabled = true + delayBetweenBlocks = 5 second + } +} + +blockchain.config { + consensusRules = { + rskip290 = -1 # Difficulty Drop + rskip351 = 4901360 # BHC + rskip144 = 4901360 # PTE + } +} + +peer { + + discovery = { + + # if peer discovery is off + # the peer window will show + # only what retrieved by active + # peer [true/false] + enabled = false + + # List of the peers to start + # the search of the online peers + # values: [ip:port] + ip.list = [] + + } + +} + +# wallet { +# enabled = true +# accounts = [ +# # Test Accounts' Private Keys +# { privateKey = d71dd02719de250de67926d059d759999e67d6485c0ce80488efb60d3006c1e5 }, # Address 0xCA734980E7c94E7083E0D18a9FA4dcc2b76d7A48 +# { privateKey = e46878e7c50fc979ed2ad667b62f0ace38524c06a09e680fb63da2b6405c3bb2 } # Address 0x1422EAc7DB1431EaFE2a07338985DE36EC1Df3FA +# ] +# } \ No newline at end of file From e389b5a29eb0d43ee3aa23da5d00c4901802348d Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Tue, 11 Jun 2024 09:13:19 -0300 Subject: [PATCH 63/89] Adding test (wip) --- .../java/misc/PteIntegrationTest.java | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java diff --git a/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java b/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java new file mode 100644 index 00000000000..abed9e140af --- /dev/null +++ b/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java @@ -0,0 +1,242 @@ +package misc; + +import co.rsk.util.HexUtils; +import co.rsk.util.OkHttpClientTestFixture; +import co.rsk.util.cli.CommandLineFixture; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.squareup.okhttp.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +public class PteIntegrationTest { + + private static final int RPC_PORT = 9999; + private final ObjectMapper objectMapper = new ObjectMapper(); + + private String buildLibsPath; + private String jarName; + private String databaseDir; + private String bloomsDbDir; + private String[] baseArgs; + private String strBaseArgs; + private String baseJavaCmd; + + @TempDir + private Path tempDir; + + @BeforeEach + public void setup() throws IOException { + String projectPath = System.getProperty("user.dir"); + buildLibsPath = String.format("%s/build/libs", projectPath); + String integrationTestResourcesPath = String.format("%s/src/integrationTest/resources", projectPath); + String logbackXmlFile = String.format("%s/logback.xml", integrationTestResourcesPath); + String rskConfFile = String.format("%s/pte-integration-test-rskj.conf", integrationTestResourcesPath); + Stream pathsStream = Files.list(Paths.get(buildLibsPath)); + jarName = pathsStream.filter(p -> !p.toFile().isDirectory()) + .map(p -> p.getFileName().toString()) + .filter(fn -> fn.endsWith("-all.jar")) + .findFirst() + .get(); + Path databaseDirPath = tempDir.resolve("database"); + databaseDir = databaseDirPath.toString(); + bloomsDbDir = databaseDirPath.resolve("blooms").toString(); + baseArgs = new String[]{ + String.format("-Xdatabase.dir=%s", databaseDir), + "--regtest", + "-Xkeyvalue.datasource=leveldb", + String.format("-Xrpc.providers.web.http.port=%s", RPC_PORT) + }; + strBaseArgs = String.join(" ", baseArgs); + baseJavaCmd = String.format("java %s %s", String.format("-Dlogback.configurationFile=%s", logbackXmlFile), String.format("-Drsk.conf.file=%s", rskConfFile)); + } + + @Test + void whenTest_shouldTest() throws Exception { + + /* + Regtest's Pre-funded Test Accounts + + 0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826 + 0x7986b3df570230288501eea3d890bd66948c9b79 + 0x0a3aa774752ec2042c46548456c094a76c7f3a79 + 0xcf7cdbbb5f7ba79d3ffe74a0bba13fc0295f6036 + 0x39b12c05e8503356e3a7df0b7b33efa4c054c409 + 0xc354d97642faa06781b76ffb6786f72cd7746c97 + 0xdebe71e1de41fc77c44df4b6db940026e31b0e71 + 0x7857288e171c6159c5576d1bd9ac40c0c48a771c + 0xa4dea4d5c954f5fd9e87f0e9752911e83a3d18b3 + 0x09a1eda29f664ac8f68106f6567276df0c65d859 + 0xec4ddeb4380ad69b3e509baad9f158cdf4e4681d + */ + + Map txResponseMap = new HashMap<>(); + + String cmd = String.format("%s -cp %s/%s co.rsk.Start --reset %s", baseJavaCmd, buildLibsPath, jarName, strBaseArgs); + + CommandLineFixture.runCommand( + cmd, + 1, + TimeUnit.MINUTES, + proc -> { + try { + Response txResponse = sendBulkTransactions( + "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", + "0x7986b3df570230288501eea3d890bd66948c9b79", + "0x0a3aa774752ec2042c46548456c094a76c7f3a79", + "0xcf7cdbbb5f7ba79d3ffe74a0bba13fc0295f6036", + "0x39b12c05e8503356e3a7df0b7b33efa4c054c409", + "0xc354d97642faa06781b76ffb6786f72cd7746c97", + "0xdebe71e1de41fc77c44df4b6db940026e31b0e71", + "0x7857288e171c6159c5576d1bd9ac40c0c48a771c" + ); + txResponseMap.put("txResponse", txResponse); + + String number = ""; + for (int i = 0; i < 5; i++) { + System.out.println(i); + Response response = getBestBlock(); + Map blockResponseMap = new HashMap<>(); + blockResponseMap.put("latestProcessedBlock", response); + + String blockResponseBody = blockResponseMap.get("latestProcessedBlock").body().string(); + JsonNode jsonRpcResponse = objectMapper.readTree(blockResponseBody); + // JsonNode transactionsNode = jsonRpcResponse.get(0).get("result").get("transactions"); + JsonNode block = jsonRpcResponse.get(0).get("result"); + JsonNode txs = block.get("transactions"); + + if (!number.equals(block.get("number").asText())) { + number = block.get("number").asText(); + System.out.println("different"); + } else { + System.out.println("same"); + } + + if (txs.size() > 1) { + Assertions.assertEquals("", txs); + } + + // Sleep here, or wait for n blocks and then assert + + } + + } catch (IOException e) { + Assertions.fail(e); + } + } + ); + + /* + String blockResponseBody = blockResponseMap.get("latestProcessedBlock").body().string(); + JsonNode jsonRpcResponse = objectMapper.readTree(blockResponseBody); + // JsonNode transactionsNode = jsonRpcResponse.get(0).get("result").get("transactions"); + JsonNode transactionsNode = jsonRpcResponse.get(0).get("result"); + + Assertions.assertEquals("", transactionsNode); + */ + + /* + long blockNumber = HexUtils.jsonHexToLong(transactionsNode.get(0).get("blockNumber").asText()); + + Assertions.assertEquals(null , txResponseMap); + Assertions.assertEquals(1, blockNumber); + */ + } + + private Response getBestBlock() throws IOException { + String content = "[{\n" + + " \"method\": \"eth_getBlockByNumber\",\n" + + " \"params\": [\n" + + " \"latest\",\n" + + " true\n" + + " ],\n" + + " \"id\": 1,\n" + + " \"jsonrpc\": \"2.0\"\n" + + "}]"; + + return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); + } + + private Response getAccounts() throws IOException { + String content = "[{\n" + + " \"method\": \"eth_accounts\",\n" + + " \"params\": [],\n" + + " \"id\": 1,\n" + + " \"jsonrpc\": \"2.0\"\n" + + "}]"; + + return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); + } + + private Response sendBulkTransactions(String address1, String address2, + String address3, String address4, + String address5, String address6, + String address7, String address8) throws IOException { + String content = "[\n" + + "{\n" + + " \"jsonrpc\": \"2.0\",\n" + + " \"method\": \"eth_sendTransaction\",\n" + + " \"id\": 1,\n" + + " \"params\": [{\n" + + " \"from\": \"" + address1 + "\",\n" + + " \"to\": \"" + address2 + "\",\n" + + " \"gas\": \"0x9C40\",\n" + + " \"gasPrice\": \"0x1\",\n" + + " \"value\": \"0x500\"\n" + + " }]\n" + + "},\n" + + "{\n" + + " \"jsonrpc\": \"2.0\",\n" + + " \"method\": \"eth_sendTransaction\",\n" + + " \"id\": 1,\n" + + " \"params\": [{\n" + + " \"from\": \"" + address3 + "\",\n" + + " \"to\": \"" + address4 + "\",\n" + + " \"gas\": \"0x9C40\",\n" + + " \"gasPrice\": \"0x1\",\n" + + " \"value\": \"0x500\"\n" + + " }]\n" + + "},\n" + + "{\n" + + " \"jsonrpc\": \"2.0\",\n" + + " \"method\": \"eth_sendTransaction\",\n" + + " \"id\": 1,\n" + + " \"params\": [{\n" + + " \"from\": \"" + address5 + "\",\n" + + " \"to\": \"" + address6 + "\",\n" + + " \"gas\": \"0x9C40\",\n" + + " \"gasPrice\": \"0x1\",\n" + + " \"value\": \"0x500\"\n" + + " }]\n" + + "},\n" + + "{\n" + + " \"jsonrpc\": \"2.0\",\n" + + " \"method\": \"eth_sendTransaction\",\n" + + " \"id\": 1,\n" + + " \"params\": [{\n" + + " \"from\": \"" + address7 + "\",\n" + + " \"to\": \"" + address8 + "\",\n" + + " \"gas\": \"0x9C40\",\n" + + " \"gasPrice\": \"0x1\",\n" + + " \"value\": \"0x500\"\n" + + " }]\n" + + "}\n" + + "]"; + + return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); + } + +} From 66cd486a907081ca1c83631a33394239d51711b3 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Wed, 12 Jun 2024 11:44:42 -0300 Subject: [PATCH 64/89] Adding pte edges field --- .../java/org/ethereum/rpc/dto/BlockResultDTO.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/rpc/dto/BlockResultDTO.java b/rskj-core/src/main/java/org/ethereum/rpc/dto/BlockResultDTO.java index 1acf8aaad7f..d529bee160c 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/dto/BlockResultDTO.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/dto/BlockResultDTO.java @@ -64,6 +64,7 @@ public class BlockResultDTO { private final String hashForMergedMining; private final String paidFees; private final String cumulativeDifficulty; + private final short[] rskPteEdges; private BlockResultDTO( Long number, @@ -90,7 +91,8 @@ private BlockResultDTO( byte[] bitcoinMergedMiningCoinbaseTransaction, byte[] bitcoinMergedMiningMerkleProof, byte[] hashForMergedMining, - Coin paidFees) { + Coin paidFees, + short[] rskPteEdges) { this.number = number != null ? HexUtils.toQuantityJsonHex(number) : null; this.hash = hash != null ? hash.toJsonString() : null; this.parentHash = parentHash.toJsonString(); @@ -120,6 +122,8 @@ private BlockResultDTO( this.bitcoinMergedMiningMerkleProof = HexUtils.toUnformattedJsonHex(bitcoinMergedMiningMerkleProof); this.hashForMergedMining = HexUtils.toUnformattedJsonHex(hashForMergedMining); this.paidFees = paidFees != null ? HexUtils.toQuantityJsonHex(paidFees.getBytes()) : null; + + this.rskPteEdges = rskPteEdges; } public static BlockResultDTO fromBlock(Block b, boolean fullTx, BlockStore blockStore, boolean skipRemasc, boolean zeroSignatureIfRemasc, SignatureCache signatureCache) { @@ -177,7 +181,8 @@ public static BlockResultDTO fromBlock(Block b, boolean fullTx, BlockStore block b.getBitcoinMergedMiningCoinbaseTransaction(), b.getBitcoinMergedMiningMerkleProof(), b.getHashForMergedMining(), - b.getFeesPaidToMiner() + b.getFeesPaidToMiner(), + b.getHeader().getTxExecutionSublistsEdges() ); } @@ -290,4 +295,8 @@ public String getHashForMergedMining() { public String getPaidFees() { return paidFees; } + + public short[] getRskPteEdges() { + return rskPteEdges; + } } \ No newline at end of file From e299f568c66d2f2d8071c9e70b6739c9d5003b03 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Wed, 12 Jun 2024 20:59:19 -0300 Subject: [PATCH 65/89] Updating test WIP --- .../java/misc/PteIntegrationTest.java | 142 ++++++++++++++---- .../resources/pte-integration-test-rskj.conf | 17 +-- 2 files changed, 114 insertions(+), 45 deletions(-) diff --git a/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java b/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java index abed9e140af..5ecb2c938b8 100644 --- a/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java +++ b/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java @@ -16,14 +16,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; +import java.util.*; +import java.util.concurrent.*; import java.util.stream.Stream; public class PteIntegrationTest { + // THIS TEST REQUIRES A JAR FILE TO BE IN THE CORRECT PLACE BEFORE RUNNING + private static final int RPC_PORT = 9999; private final ObjectMapper objectMapper = new ObjectMapper(); @@ -83,6 +83,20 @@ void whenTest_shouldTest() throws Exception { 0xec4ddeb4380ad69b3e509baad9f158cdf4e4681d */ + List accounts = Arrays.asList( + "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", + "0x7986b3df570230288501eea3d890bd66948c9b79", + "0x0a3aa774752ec2042c46548456c094a76c7f3a79", + "0xcf7cdbbb5f7ba79d3ffe74a0bba13fc0295f6036", + "0x39b12c05e8503356e3a7df0b7b33efa4c054c409", + "0xc354d97642faa06781b76ffb6786f72cd7746c97", + "0xdebe71e1de41fc77c44df4b6db940026e31b0e71", + "0x7857288e171c6159c5576d1bd9ac40c0c48a771c", + "0xa4dea4d5c954f5fd9e87f0e9752911e83a3d18b3", + "0x09a1eda29f664ac8f68106f6567276df0c65d859", + "0xec4ddeb4380ad69b3e509baad9f158cdf4e4681d" + ); + Map txResponseMap = new HashMap<>(); String cmd = String.format("%s -cp %s/%s co.rsk.Start --reset %s", baseJavaCmd, buildLibsPath, jarName, strBaseArgs); @@ -93,6 +107,19 @@ void whenTest_shouldTest() throws Exception { TimeUnit.MINUTES, proc -> { try { + + // Check balances + + List balances = new ArrayList<>(); + + for (String account : accounts) { + balances.add(getBalance(account).body().string()); + } + + Assertions.assertEquals(null, balances); + + // Send bulk transactions + Response txResponse = sendBulkTransactions( "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", "0x7986b3df570230288501eea3d890bd66948c9b79", @@ -105,32 +132,21 @@ void whenTest_shouldTest() throws Exception { ); txResponseMap.put("txResponse", txResponse); - String number = ""; - for (int i = 0; i < 5; i++) { - System.out.println(i); - Response response = getBestBlock(); - Map blockResponseMap = new HashMap<>(); - blockResponseMap.put("latestProcessedBlock", response); + // Await for n blocks to be mined and return them + + Future> future = getBlocksAsync(); - String blockResponseBody = blockResponseMap.get("latestProcessedBlock").body().string(); - JsonNode jsonRpcResponse = objectMapper.readTree(blockResponseBody); - // JsonNode transactionsNode = jsonRpcResponse.get(0).get("result").get("transactions"); - JsonNode block = jsonRpcResponse.get(0).get("result"); - JsonNode txs = block.get("transactions"); + try { - if (!number.equals(block.get("number").asText())) { - number = block.get("number").asText(); - System.out.println("different"); - } else { - System.out.println("same"); - } + Map result = future.get(); - if (txs.size() > 1) { - Assertions.assertEquals("", txs); - } + // TODO Check transaction edges on each block - // Sleep here, or wait for n blocks and then assert + Assertions.assertEquals(null, result); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + Assertions.fail(e); } } catch (IOException e) { @@ -156,12 +172,12 @@ void whenTest_shouldTest() throws Exception { */ } - private Response getBestBlock() throws IOException { + private Response getBalance(String account) throws IOException { String content = "[{\n" + - " \"method\": \"eth_getBlockByNumber\",\n" + + " \"method\": \"eth_getBalance\",\n" + " \"params\": [\n" + - " \"latest\",\n" + - " true\n" + + " \"" + account + "\",\n" + + " \"latest\"\n" + " ],\n" + " \"id\": 1,\n" + " \"jsonrpc\": \"2.0\"\n" + @@ -170,10 +186,13 @@ private Response getBestBlock() throws IOException { return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); } - private Response getAccounts() throws IOException { + private Response getBlockByNumber(String number) throws IOException { String content = "[{\n" + - " \"method\": \"eth_accounts\",\n" + - " \"params\": [],\n" + + " \"method\": \"eth_getBlockByNumber\",\n" + + " \"params\": [\n" + + " \"" + number + "\",\n" + + " true\n" + + " ],\n" + " \"id\": 1,\n" + " \"jsonrpc\": \"2.0\"\n" + "}]"; @@ -239,4 +258,63 @@ private Response sendBulkTransactions(String address1, String address2, return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); } + private Future> getBlocksAsync() { + CompletableFuture> completableFuture = new CompletableFuture<>(); + + Executors.newCachedThreadPool().submit(() -> { + Map results = new HashMap<>(); + + for (int i = 0; i < 9; i++) { + String response = getBlockByNumber("0x" + i).body().string(); + + results.put(i, response); + Thread.sleep(500); + } + + completableFuture.complete(results); + return null; + }); + + return completableFuture; + } + } + +/* + + Map blockResponseMap = new HashMap<>(); + blockResponseMap.put("latestProcessedBlock", response); + + String blockResponseBody = blockResponseMap.get("latestProcessedBlock").body().string(); + JsonNode jsonRpcResponse = objectMapper.readTree(blockResponseBody); + JsonNode block = jsonRpcResponse.get(0).get("result"); + JsonNode txs = block.get("transactions"); + + String number = ""; + for (int i = 0; i < 5; i++) { + System.out.println(i); + Response response = getBestBlock(); + Map blockResponseMap = new HashMap<>(); + blockResponseMap.put("latestProcessedBlock", response); + + String blockResponseBody = blockResponseMap.get("latestProcessedBlock").body().string(); + JsonNode jsonRpcResponse = objectMapper.readTree(blockResponseBody); + // JsonNode transactionsNode = jsonRpcResponse.get(0).get("result").get("transactions"); + JsonNode block = jsonRpcResponse.get(0).get("result"); + JsonNode txs = block.get("transactions"); + + if (!number.equals(block.get("number").asText())) { + number = block.get("number").asText(); + System.out.println("different"); + System.out.println(number); + } else { + System.out.println("same"); + } + + if (txs.size() > 1) { + Assertions.assertEquals("", txs); + } + + // Sleep here, or wait for n blocks and then assert + } + */ diff --git a/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf b/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf index 231d9f58c0d..8641482e7a1 100644 --- a/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf +++ b/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf @@ -8,9 +8,9 @@ miner { blockchain.config { consensusRules = { - rskip290 = -1 # Difficulty Drop - rskip351 = 4901360 # BHC - rskip144 = 4901360 # PTE + rskip290 = -1 # Difficulty Drop + rskip351 = -1 # BHC + rskip144 = -1 # PTE } } @@ -31,13 +31,4 @@ peer { } -} - -# wallet { -# enabled = true -# accounts = [ -# # Test Accounts' Private Keys -# { privateKey = d71dd02719de250de67926d059d759999e67d6485c0ce80488efb60d3006c1e5 }, # Address 0xCA734980E7c94E7083E0D18a9FA4dcc2b76d7A48 -# { privateKey = e46878e7c50fc979ed2ad667b62f0ace38524c06a09e680fb63da2b6405c3bb2 } # Address 0x1422EAc7DB1431EaFE2a07338985DE36EC1Df3FA -# ] -# } \ No newline at end of file +} \ No newline at end of file From 18f2c0f5132cc387d1b3de858f9cda8b6155e326 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Sun, 16 Jun 2024 23:48:16 -0300 Subject: [PATCH 66/89] Updating tests --- .../java/misc/PteIntegrationTest.java | 238 +++++++----------- .../resources/pte-integration-test-rskj.conf | 18 +- .../ethereum/rpc/dto/BlockResultDTOTest.java | 1 + 3 files changed, 95 insertions(+), 162 deletions(-) diff --git a/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java b/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java index 5ecb2c938b8..2616f0274c8 100644 --- a/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java +++ b/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java @@ -1,6 +1,5 @@ package misc; -import co.rsk.util.HexUtils; import co.rsk.util.OkHttpClientTestFixture; import co.rsk.util.cli.CommandLineFixture; import com.fasterxml.jackson.databind.JsonNode; @@ -11,7 +10,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -25,13 +23,12 @@ public class PteIntegrationTest { // THIS TEST REQUIRES A JAR FILE TO BE IN THE CORRECT PLACE BEFORE RUNNING private static final int RPC_PORT = 9999; + private static final int MAX_BLOCKS_TO_GET = 20; + private final ObjectMapper objectMapper = new ObjectMapper(); private String buildLibsPath; private String jarName; - private String databaseDir; - private String bloomsDbDir; - private String[] baseArgs; private String strBaseArgs; private String baseJavaCmd; @@ -50,14 +47,13 @@ public void setup() throws IOException { .map(p -> p.getFileName().toString()) .filter(fn -> fn.endsWith("-all.jar")) .findFirst() - .get(); + .orElse(""); + Path databaseDirPath = tempDir.resolve("database"); - databaseDir = databaseDirPath.toString(); - bloomsDbDir = databaseDirPath.resolve("blooms").toString(); - baseArgs = new String[]{ + String databaseDir = databaseDirPath.toString(); + String[] baseArgs = new String[]{ String.format("-Xdatabase.dir=%s", databaseDir), "--regtest", - "-Xkeyvalue.datasource=leveldb", String.format("-Xrpc.providers.web.http.port=%s", RPC_PORT) }; strBaseArgs = String.join(" ", baseArgs); @@ -65,24 +61,11 @@ public void setup() throws IOException { } @Test - void whenTest_shouldTest() throws Exception { - - /* - Regtest's Pre-funded Test Accounts - - 0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826 - 0x7986b3df570230288501eea3d890bd66948c9b79 - 0x0a3aa774752ec2042c46548456c094a76c7f3a79 - 0xcf7cdbbb5f7ba79d3ffe74a0bba13fc0295f6036 - 0x39b12c05e8503356e3a7df0b7b33efa4c054c409 - 0xc354d97642faa06781b76ffb6786f72cd7746c97 - 0xdebe71e1de41fc77c44df4b6db940026e31b0e71 - 0x7857288e171c6159c5576d1bd9ac40c0c48a771c - 0xa4dea4d5c954f5fd9e87f0e9752911e83a3d18b3 - 0x09a1eda29f664ac8f68106f6567276df0c65d859 - 0xec4ddeb4380ad69b3e509baad9f158cdf4e4681d - */ + void whenParallelizableTransactionsAreSent_someAreExecutedInParallel() throws Exception { + + // Given + // Pre-funded Test Accounts on Regtest List accounts = Arrays.asList( "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", "0x7986b3df570230288501eea3d890bd66948c9b79", @@ -98,8 +81,16 @@ void whenTest_shouldTest() throws Exception { ); Map txResponseMap = new HashMap<>(); + Map> blocksResponseMap = new HashMap<>(); - String cmd = String.format("%s -cp %s/%s co.rsk.Start --reset %s", baseJavaCmd, buildLibsPath, jarName, strBaseArgs); + String cmd = String.format( + "%s -cp %s/%s co.rsk.Start --reset %s", + baseJavaCmd, + buildLibsPath, + jarName, + strBaseArgs); + + // When CommandLineFixture.runCommand( cmd, @@ -108,29 +99,13 @@ void whenTest_shouldTest() throws Exception { proc -> { try { - // Check balances - - List balances = new ArrayList<>(); - - for (String account : accounts) { - balances.add(getBalance(account).body().string()); - } - - Assertions.assertEquals(null, balances); - // Send bulk transactions Response txResponse = sendBulkTransactions( - "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", - "0x7986b3df570230288501eea3d890bd66948c9b79", - "0x0a3aa774752ec2042c46548456c094a76c7f3a79", - "0xcf7cdbbb5f7ba79d3ffe74a0bba13fc0295f6036", - "0x39b12c05e8503356e3a7df0b7b33efa4c054c409", - "0xc354d97642faa06781b76ffb6786f72cd7746c97", - "0xdebe71e1de41fc77c44df4b6db940026e31b0e71", - "0x7857288e171c6159c5576d1bd9ac40c0c48a771c" - ); - txResponseMap.put("txResponse", txResponse); + accounts.get(0), accounts.get(1), accounts.get(2), accounts.get(3), + accounts.get(4), accounts.get(5), accounts.get(6), accounts.get(7)); + + txResponseMap.put("bulkTransactionsResponse", txResponse); // Await for n blocks to be mined and return them @@ -138,11 +113,7 @@ void whenTest_shouldTest() throws Exception { try { - Map result = future.get(); - - // TODO Check transaction edges on each block - - Assertions.assertEquals(null, result); + blocksResponseMap.put("asyncBlocksResult", future.get()); } catch (ExecutionException | InterruptedException e) { e.printStackTrace(); @@ -155,35 +126,33 @@ void whenTest_shouldTest() throws Exception { } ); - /* - String blockResponseBody = blockResponseMap.get("latestProcessedBlock").body().string(); - JsonNode jsonRpcResponse = objectMapper.readTree(blockResponseBody); - // JsonNode transactionsNode = jsonRpcResponse.get(0).get("result").get("transactions"); - JsonNode transactionsNode = jsonRpcResponse.get(0).get("result"); + // Then - Assertions.assertEquals("", transactionsNode); - */ + Assertions.assertEquals(200, txResponseMap.get("bulkTransactionsResponse").code()); - /* - long blockNumber = HexUtils.jsonHexToLong(transactionsNode.get(0).get("blockNumber").asText()); + Map blocksResult = blocksResponseMap.get("asyncBlocksResult"); + Assertions.assertEquals(MAX_BLOCKS_TO_GET, blocksResult.size()); - Assertions.assertEquals(null , txResponseMap); - Assertions.assertEquals(1, blockNumber); - */ - } + boolean pteFound = false; + int i = blocksResult.size(); // Start from the last element (optimization) + while (!pteFound && i >= 0) { + i -= 1; - private Response getBalance(String account) throws IOException { - String content = "[{\n" + - " \"method\": \"eth_getBalance\",\n" + - " \"params\": [\n" + - " \"" + account + "\",\n" + - " \"latest\"\n" + - " ],\n" + - " \"id\": 1,\n" + - " \"jsonrpc\": \"2.0\"\n" + - "}]"; + JsonNode blockResponse = objectMapper.readTree(blocksResult.get(i)); + JsonNode result = blockResponse.get(0).get("result"); + + if (!result.isNull()) { + JsonNode pteEdges = result.get("rskPteEdges"); + if (pteEdges.isArray() && pteEdges.size() > 0) { + Assertions.assertTrue(result.get("transactions").isArray()); + Assertions.assertTrue(result.get("transactions").size() > 1); + pteFound = true; + } + } + } + + Assertions.assertTrue(pteFound); - return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); } private Response getBlockByNumber(String number) throws IOException { @@ -200,20 +169,42 @@ private Response getBlockByNumber(String number) throws IOException { return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); } - private Response sendBulkTransactions(String address1, String address2, - String address3, String address4, - String address5, String address6, - String address7, String address8) throws IOException { + private Future> getBlocksAsync() { + CompletableFuture> completableFuture = new CompletableFuture<>(); + + Executors.newCachedThreadPool().submit(() -> { + Map results = new HashMap<>(); + + for (int i = 0; i < MAX_BLOCKS_TO_GET; i++) { + String response = getBlockByNumber("0x" + String.format("%02x", i)).body().string(); + + results.put(i, response); + Thread.sleep(500); + } + + completableFuture.complete(results); + return null; + }); + + return completableFuture; + } + + private Response sendBulkTransactions( + String addressFrom1, String addressTo1, + String addressFrom2, String addressTo2, + String addressFrom3, String addressTo3, + String addressFrom4, String addressTo4) throws IOException { + String content = "[\n" + "{\n" + " \"jsonrpc\": \"2.0\",\n" + " \"method\": \"eth_sendTransaction\",\n" + " \"id\": 1,\n" + " \"params\": [{\n" + - " \"from\": \"" + address1 + "\",\n" + - " \"to\": \"" + address2 + "\",\n" + + " \"from\": \"" + addressFrom1 + "\",\n" + + " \"to\": \"" + addressTo1 + "\",\n" + " \"gas\": \"0x9C40\",\n" + - " \"gasPrice\": \"0x1\",\n" + + " \"gasPrice\": \"0x10\",\n" + " \"value\": \"0x500\"\n" + " }]\n" + "},\n" + @@ -222,10 +213,10 @@ private Response sendBulkTransactions(String address1, String address2, " \"method\": \"eth_sendTransaction\",\n" + " \"id\": 1,\n" + " \"params\": [{\n" + - " \"from\": \"" + address3 + "\",\n" + - " \"to\": \"" + address4 + "\",\n" + + " \"from\": \"" + addressFrom2 + "\",\n" + + " \"to\": \"" + addressTo2 + "\",\n" + " \"gas\": \"0x9C40\",\n" + - " \"gasPrice\": \"0x1\",\n" + + " \"gasPrice\": \"0x10\",\n" + " \"value\": \"0x500\"\n" + " }]\n" + "},\n" + @@ -234,10 +225,10 @@ private Response sendBulkTransactions(String address1, String address2, " \"method\": \"eth_sendTransaction\",\n" + " \"id\": 1,\n" + " \"params\": [{\n" + - " \"from\": \"" + address5 + "\",\n" + - " \"to\": \"" + address6 + "\",\n" + + " \"from\": \"" + addressFrom3 + "\",\n" + + " \"to\": \"" + addressTo3 + "\",\n" + " \"gas\": \"0x9C40\",\n" + - " \"gasPrice\": \"0x1\",\n" + + " \"gasPrice\": \"0x10\",\n" + " \"value\": \"0x500\"\n" + " }]\n" + "},\n" + @@ -246,10 +237,10 @@ private Response sendBulkTransactions(String address1, String address2, " \"method\": \"eth_sendTransaction\",\n" + " \"id\": 1,\n" + " \"params\": [{\n" + - " \"from\": \"" + address7 + "\",\n" + - " \"to\": \"" + address8 + "\",\n" + + " \"from\": \"" + addressFrom4 + "\",\n" + + " \"to\": \"" + addressTo4 + "\",\n" + " \"gas\": \"0x9C40\",\n" + - " \"gasPrice\": \"0x1\",\n" + + " \"gasPrice\": \"0x10\",\n" + " \"value\": \"0x500\"\n" + " }]\n" + "}\n" + @@ -258,63 +249,4 @@ private Response sendBulkTransactions(String address1, String address2, return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); } - private Future> getBlocksAsync() { - CompletableFuture> completableFuture = new CompletableFuture<>(); - - Executors.newCachedThreadPool().submit(() -> { - Map results = new HashMap<>(); - - for (int i = 0; i < 9; i++) { - String response = getBlockByNumber("0x" + i).body().string(); - - results.put(i, response); - Thread.sleep(500); - } - - completableFuture.complete(results); - return null; - }); - - return completableFuture; - } - -} - -/* - - Map blockResponseMap = new HashMap<>(); - blockResponseMap.put("latestProcessedBlock", response); - - String blockResponseBody = blockResponseMap.get("latestProcessedBlock").body().string(); - JsonNode jsonRpcResponse = objectMapper.readTree(blockResponseBody); - JsonNode block = jsonRpcResponse.get(0).get("result"); - JsonNode txs = block.get("transactions"); - - String number = ""; - for (int i = 0; i < 5; i++) { - System.out.println(i); - Response response = getBestBlock(); - Map blockResponseMap = new HashMap<>(); - blockResponseMap.put("latestProcessedBlock", response); - - String blockResponseBody = blockResponseMap.get("latestProcessedBlock").body().string(); - JsonNode jsonRpcResponse = objectMapper.readTree(blockResponseBody); - // JsonNode transactionsNode = jsonRpcResponse.get(0).get("result").get("transactions"); - JsonNode block = jsonRpcResponse.get(0).get("result"); - JsonNode txs = block.get("transactions"); - - if (!number.equals(block.get("number").asText())) { - number = block.get("number").asText(); - System.out.println("different"); - System.out.println(number); - } else { - System.out.println("same"); - } - - if (txs.size() > 1) { - Assertions.assertEquals("", txs); - } - - // Sleep here, or wait for n blocks and then assert - } - */ +} \ No newline at end of file diff --git a/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf b/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf index 8641482e7a1..3e6f988dfb3 100644 --- a/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf +++ b/rskj-core/src/integrationTest/resources/pte-integration-test-rskj.conf @@ -1,17 +1,17 @@ miner { - server.enabled = true - client { + + server { enabled = true - delayBetweenBlocks = 5 second + updateWorkOnNewTransaction = true } -} -blockchain.config { - consensusRules = { - rskip290 = -1 # Difficulty Drop - rskip351 = -1 # BHC - rskip144 = -1 # PTE + client { + enabled = true + automine = false + delayBetweenBlocks = 5 seconds + delayBetweenRefreshes = 1 second } + } peer { diff --git a/rskj-core/src/test/java/org/ethereum/rpc/dto/BlockResultDTOTest.java b/rskj-core/src/test/java/org/ethereum/rpc/dto/BlockResultDTOTest.java index b5eee54f482..9b2677db62b 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/dto/BlockResultDTOTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/dto/BlockResultDTOTest.java @@ -142,6 +142,7 @@ void getBlockResultDTOWithoutRemascAndFullTransactions() { Assertions.assertEquals(1, blockResultDTO.getTransactions().size()); Assertions.assertTrue(transactionResultsHashes.contains(TRANSACTION.getHash().toJsonString())); Assertions.assertFalse(transactionResultsHashes.contains(REMASC_TRANSACTION.getHash().toJsonString())); + Assertions.assertNotNull(blockResultDTO.getRskPteEdges()); } private List transactionResultsByBlock(BlockResultDTO blockResultDTO) { From d79f2a78dac94772422e6bb9b17f9b80a7f03a6a Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Wed, 19 Jun 2024 14:49:31 -0300 Subject: [PATCH 67/89] Updating package name --- .../{misc => pte}/PteIntegrationTest.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) rename rskj-core/src/integrationTest/java/{misc => pte}/PteIntegrationTest.java (92%) diff --git a/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java b/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java similarity index 92% rename from rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java rename to rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java index 2616f0274c8..e3bdf2e4dc2 100644 --- a/rskj-core/src/integrationTest/java/misc/PteIntegrationTest.java +++ b/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java @@ -1,4 +1,22 @@ -package misc; +/* + * This file is part of RskJ + * Copyright (C) 2024 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program 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. + * + * This program 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 this program. If not, see . + */ +package pte; import co.rsk.util.OkHttpClientTestFixture; import co.rsk.util.cli.CommandLineFixture; From 36a2d7661c044bf666187107b25986da3637f467 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Thu, 20 Jun 2024 12:00:57 -0300 Subject: [PATCH 68/89] Removing duplicated stacktrace print --- rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java b/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java index e3bdf2e4dc2..03f72ef0879 100644 --- a/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java +++ b/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java @@ -134,7 +134,6 @@ void whenParallelizableTransactionsAreSent_someAreExecutedInParallel() throws Ex blocksResponseMap.put("asyncBlocksResult", future.get()); } catch (ExecutionException | InterruptedException e) { - e.printStackTrace(); Assertions.fail(e); } From cc2b39a6294416542536ac414c47c1e674c72697 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Mon, 24 Jun 2024 12:36:56 -0300 Subject: [PATCH 69/89] Updating test --- .../co/rsk/util/OkHttpClientTestFixture.java | 30 +++++ .../java/pte/PteIntegrationTest.java | 115 +++++++----------- 2 files changed, 75 insertions(+), 70 deletions(-) diff --git a/rskj-core/src/integrationTest/java/co/rsk/util/OkHttpClientTestFixture.java b/rskj-core/src/integrationTest/java/co/rsk/util/OkHttpClientTestFixture.java index e3e1fabe913..66bf51c8f3d 100644 --- a/rskj-core/src/integrationTest/java/co/rsk/util/OkHttpClientTestFixture.java +++ b/rskj-core/src/integrationTest/java/co/rsk/util/OkHttpClientTestFixture.java @@ -40,6 +40,31 @@ public class OkHttpClientTestFixture { " \"jsonrpc\": \"2.0\"\n" + "}]"; + public static final String ETH_GET_BLOCK_BY_NUMBER = + "{\n" + + " \"method\": \"eth_getBlockByNumber\",\n" + + " \"params\": [\n" + + " \"\",\n" + + " true\n" + + " ],\n" + + " \"id\": 1,\n" + + " \"jsonrpc\": \"2.0\"\n" + + "}"; + + public static final String ETH_SEND_TRANSACTION = + "{\n" + + " \"jsonrpc\": \"2.0\",\n" + + " \"method\": \"eth_sendTransaction\",\n" + + " \"id\": 1,\n" + + " \"params\": [{\n" + + " \"from\": \"\",\n" + + " \"to\": \"\",\n" + + " \"gas\": \"\",\n" + + " \"gasPrice\": \"\",\n" + + " \"value\": \"\"\n" + + " }]\n" + + "}"; + private OkHttpClientTestFixture() { } @@ -101,4 +126,9 @@ public static JsonNode getJsonResponseForGetBestBlockMessage(int port) throws IO Response response = sendJsonRpcGetBestBlockMessage(port); return new ObjectMapper().readTree(response.body().string()); } + + public static String getEnvelopedMethodCalls(String... methodCall) { + return "[\n" + String.join(",\n", methodCall) + "]"; + } + } diff --git a/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java b/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java index 03f72ef0879..fefc75b6d83 100644 --- a/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java +++ b/rskj-core/src/integrationTest/java/pte/PteIntegrationTest.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.squareup.okhttp.Response; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,9 +37,14 @@ import java.util.concurrent.*; import java.util.stream.Stream; +import static co.rsk.util.OkHttpClientTestFixture.*; + public class PteIntegrationTest { - // THIS TEST REQUIRES A JAR FILE TO BE IN THE CORRECT PLACE BEFORE RUNNING + /* + When running this test locally, don't forget to build the .jar for the code you're trying to + test ('./gradlew clean' and './gradlew assemble' should be sufficient for most cases). + */ private static final int RPC_PORT = 9999; private static final int MAX_BLOCKS_TO_GET = 20; @@ -173,15 +179,44 @@ void whenParallelizableTransactionsAreSent_someAreExecutedInParallel() throws Ex } private Response getBlockByNumber(String number) throws IOException { - String content = "[{\n" + - " \"method\": \"eth_getBlockByNumber\",\n" + - " \"params\": [\n" + - " \"" + number + "\",\n" + - " true\n" + - " ],\n" + - " \"id\": 1,\n" + - " \"jsonrpc\": \"2.0\"\n" + - "}]"; + String content = getEnvelopedMethodCalls( + ETH_GET_BLOCK_BY_NUMBER.replace( + "", + number) + ); + + System.out.println(content); + + return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); + } + + private Response sendBulkTransactions( + String addressFrom1, String addressTo1, + String addressFrom2, String addressTo2, + String addressFrom3, String addressTo3, + String addressFrom4, String addressTo4) throws IOException { + + String gas = "0x9C40"; + String gasPrice = "0x10"; + String value = "0x500"; + + String[] placeholders = new String[]{ + "", "", "", + "", "" + }; + + String content = getEnvelopedMethodCalls( + StringUtils.replaceEach(ETH_SEND_TRANSACTION, placeholders, + new String[]{addressFrom1, addressTo1, gas, gasPrice, value}), + StringUtils.replaceEach(ETH_SEND_TRANSACTION, placeholders, + new String[]{addressFrom2, addressTo2, gas, gasPrice, value}), + StringUtils.replaceEach(ETH_SEND_TRANSACTION, placeholders, + new String[]{addressFrom3, addressTo3, gas, gasPrice, value}), + StringUtils.replaceEach(ETH_SEND_TRANSACTION, placeholders, + new String[]{addressFrom4, addressTo4, gas, gasPrice, value}) + ); + + System.out.println(content); return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); } @@ -206,64 +241,4 @@ private Future> getBlocksAsync() { return completableFuture; } - private Response sendBulkTransactions( - String addressFrom1, String addressTo1, - String addressFrom2, String addressTo2, - String addressFrom3, String addressTo3, - String addressFrom4, String addressTo4) throws IOException { - - String content = "[\n" + - "{\n" + - " \"jsonrpc\": \"2.0\",\n" + - " \"method\": \"eth_sendTransaction\",\n" + - " \"id\": 1,\n" + - " \"params\": [{\n" + - " \"from\": \"" + addressFrom1 + "\",\n" + - " \"to\": \"" + addressTo1 + "\",\n" + - " \"gas\": \"0x9C40\",\n" + - " \"gasPrice\": \"0x10\",\n" + - " \"value\": \"0x500\"\n" + - " }]\n" + - "},\n" + - "{\n" + - " \"jsonrpc\": \"2.0\",\n" + - " \"method\": \"eth_sendTransaction\",\n" + - " \"id\": 1,\n" + - " \"params\": [{\n" + - " \"from\": \"" + addressFrom2 + "\",\n" + - " \"to\": \"" + addressTo2 + "\",\n" + - " \"gas\": \"0x9C40\",\n" + - " \"gasPrice\": \"0x10\",\n" + - " \"value\": \"0x500\"\n" + - " }]\n" + - "},\n" + - "{\n" + - " \"jsonrpc\": \"2.0\",\n" + - " \"method\": \"eth_sendTransaction\",\n" + - " \"id\": 1,\n" + - " \"params\": [{\n" + - " \"from\": \"" + addressFrom3 + "\",\n" + - " \"to\": \"" + addressTo3 + "\",\n" + - " \"gas\": \"0x9C40\",\n" + - " \"gasPrice\": \"0x10\",\n" + - " \"value\": \"0x500\"\n" + - " }]\n" + - "},\n" + - "{\n" + - " \"jsonrpc\": \"2.0\",\n" + - " \"method\": \"eth_sendTransaction\",\n" + - " \"id\": 1,\n" + - " \"params\": [{\n" + - " \"from\": \"" + addressFrom4 + "\",\n" + - " \"to\": \"" + addressTo4 + "\",\n" + - " \"gas\": \"0x9C40\",\n" + - " \"gasPrice\": \"0x10\",\n" + - " \"value\": \"0x500\"\n" + - " }]\n" + - "}\n" + - "]"; - - return OkHttpClientTestFixture.sendJsonRpcMessage(content, RPC_PORT); - } - } \ No newline at end of file From d894940a3845ed990678b8f338f1d84ffb8a1d6d Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Thu, 4 Jul 2024 12:10:36 +0300 Subject: [PATCH 70/89] fixed executedContractWithDelegateCallToNonExistentContract test --- rskj-core/src/test/resources/dsl/delegatecall02.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/rskj-core/src/test/resources/dsl/delegatecall02.txt b/rskj-core/src/test/resources/dsl/delegatecall02.txt index 27a66edbca5..a897cb1dda8 100644 --- a/rskj-core/src/test/resources/dsl/delegatecall02.txt +++ b/rskj-core/src/test/resources/dsl/delegatecall02.txt @@ -30,6 +30,7 @@ transaction_build tx01 block_build b01 parent g00 + gasLimit 6800000 transactions tx01 build From 3856836de945171328798cbfff52daa849cd00de Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Thu, 5 Sep 2024 11:30:16 +0300 Subject: [PATCH 71/89] fix(pte): fixing sonar complaints --- rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java | 2 -- .../org/ethereum/validator/ProofOfWorkRuleArrowhead600Test.java | 1 + .../java/org/ethereum/validator/ProofOfWorkRuleBambooTest.java | 1 + .../java/org/ethereum/validator/ProofOfWorkRuleOrchidTest.java | 1 + 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java index 169cd485576..5df05095789 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/Web3ImplTest.java @@ -3128,14 +3128,12 @@ private void assertInvalidInput(Function toInvoke) { //Chain Param Object creations private class ChainParams { - private final World world; private final Web3Impl web3; private final String accountAddress; private final Block block; private CallArguments argsForCall; // for call tests could be null private ChainParams(World world, String accountAddress, Block block) { - this.world = world; this.web3 = createWeb3(world); this.accountAddress = accountAddress; this.block = block; diff --git a/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleArrowhead600Test.java b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleArrowhead600Test.java index 33541942f83..7cef67a14b9 100644 --- a/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleArrowhead600Test.java +++ b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleArrowhead600Test.java @@ -21,6 +21,7 @@ import co.rsk.mine.NetworkUpgrades; import org.junit.jupiter.api.BeforeEach; +@SuppressWarnings("java:S2187") class ProofOfWorkRuleArrowhead600Test extends ProofOfWorkRuleTest { @BeforeEach diff --git a/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleBambooTest.java b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleBambooTest.java index fc8bff68473..4516b50ecae 100644 --- a/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleBambooTest.java +++ b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleBambooTest.java @@ -21,6 +21,7 @@ import co.rsk.mine.NetworkUpgrades; import org.junit.jupiter.api.BeforeEach; +@SuppressWarnings("java:S2187") class ProofOfWorkRuleBambooTest extends ProofOfWorkRuleTest { @BeforeEach diff --git a/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleOrchidTest.java b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleOrchidTest.java index 4203d592e7b..3d0d737a8f5 100644 --- a/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleOrchidTest.java +++ b/rskj-core/src/test/java/org/ethereum/validator/ProofOfWorkRuleOrchidTest.java @@ -21,6 +21,7 @@ import co.rsk.mine.NetworkUpgrades; import org.junit.jupiter.api.BeforeEach; +@SuppressWarnings("java:S2187") class ProofOfWorkRuleOrchidTest extends ProofOfWorkRuleTest { @BeforeEach From 79523caaa68d0183ad94e7ddccbe667d6af40386 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Thu, 5 Sep 2024 15:13:57 +0300 Subject: [PATCH 72/89] fix(pte): suppress sonar warnings --- .../src/main/java/org/ethereum/vm/program/Program.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java index ecfa58ca940..d8309d786bc 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/program/Program.java +++ b/rskj-core/src/main/java/org/ethereum/vm/program/Program.java @@ -215,10 +215,6 @@ private T setupProgramListener(T traceListenerA return traceListenerAware; } - public byte getOp(int pc) { - return (getLength(ops) <= pc) ? 0 : ops[pc]; - } - public byte getCurrentOp() { return isEmpty(ops) ? 0 : ops[pc]; } @@ -1338,7 +1334,7 @@ public int verifyJumpDest(DataWord nextPC) { if (nextPC.occupyMoreThan(4)) { throw ExceptionHelper.badJumpDestination(this, -1); } - int ret = nextPC.intValue(); // could be negative + int ret = nextPC.intValue(); // NOSONAR // could be negative if (ret < 0 || ret >= jumpdestSet.size() || !jumpdestSet.get(ret)) { throw ExceptionHelper.badJumpDestination(this, ret); } From ecbb0ad1a5c964d83c4c89224fa8433588322b77 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Thu, 26 Sep 2024 13:50:05 +0300 Subject: [PATCH 73/89] feat(pte): fix unit tests after merge from master --- .../co/rsk/core/bc/BlockExecutorTest.java | 58 ++++++++----------- ...ion_doesnt_fail_with_RSKIP_deactivated.txt | 5 +- ...e_validation_fail_with_RSKIP_activated.txt | 8 +-- ..._validation_success_with_initcode_cost.txt | 6 +- ...lidation_success_without_initcode_cost.txt | 6 +- ...ion_doesnt_fail_with_RSKIP_deactivated.txt | 5 +- ...e_validation_fail_with_RSKIP_activated.txt | 8 +-- ..._validation_success_with_initcode_cost.txt | 6 +- ...lidation_success_without_initcode_cost.txt | 6 +- ..._not_enough_gas_with_rskip_deactivated.txt | 2 +- ...alidation_success_with_rskip_activated.txt | 2 +- ...idation_success_with_rskip_deactivated.txt | 2 +- ...tivated_with_initcode_size_max_reached.txt | 2 +- 13 files changed, 47 insertions(+), 69 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java index f7fb559554e..16ae34501ea 100644 --- a/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java +++ b/rskj-core/src/test/java/co/rsk/core/bc/BlockExecutorTest.java @@ -75,11 +75,12 @@ */ public class BlockExecutorTest { private static final byte[] EMPTY_TRIE_HASH = sha3(RLP.encodeElement(EMPTY_BYTE_ARRAY)); - private static final TestSystemProperties CONFIG = new TestSystemProperties(); - private static final ActivationConfig activationConfig = spy(CONFIG.getActivationConfig()); - private static final BlockFactory BLOCK_FACTORY = new BlockFactory(activationConfig); - public static final boolean RSKIP_126_IS_ACTIVE = true; - private final long MIN_SEQUENTIAL_SET_GAS_LIMIT = Constants.regtest().getMinSequentialSetGasLimit(); + private static final boolean RSKIP_126_IS_ACTIVE = true; + private static final long MIN_SEQUENTIAL_SET_GAS_LIMIT = Constants.regtest().getMinSequentialSetGasLimit(); + + private final TestSystemProperties config = new TestSystemProperties(); + private final ActivationConfig activationConfig = spy(config.getActivationConfig()); + private final BlockFactory BLOCK_FACTORY = new BlockFactory(activationConfig); @TempDir public Path tempDir; @@ -87,13 +88,10 @@ public class BlockExecutorTest { private Blockchain blockchain; private TrieStore trieStore; private RepositorySnapshot repository; - private RskSystemProperties cfg; @BeforeEach public void setUp() { - cfg = spy(CONFIG); - doReturn(activationConfig).when(cfg).getActivationConfig(); - RskTestFactory objects = new RskTestFactory(tempDir, CONFIG); + RskTestFactory objects = new RskTestFactory(tempDir, config); blockchain = objects.getBlockchain(); trieStore = objects.getTrieStore(); repository = objects.getRepositoryLocator().snapshotAt(blockchain.getBestBlock().getHeader()); @@ -362,7 +360,7 @@ private Block createBlockWithExcludedTransaction(boolean withRemasc, boolean act .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(account2.getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx3.sign(account.getEcKey().getPrivKeyBytes()); @@ -373,7 +371,7 @@ private Block createBlockWithExcludedTransaction(boolean withRemasc, boolean act .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(account2.getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx1.sign(account3.getEcKey().getPrivKeyBytes()); @@ -451,7 +449,7 @@ void executeBlockWithTxThatMakesBlockInvalidSenderHasNoBalance(boolean activeRsk .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(account2.getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx3.sign(account.getEcKey().getPrivKeyBytes()); @@ -462,7 +460,7 @@ void executeBlockWithTxThatMakesBlockInvalidSenderHasNoBalance(boolean activeRsk .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(account2.getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx1.sign(account3.getEcKey().getPrivKeyBytes()); @@ -748,7 +746,7 @@ void whenExecuteATxWithGasLimitExceedingSublistGasLimitShouldNotBeInlcuded(boole .gasPrice(BigInteger.ONE) .gasLimit(parent.getGasLimit()) .destination(receiver.getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx.sign(sender.getEcKey().getPrivKeyBytes()); @@ -877,13 +875,7 @@ void validateStateRootWithRskip126DisabledAndValidStateRoot(boolean activeRskip1 BlockResult blockResult = new BlockResult(block, Collections.emptyList(), Collections.emptyList(), new short[0], 0, Coin.ZERO, trie); -// RskSystemProperties cfg = spy(CONFIG); - - ActivationConfig activationConfig = spy(cfg.getActivationConfig()); - doReturn(false).when(activationConfig).isActive(eq(RSKIP126), anyLong()); - doReturn(activationConfig).when(cfg).getActivationConfig(); - - BlockExecutor executor = buildBlockExecutor(trieStore, cfg, activeRskip144, false); + BlockExecutor executor = buildBlockExecutor(trieStore, activeRskip144, false); short[] expectedEdges = activeRskip144 ? new short[0] : null; @@ -1016,7 +1008,7 @@ void invalidBlockBadLogsBloom(boolean activeRskip144) { Assertions.assertFalse(executor.executeAndValidate(block, parent.getHeader())); } - private static TestObjects generateBlockWithOneTransaction(Boolean activeRskip144, boolean rskip126IsActive) { + private TestObjects generateBlockWithOneTransaction(Boolean activeRskip144, boolean rskip126IsActive) { TrieStore trieStore = new TrieStoreImpl(new HashMapDB()); Repository repository = new MutableRepository(trieStore, new Trie(trieStore)); @@ -1037,7 +1029,7 @@ private static TestObjects generateBlockWithOneTransaction(Boolean activeRskip14 .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(account2.getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx1.sign(account.getEcKey().getPrivKeyBytes()); @@ -1084,7 +1076,7 @@ private Block getBlockWithOneTransaction() { .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(account2.getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx.sign(account.getEcKey().getPrivKeyBytes()); @@ -1117,7 +1109,7 @@ private Block getBlockWithTwoTransactions() { .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(account2.getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx.sign(account.getEcKey().getPrivKeyBytes()); @@ -1127,7 +1119,7 @@ private Block getBlockWithTwoTransactions() { .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(account2.getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx1.sign(account.getEcKey().getPrivKeyBytes()); @@ -1161,7 +1153,7 @@ private Block getBlockWithTwoDependentTransactions(short[] edges) { .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(accounts.get((i + 1) % 2).getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx.sign(accounts.get(i).getEcKey().getPrivKeyBytes()); @@ -1203,7 +1195,7 @@ private Block getBlockWithTenTransactions(short[] edges) { .gasPrice(BigInteger.ONE) .gasLimit(BigInteger.valueOf(21000)) .destination(accounts.get(i + nTxs).getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx.sign(accounts.get(i).getEcKey().getPrivKeyBytes()); @@ -1244,7 +1236,7 @@ private Block getBlockWithNIndependentTransactions(int numberOfTxs, BigInteger t .gasPrice(BigInteger.ONE) .gasLimit(txGasLimit) .destination(accounts.get(i + numberOfTxs).getAddress()) - .chainId(CONFIG.getNetworkConstants().getChainId()) + .chainId(config.getNetworkConstants().getChainId()) .value(BigInteger.TEN) .build(); tx.sign(accounts.get(i).getEcKey().getPrivKeyBytes()); @@ -1413,7 +1405,7 @@ public TestObjects generateBlockWithOneStrangeTransaction(int strangeTransaction private byte[] calculateTxTrieRoot(List transactions, long blockNumber) { return BlockHashesHelper.getTxTrieRoot( transactions, - CONFIG.getActivationConfig().isActive(ConsensusRule.RSKIP126, blockNumber) + config.getActivationConfig().isActive(ConsensusRule.RSKIP126, blockNumber) ); } @@ -1455,11 +1447,11 @@ private static byte[] sha3(byte[] input) { return digest.digest(); } - private static BlockExecutor buildBlockExecutor(TrieStore store, Boolean activeRskip144, boolean rskip126IsActive) { - return buildBlockExecutor(store, CONFIG, activeRskip144, rskip126IsActive); + private BlockExecutor buildBlockExecutor(TrieStore store, Boolean activeRskip144, boolean rskip126IsActive) { + return buildBlockExecutor(store, config, activeRskip144, rskip126IsActive); } - private static BlockExecutor buildBlockExecutor(TrieStore store, RskSystemProperties config, Boolean activeRskip144, Boolean activeRskip126) { + private BlockExecutor buildBlockExecutor(TrieStore store, RskSystemProperties config, Boolean activeRskip144, Boolean activeRskip126) { RskSystemProperties cfg = spy(config); doReturn(activationConfig).when(cfg).getActivationConfig(); doReturn(activeRskip144).when(activationConfig).isActive(eq(RSKIP144), anyLong()); diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_doesnt_fail_with_RSKIP_deactivated.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_doesnt_fail_with_RSKIP_deactivated.txt index a6f1a5d63c6..947eca67428 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_doesnt_fail_with_RSKIP_deactivated.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_doesnt_fail_with_RSKIP_deactivated.txt @@ -40,12 +40,11 @@ transaction_build txCreateContractFactory receiverAddress 00 value 0 data 608060405234801561001057600080fd5b50610cde806100206000396000f3fe6080604052600436106200002c5760003560e01c8063490af4231462000031578063fdaaa16f1462000067575b600080fd5b6200004f6004803603810190620000499190620002c1565b620000ab565b6040516200005e919062000357565b60405180910390f35b3480156200007457600080fd5b506200009360048036038101906200008d9190620002c1565b620000fb565b604051620000a2919062000357565b60405180910390f35b600080600160001b90508083604051620000c5906200013d565b620000d19190620003fd565b8190604051809103906000f5905080158015620000f2573d6000803e3d6000fd5b50915050919050565b6000816040516200010c906200013d565b620001189190620003fd565b604051809103906000f08015801562000135573d6000803e3d6000fd5b509050919050565b610887806200042283390190565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001b48262000169565b810181811067ffffffffffffffff82111715620001d657620001d56200017a565b5b80604052505050565b6000620001eb6200014b565b9050620001f98282620001a9565b919050565b600067ffffffffffffffff8211156200021c576200021b6200017a565b5b620002278262000169565b9050602081019050919050565b82818337600083830152505050565b60006200025a6200025484620001fe565b620001df565b90508281526020810184848401111562000279576200027862000164565b5b6200028684828562000234565b509392505050565b600082601f830112620002a657620002a56200015f565b5b8135620002b884826020860162000243565b91505092915050565b600060208284031215620002da57620002d962000155565b5b600082013567ffffffffffffffff811115620002fb57620002fa6200015a565b5b62000309848285016200028e565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200033f8262000312565b9050919050565b620003518162000332565b82525050565b60006020820190506200036e600083018462000346565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015620003b057808201518184015260208101905062000393565b60008484015250505050565b6000620003c98262000374565b620003d581856200037f565b9350620003e781856020860162000390565b620003f28162000169565b840191505092915050565b60006020820190508181036000830152620004198184620003bc565b90509291505056fe60806040523480156200001157600080fd5b506040516200088738038062000887833981810160405281019062000037919062000223565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019081620000889190620004bf565b5050620005a6565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620000f982620000ae565b810181811067ffffffffffffffff821117156200011b576200011a620000bf565b5b80604052505050565b60006200013062000090565b90506200013e8282620000ee565b919050565b600067ffffffffffffffff821115620001615762000160620000bf565b5b6200016c82620000ae565b9050602081019050919050565b60005b83811015620001995780820151818401526020810190506200017c565b60008484015250505050565b6000620001bc620001b68462000143565b62000124565b905082815260208101848484011115620001db57620001da620000a9565b5b620001e884828562000179565b509392505050565b600082601f830112620002085762000207620000a4565b5b81516200021a848260208601620001a5565b91505092915050565b6000602082840312156200023c576200023b6200009a565b5b600082015167ffffffffffffffff8111156200025d576200025c6200009f565b5b6200026b84828501620001f0565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620002c757607f821691505b602082108103620002dd57620002dc6200027f565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003477fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000308565b62000353868362000308565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620003a06200039a62000394846200036b565b62000375565b6200036b565b9050919050565b6000819050919050565b620003bc836200037f565b620003d4620003cb82620003a7565b84845462000315565b825550505050565b600090565b620003eb620003dc565b620003f8818484620003b1565b505050565b5b81811015620004205762000414600082620003e1565b600181019050620003fe565b5050565b601f8211156200046f576200043981620002e3565b6200044484620002f8565b8101602085101562000454578190505b6200046c6200046385620002f8565b830182620003fd565b50505b505050565b600082821c905092915050565b6000620004946000198460080262000474565b1980831691505092915050565b6000620004af838362000481565b9150826002028217905092915050565b620004ca8262000274565b67ffffffffffffffff811115620004e657620004e5620000bf565b5b620004f28254620002ae565b620004ff82828562000424565b600060209050601f83116001811462000537576000841562000522578287015190505b6200052e8582620004a1565b8655506200059e565b601f1984166200054786620002e3565b60005b8281101562000571578489015182556001820191506020850194506020810190506200054a565b868310156200059157848901516200058d601f89168262000481565b8355505b6001600288020188555050505b505050505050565b6102d180620005b66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636d4ce63c1461003b5780638da5cb5b14610059575b600080fd5b610043610077565b60405161005091906101bd565b60405180910390f35b610061610109565b60405161006e9190610220565b60405180910390f35b6060600180546100869061026a565b80601f01602080910402602001604051908101604052809291908181526020018280546100b29061026a565b80156100ff5780601f106100d4576101008083540402835291602001916100ff565b820191906000526020600020905b8154815290600101906020018083116100e257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600081519050919050565b600082825260208201905092915050565b60005b8381101561016757808201518184015260208101905061014c565b60008484015250505050565b6000601f19601f8301169050919050565b600061018f8261012d565b6101998185610138565b93506101a9818560208601610149565b6101b281610173565b840191505092915050565b600060208201905081810360008301526101d78184610184565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020a826101df565b9050919050565b61021a816101ff565b82525050565b60006020820190506102356000830184610211565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061028257607f821691505b6020821081036102955761029461023b565b5b5091905056fea264697066735822122071163cf749253f921b29799fb8f2b856a099dcdc627d7622ee7536e5e392509464736f6c63430008180033a2646970667358221220bd9fa3c126989a1d6a7a7fe44541a0bf9a4b43a3d2e12733f1208d6beaf0ede564736f6c63430008180033 - gas 6500000 + gas 800000 build block_build b01 parent g00 - gasLimit 6500000 transactions txCreateContractFactory build @@ -69,7 +68,7 @@ transaction_build txCreateContractViaOpCodeCreate2 block_build b02 parent b01 - gasLimit 10000000 + gasLimit 30000000 transactions txCreateContractViaOpCodeCreate2 build diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_fail_with_RSKIP_activated.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_fail_with_RSKIP_activated.txt index 26e56ea6c64..852c650dceb 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_fail_with_RSKIP_activated.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_fail_with_RSKIP_activated.txt @@ -40,12 +40,11 @@ transaction_build txCreateContractFactory receiverAddress 00 value 0 data 608060405234801561001057600080fd5b50610cde806100206000396000f3fe6080604052600436106200002c5760003560e01c8063490af4231462000031578063fdaaa16f1462000067575b600080fd5b6200004f6004803603810190620000499190620002c1565b620000ab565b6040516200005e919062000357565b60405180910390f35b3480156200007457600080fd5b506200009360048036038101906200008d9190620002c1565b620000fb565b604051620000a2919062000357565b60405180910390f35b600080600160001b90508083604051620000c5906200013d565b620000d19190620003fd565b8190604051809103906000f5905080158015620000f2573d6000803e3d6000fd5b50915050919050565b6000816040516200010c906200013d565b620001189190620003fd565b604051809103906000f08015801562000135573d6000803e3d6000fd5b509050919050565b610887806200042283390190565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001b48262000169565b810181811067ffffffffffffffff82111715620001d657620001d56200017a565b5b80604052505050565b6000620001eb6200014b565b9050620001f98282620001a9565b919050565b600067ffffffffffffffff8211156200021c576200021b6200017a565b5b620002278262000169565b9050602081019050919050565b82818337600083830152505050565b60006200025a6200025484620001fe565b620001df565b90508281526020810184848401111562000279576200027862000164565b5b6200028684828562000234565b509392505050565b600082601f830112620002a657620002a56200015f565b5b8135620002b884826020860162000243565b91505092915050565b600060208284031215620002da57620002d962000155565b5b600082013567ffffffffffffffff811115620002fb57620002fa6200015a565b5b62000309848285016200028e565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200033f8262000312565b9050919050565b620003518162000332565b82525050565b60006020820190506200036e600083018462000346565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015620003b057808201518184015260208101905062000393565b60008484015250505050565b6000620003c98262000374565b620003d581856200037f565b9350620003e781856020860162000390565b620003f28162000169565b840191505092915050565b60006020820190508181036000830152620004198184620003bc565b90509291505056fe60806040523480156200001157600080fd5b506040516200088738038062000887833981810160405281019062000037919062000223565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019081620000889190620004bf565b5050620005a6565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620000f982620000ae565b810181811067ffffffffffffffff821117156200011b576200011a620000bf565b5b80604052505050565b60006200013062000090565b90506200013e8282620000ee565b919050565b600067ffffffffffffffff821115620001615762000160620000bf565b5b6200016c82620000ae565b9050602081019050919050565b60005b83811015620001995780820151818401526020810190506200017c565b60008484015250505050565b6000620001bc620001b68462000143565b62000124565b905082815260208101848484011115620001db57620001da620000a9565b5b620001e884828562000179565b509392505050565b600082601f830112620002085762000207620000a4565b5b81516200021a848260208601620001a5565b91505092915050565b6000602082840312156200023c576200023b6200009a565b5b600082015167ffffffffffffffff8111156200025d576200025c6200009f565b5b6200026b84828501620001f0565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620002c757607f821691505b602082108103620002dd57620002dc6200027f565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003477fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000308565b62000353868362000308565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620003a06200039a62000394846200036b565b62000375565b6200036b565b9050919050565b6000819050919050565b620003bc836200037f565b620003d4620003cb82620003a7565b84845462000315565b825550505050565b600090565b620003eb620003dc565b620003f8818484620003b1565b505050565b5b81811015620004205762000414600082620003e1565b600181019050620003fe565b5050565b601f8211156200046f576200043981620002e3565b6200044484620002f8565b8101602085101562000454578190505b6200046c6200046385620002f8565b830182620003fd565b50505b505050565b600082821c905092915050565b6000620004946000198460080262000474565b1980831691505092915050565b6000620004af838362000481565b9150826002028217905092915050565b620004ca8262000274565b67ffffffffffffffff811115620004e657620004e5620000bf565b5b620004f28254620002ae565b620004ff82828562000424565b600060209050601f83116001811462000537576000841562000522578287015190505b6200052e8582620004a1565b8655506200059e565b601f1984166200054786620002e3565b60005b8281101562000571578489015182556001820191506020850194506020810190506200054a565b868310156200059157848901516200058d601f89168262000481565b8355505b6001600288020188555050505b505050505050565b6102d180620005b66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636d4ce63c1461003b5780638da5cb5b14610059575b600080fd5b610043610077565b60405161005091906101bd565b60405180910390f35b610061610109565b60405161006e9190610220565b60405180910390f35b6060600180546100869061026a565b80601f01602080910402602001604051908101604052809291908181526020018280546100b29061026a565b80156100ff5780601f106100d4576101008083540402835291602001916100ff565b820191906000526020600020905b8154815290600101906020018083116100e257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600081519050919050565b600082825260208201905092915050565b60005b8381101561016757808201518184015260208101905061014c565b60008484015250505050565b6000601f19601f8301169050919050565b600061018f8261012d565b6101998185610138565b93506101a9818560208601610149565b6101b281610173565b840191505092915050565b600060208201905081810360008301526101d78184610184565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020a826101df565b9050919050565b61021a816101ff565b82525050565b60006020820190506102356000830184610211565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061028257607f821691505b6020821081036102955761029461023b565b5b5091905056fea264697066735822122071163cf749253f921b29799fb8f2b856a099dcdc627d7622ee7536e5e392509464736f6c63430008180033a2646970667358221220bd9fa3c126989a1d6a7a7fe44541a0bf9a4b43a3d2e12733f1208d6beaf0ede564736f6c63430008180033 - gas 6500000 + gas 800000 build block_build b01 parent g00 - gasLimit 6500000 transactions txCreateContractFactory build @@ -64,12 +63,11 @@ transaction_build txCreateContractViaOpCodeCreate2 contract txCreateContractFactory # created in txCreateContractFactory value 0 data 490af4230000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000b7bd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000 - gas 6500000 + gas 600000 build block_build b02 parent b01 - gasLimit 6500000 transactions txCreateContractViaOpCodeCreate2 build @@ -79,4 +77,4 @@ block_connect b02 assert_best b02 # Assert balance after have created the contract via opcode create2 -assert_balance acc1 12739631 \ No newline at end of file +assert_balance acc1 18639631 \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_success_with_initcode_cost.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_success_with_initcode_cost.txt index 1c4e524a778..e1411dc0544 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_success_with_initcode_cost.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_success_with_initcode_cost.txt @@ -40,12 +40,11 @@ transaction_build txCreateContractFactory receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061081b806100206000396000f3fe6080604052600436106100295760003560e01c806313a0432e1461002e5780635d828e9a1461004c575b600080fd5b610036610077565b6040516100439190610133565b60405180910390f35b34801561005857600080fd5b506100616100b5565b60405161006e9190610133565b60405180910390f35b600080600160001b90508060405161008e906100e5565b8190604051809103906000f59050801580156100ae573d6000803e3d6000fd5b5091505090565b60006040516100c3906100e5565b604051809103906000f0801580156100df573d6000803e3d6000fd5b50905090565b6106978061014f83390190565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061011d826100f2565b9050919050565b61012d81610112565b82525050565b60006020820190506101486000830184610124565b9291505056fe608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040518060400160405280600781526020017901000000000000000000000000000000000000000000000000008152506001908161008f91906102e5565b506103b7565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061011657607f821691505b602082108103610129576101286100cf565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026101917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610154565b61019b8683610154565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006101e26101dd6101d8846101b3565b6101bd565b6101b3565b9050919050565b6000819050919050565b6101fc836101c7565b610210610208826101e9565b848454610161565b825550505050565b600090565b610225610218565b6102308184846101f3565b505050565b5b818110156102545761024960008261021d565b600181019050610236565b5050565b601f8211156102995761026a8161012f565b61027384610144565b81016020851015610282578190505b61029661028e85610144565b830182610235565b50505b505050565b600082821c905092915050565b60006102bc6000198460080261029e565b1980831691505092915050565b60006102d583836102ab565b9150826002028217905092915050565b6102ee82610095565b67ffffffffffffffff811115610307576103066100a0565b5b61031182546100fe565b61031c828285610258565b600060209050601f83116001811461034f576000841561033d578287015190505b61034785826102c9565b8655506103af565b601f19841661035d8661012f565b60005b8281101561038557848901518255600182019150602085019450602081019050610360565b868310156103a2578489015161039e601f8916826102ab565b8355505b6001600288020188555050505b505050505050565b6102d1806103c66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636d4ce63c1461003b5780638da5cb5b14610059575b600080fd5b610043610077565b60405161005091906101bd565b60405180910390f35b610061610109565b60405161006e9190610220565b60405180910390f35b6060600180546100869061026a565b80601f01602080910402602001604051908101604052809291908181526020018280546100b29061026a565b80156100ff5780601f106100d4576101008083540402835291602001916100ff565b820191906000526020600020905b8154815290600101906020018083116100e257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600081519050919050565b600082825260208201905092915050565b60005b8381101561016757808201518184015260208101905061014c565b60008484015250505050565b6000601f19601f8301169050919050565b600061018f8261012d565b6101998185610138565b93506101a9818560208601610149565b6101b281610173565b840191505092915050565b600060208201905081810360008301526101d78184610184565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020a826101df565b9050919050565b61021a816101ff565b82525050565b60006020820190506102356000830184610211565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061028257607f821691505b6020821081036102955761029461023b565b5b5091905056fea264697066735822122049d311e90db3a44f73eb3cf557d8a87356f3317e1459eda82ef2a687d7a18b3a64736f6c63430008180033a26469706673582212202aef06f0c7e6b0968848c66b5849bb66c58dda606e71a6ad568f4ab402c878bd64736f6c63430008180033 - gas 4700000 + gas 600000 build block_build b01 parent g00 - gasLimit 6500000 transactions txCreateContractFactory build @@ -64,12 +63,11 @@ transaction_build txCreateContractViaOpCodeCreate2 contract txCreateContractFactory # created in txCreateContractFactory value 0 data 13a0432e - gas 1000000 + gas 600000 build block_build b02 parent b01 - gasLimit 6500000 transactions txCreateContractViaOpCodeCreate2 build diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_success_without_initcode_cost.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_success_without_initcode_cost.txt index e775e285b4c..ee380040af1 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_success_without_initcode_cost.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE2_opcode_validation_success_without_initcode_cost.txt @@ -40,12 +40,11 @@ transaction_build txCreateContractFactory receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061081b806100206000396000f3fe6080604052600436106100295760003560e01c806313a0432e1461002e5780635d828e9a1461004c575b600080fd5b610036610077565b6040516100439190610133565b60405180910390f35b34801561005857600080fd5b506100616100b5565b60405161006e9190610133565b60405180910390f35b600080600160001b90508060405161008e906100e5565b8190604051809103906000f59050801580156100ae573d6000803e3d6000fd5b5091505090565b60006040516100c3906100e5565b604051809103906000f0801580156100df573d6000803e3d6000fd5b50905090565b6106978061014f83390190565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061011d826100f2565b9050919050565b61012d81610112565b82525050565b60006020820190506101486000830184610124565b9291505056fe608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040518060400160405280600781526020017901000000000000000000000000000000000000000000000000008152506001908161008f91906102e5565b506103b7565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061011657607f821691505b602082108103610129576101286100cf565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026101917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610154565b61019b8683610154565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006101e26101dd6101d8846101b3565b6101bd565b6101b3565b9050919050565b6000819050919050565b6101fc836101c7565b610210610208826101e9565b848454610161565b825550505050565b600090565b610225610218565b6102308184846101f3565b505050565b5b818110156102545761024960008261021d565b600181019050610236565b5050565b601f8211156102995761026a8161012f565b61027384610144565b81016020851015610282578190505b61029661028e85610144565b830182610235565b50505b505050565b600082821c905092915050565b60006102bc6000198460080261029e565b1980831691505092915050565b60006102d583836102ab565b9150826002028217905092915050565b6102ee82610095565b67ffffffffffffffff811115610307576103066100a0565b5b61031182546100fe565b61031c828285610258565b600060209050601f83116001811461034f576000841561033d578287015190505b61034785826102c9565b8655506103af565b601f19841661035d8661012f565b60005b8281101561038557848901518255600182019150602085019450602081019050610360565b868310156103a2578489015161039e601f8916826102ab565b8355505b6001600288020188555050505b505050505050565b6102d1806103c66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636d4ce63c1461003b5780638da5cb5b14610059575b600080fd5b610043610077565b60405161005091906101bd565b60405180910390f35b610061610109565b60405161006e9190610220565b60405180910390f35b6060600180546100869061026a565b80601f01602080910402602001604051908101604052809291908181526020018280546100b29061026a565b80156100ff5780601f106100d4576101008083540402835291602001916100ff565b820191906000526020600020905b8154815290600101906020018083116100e257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600081519050919050565b600082825260208201905092915050565b60005b8381101561016757808201518184015260208101905061014c565b60008484015250505050565b6000601f19601f8301169050919050565b600061018f8261012d565b6101998185610138565b93506101a9818560208601610149565b6101b281610173565b840191505092915050565b600060208201905081810360008301526101d78184610184565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020a826101df565b9050919050565b61021a816101ff565b82525050565b60006020820190506102356000830184610211565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061028257607f821691505b6020821081036102955761029461023b565b5b5091905056fea264697066735822122049d311e90db3a44f73eb3cf557d8a87356f3317e1459eda82ef2a687d7a18b3a64736f6c63430008180033a26469706673582212202aef06f0c7e6b0968848c66b5849bb66c58dda606e71a6ad568f4ab402c878bd64736f6c63430008180033 - gas 4700000 + gas 600000 build block_build b01 parent g00 - gasLimit 6500000 transactions txCreateContractFactory build @@ -64,12 +63,11 @@ transaction_build txCreateContractViaOpCodeCreate2 contract txCreateContractFactory # created in txCreateContractFactory value 0 data 13a0432e - gas 1000000 + gas 600000 build block_build b02 parent b01 - gasLimit 6500000 transactions txCreateContractViaOpCodeCreate2 build diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_doesnt_fail_with_RSKIP_deactivated.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_doesnt_fail_with_RSKIP_deactivated.txt index f8e0c54db8a..34ee3daa399 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_doesnt_fail_with_RSKIP_deactivated.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_doesnt_fail_with_RSKIP_deactivated.txt @@ -40,12 +40,11 @@ transaction_build txCreateContractFactory receiverAddress 00 value 0 data 608060405234801561001057600080fd5b50610cde806100206000396000f3fe6080604052600436106200002c5760003560e01c8063490af4231462000031578063fdaaa16f1462000067575b600080fd5b6200004f6004803603810190620000499190620002c1565b620000ab565b6040516200005e919062000357565b60405180910390f35b3480156200007457600080fd5b506200009360048036038101906200008d9190620002c1565b620000fb565b604051620000a2919062000357565b60405180910390f35b600080600160001b90508083604051620000c5906200013d565b620000d19190620003fd565b8190604051809103906000f5905080158015620000f2573d6000803e3d6000fd5b50915050919050565b6000816040516200010c906200013d565b620001189190620003fd565b604051809103906000f08015801562000135573d6000803e3d6000fd5b509050919050565b610887806200042283390190565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001b48262000169565b810181811067ffffffffffffffff82111715620001d657620001d56200017a565b5b80604052505050565b6000620001eb6200014b565b9050620001f98282620001a9565b919050565b600067ffffffffffffffff8211156200021c576200021b6200017a565b5b620002278262000169565b9050602081019050919050565b82818337600083830152505050565b60006200025a6200025484620001fe565b620001df565b90508281526020810184848401111562000279576200027862000164565b5b6200028684828562000234565b509392505050565b600082601f830112620002a657620002a56200015f565b5b8135620002b884826020860162000243565b91505092915050565b600060208284031215620002da57620002d962000155565b5b600082013567ffffffffffffffff811115620002fb57620002fa6200015a565b5b62000309848285016200028e565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200033f8262000312565b9050919050565b620003518162000332565b82525050565b60006020820190506200036e600083018462000346565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015620003b057808201518184015260208101905062000393565b60008484015250505050565b6000620003c98262000374565b620003d581856200037f565b9350620003e781856020860162000390565b620003f28162000169565b840191505092915050565b60006020820190508181036000830152620004198184620003bc565b90509291505056fe60806040523480156200001157600080fd5b506040516200088738038062000887833981810160405281019062000037919062000223565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019081620000889190620004bf565b5050620005a6565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620000f982620000ae565b810181811067ffffffffffffffff821117156200011b576200011a620000bf565b5b80604052505050565b60006200013062000090565b90506200013e8282620000ee565b919050565b600067ffffffffffffffff821115620001615762000160620000bf565b5b6200016c82620000ae565b9050602081019050919050565b60005b83811015620001995780820151818401526020810190506200017c565b60008484015250505050565b6000620001bc620001b68462000143565b62000124565b905082815260208101848484011115620001db57620001da620000a9565b5b620001e884828562000179565b509392505050565b600082601f830112620002085762000207620000a4565b5b81516200021a848260208601620001a5565b91505092915050565b6000602082840312156200023c576200023b6200009a565b5b600082015167ffffffffffffffff8111156200025d576200025c6200009f565b5b6200026b84828501620001f0565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620002c757607f821691505b602082108103620002dd57620002dc6200027f565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003477fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000308565b62000353868362000308565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620003a06200039a62000394846200036b565b62000375565b6200036b565b9050919050565b6000819050919050565b620003bc836200037f565b620003d4620003cb82620003a7565b84845462000315565b825550505050565b600090565b620003eb620003dc565b620003f8818484620003b1565b505050565b5b81811015620004205762000414600082620003e1565b600181019050620003fe565b5050565b601f8211156200046f576200043981620002e3565b6200044484620002f8565b8101602085101562000454578190505b6200046c6200046385620002f8565b830182620003fd565b50505b505050565b600082821c905092915050565b6000620004946000198460080262000474565b1980831691505092915050565b6000620004af838362000481565b9150826002028217905092915050565b620004ca8262000274565b67ffffffffffffffff811115620004e657620004e5620000bf565b5b620004f28254620002ae565b620004ff82828562000424565b600060209050601f83116001811462000537576000841562000522578287015190505b6200052e8582620004a1565b8655506200059e565b601f1984166200054786620002e3565b60005b8281101562000571578489015182556001820191506020850194506020810190506200054a565b868310156200059157848901516200058d601f89168262000481565b8355505b6001600288020188555050505b505050505050565b6102d180620005b66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636d4ce63c1461003b5780638da5cb5b14610059575b600080fd5b610043610077565b60405161005091906101bd565b60405180910390f35b610061610109565b60405161006e9190610220565b60405180910390f35b6060600180546100869061026a565b80601f01602080910402602001604051908101604052809291908181526020018280546100b29061026a565b80156100ff5780601f106100d4576101008083540402835291602001916100ff565b820191906000526020600020905b8154815290600101906020018083116100e257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600081519050919050565b600082825260208201905092915050565b60005b8381101561016757808201518184015260208101905061014c565b60008484015250505050565b6000601f19601f8301169050919050565b600061018f8261012d565b6101998185610138565b93506101a9818560208601610149565b6101b281610173565b840191505092915050565b600060208201905081810360008301526101d78184610184565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020a826101df565b9050919050565b61021a816101ff565b82525050565b60006020820190506102356000830184610211565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061028257607f821691505b6020821081036102955761029461023b565b5b5091905056fea264697066735822122071163cf749253f921b29799fb8f2b856a099dcdc627d7622ee7536e5e392509464736f6c63430008180033a2646970667358221220bd9fa3c126989a1d6a7a7fe44541a0bf9a4b43a3d2e12733f1208d6beaf0ede564736f6c63430008180033 - gas 7500000 + gas 800000 build block_build b01 parent g00 - gasLimit 7500000 transactions txCreateContractFactory build @@ -69,7 +68,7 @@ transaction_build txCreateContractViaOpCodeCreate block_build b02 parent b01 - gasLimit 10000000 + gasLimit 30000000 transactions txCreateContractViaOpCodeCreate build diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_fail_with_RSKIP_activated.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_fail_with_RSKIP_activated.txt index b89dc32aa78..adfed8b06c0 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_fail_with_RSKIP_activated.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_fail_with_RSKIP_activated.txt @@ -40,12 +40,11 @@ transaction_build txCreateContractFactory receiverAddress 00 value 0 data 608060405234801561001057600080fd5b50610cde806100206000396000f3fe6080604052600436106200002c5760003560e01c8063490af4231462000031578063fdaaa16f1462000067575b600080fd5b6200004f6004803603810190620000499190620002c1565b620000ab565b6040516200005e919062000357565b60405180910390f35b3480156200007457600080fd5b506200009360048036038101906200008d9190620002c1565b620000fb565b604051620000a2919062000357565b60405180910390f35b600080600160001b90508083604051620000c5906200013d565b620000d19190620003fd565b8190604051809103906000f5905080158015620000f2573d6000803e3d6000fd5b50915050919050565b6000816040516200010c906200013d565b620001189190620003fd565b604051809103906000f08015801562000135573d6000803e3d6000fd5b509050919050565b610887806200042283390190565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620001b48262000169565b810181811067ffffffffffffffff82111715620001d657620001d56200017a565b5b80604052505050565b6000620001eb6200014b565b9050620001f98282620001a9565b919050565b600067ffffffffffffffff8211156200021c576200021b6200017a565b5b620002278262000169565b9050602081019050919050565b82818337600083830152505050565b60006200025a6200025484620001fe565b620001df565b90508281526020810184848401111562000279576200027862000164565b5b6200028684828562000234565b509392505050565b600082601f830112620002a657620002a56200015f565b5b8135620002b884826020860162000243565b91505092915050565b600060208284031215620002da57620002d962000155565b5b600082013567ffffffffffffffff811115620002fb57620002fa6200015a565b5b62000309848285016200028e565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200033f8262000312565b9050919050565b620003518162000332565b82525050565b60006020820190506200036e600083018462000346565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015620003b057808201518184015260208101905062000393565b60008484015250505050565b6000620003c98262000374565b620003d581856200037f565b9350620003e781856020860162000390565b620003f28162000169565b840191505092915050565b60006020820190508181036000830152620004198184620003bc565b90509291505056fe60806040523480156200001157600080fd5b506040516200088738038062000887833981810160405281019062000037919062000223565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060019081620000889190620004bf565b5050620005a6565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620000f982620000ae565b810181811067ffffffffffffffff821117156200011b576200011a620000bf565b5b80604052505050565b60006200013062000090565b90506200013e8282620000ee565b919050565b600067ffffffffffffffff821115620001615762000160620000bf565b5b6200016c82620000ae565b9050602081019050919050565b60005b83811015620001995780820151818401526020810190506200017c565b60008484015250505050565b6000620001bc620001b68462000143565b62000124565b905082815260208101848484011115620001db57620001da620000a9565b5b620001e884828562000179565b509392505050565b600082601f830112620002085762000207620000a4565b5b81516200021a848260208601620001a5565b91505092915050565b6000602082840312156200023c576200023b6200009a565b5b600082015167ffffffffffffffff8111156200025d576200025c6200009f565b5b6200026b84828501620001f0565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680620002c757607f821691505b602082108103620002dd57620002dc6200027f565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003477fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000308565b62000353868362000308565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620003a06200039a62000394846200036b565b62000375565b6200036b565b9050919050565b6000819050919050565b620003bc836200037f565b620003d4620003cb82620003a7565b84845462000315565b825550505050565b600090565b620003eb620003dc565b620003f8818484620003b1565b505050565b5b81811015620004205762000414600082620003e1565b600181019050620003fe565b5050565b601f8211156200046f576200043981620002e3565b6200044484620002f8565b8101602085101562000454578190505b6200046c6200046385620002f8565b830182620003fd565b50505b505050565b600082821c905092915050565b6000620004946000198460080262000474565b1980831691505092915050565b6000620004af838362000481565b9150826002028217905092915050565b620004ca8262000274565b67ffffffffffffffff811115620004e657620004e5620000bf565b5b620004f28254620002ae565b620004ff82828562000424565b600060209050601f83116001811462000537576000841562000522578287015190505b6200052e8582620004a1565b8655506200059e565b601f1984166200054786620002e3565b60005b8281101562000571578489015182556001820191506020850194506020810190506200054a565b868310156200059157848901516200058d601f89168262000481565b8355505b6001600288020188555050505b505050505050565b6102d180620005b66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636d4ce63c1461003b5780638da5cb5b14610059575b600080fd5b610043610077565b60405161005091906101bd565b60405180910390f35b610061610109565b60405161006e9190610220565b60405180910390f35b6060600180546100869061026a565b80601f01602080910402602001604051908101604052809291908181526020018280546100b29061026a565b80156100ff5780601f106100d4576101008083540402835291602001916100ff565b820191906000526020600020905b8154815290600101906020018083116100e257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600081519050919050565b600082825260208201905092915050565b60005b8381101561016757808201518184015260208101905061014c565b60008484015250505050565b6000601f19601f8301169050919050565b600061018f8261012d565b6101998185610138565b93506101a9818560208601610149565b6101b281610173565b840191505092915050565b600060208201905081810360008301526101d78184610184565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020a826101df565b9050919050565b61021a816101ff565b82525050565b60006020820190506102356000830184610211565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061028257607f821691505b6020821081036102955761029461023b565b5b5091905056fea264697066735822122071163cf749253f921b29799fb8f2b856a099dcdc627d7622ee7536e5e392509464736f6c63430008180033a2646970667358221220bd9fa3c126989a1d6a7a7fe44541a0bf9a4b43a3d2e12733f1208d6beaf0ede564736f6c63430008180033 - gas 6500000 + gas 800000 build block_build b01 parent g00 - gasLimit 6500000 transactions txCreateContractFactory build @@ -64,12 +63,11 @@ transaction_build txCreateContractViaOpCodeCreate contract txCreateContractFactory # created in txCreateContractFactory value 0 data fdaaa16f0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000b7bd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000 - gas 6500000 + gas 600000 build block_build b02 parent b01 - gasLimit 6500000 transactions txCreateContractViaOpCodeCreate build @@ -79,4 +77,4 @@ block_connect b02 assert_best b02 # Assert balance after have created the contract via opcode create -assert_balance acc1 12739631 \ No newline at end of file +assert_balance acc1 18639631 \ No newline at end of file diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_success_with_initcode_cost.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_success_with_initcode_cost.txt index 25e0987b277..f21c5794fe3 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_success_with_initcode_cost.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_success_with_initcode_cost.txt @@ -40,12 +40,11 @@ transaction_build txCreateContractFactory receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061081b806100206000396000f3fe6080604052600436106100295760003560e01c806313a0432e1461002e5780635d828e9a1461004c575b600080fd5b610036610077565b6040516100439190610133565b60405180910390f35b34801561005857600080fd5b506100616100b5565b60405161006e9190610133565b60405180910390f35b600080600160001b90508060405161008e906100e5565b8190604051809103906000f59050801580156100ae573d6000803e3d6000fd5b5091505090565b60006040516100c3906100e5565b604051809103906000f0801580156100df573d6000803e3d6000fd5b50905090565b6106978061014f83390190565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061011d826100f2565b9050919050565b61012d81610112565b82525050565b60006020820190506101486000830184610124565b9291505056fe608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040518060400160405280600781526020017901000000000000000000000000000000000000000000000000008152506001908161008f91906102e5565b506103b7565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061011657607f821691505b602082108103610129576101286100cf565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026101917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610154565b61019b8683610154565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006101e26101dd6101d8846101b3565b6101bd565b6101b3565b9050919050565b6000819050919050565b6101fc836101c7565b610210610208826101e9565b848454610161565b825550505050565b600090565b610225610218565b6102308184846101f3565b505050565b5b818110156102545761024960008261021d565b600181019050610236565b5050565b601f8211156102995761026a8161012f565b61027384610144565b81016020851015610282578190505b61029661028e85610144565b830182610235565b50505b505050565b600082821c905092915050565b60006102bc6000198460080261029e565b1980831691505092915050565b60006102d583836102ab565b9150826002028217905092915050565b6102ee82610095565b67ffffffffffffffff811115610307576103066100a0565b5b61031182546100fe565b61031c828285610258565b600060209050601f83116001811461034f576000841561033d578287015190505b61034785826102c9565b8655506103af565b601f19841661035d8661012f565b60005b8281101561038557848901518255600182019150602085019450602081019050610360565b868310156103a2578489015161039e601f8916826102ab565b8355505b6001600288020188555050505b505050505050565b6102d1806103c66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636d4ce63c1461003b5780638da5cb5b14610059575b600080fd5b610043610077565b60405161005091906101bd565b60405180910390f35b610061610109565b60405161006e9190610220565b60405180910390f35b6060600180546100869061026a565b80601f01602080910402602001604051908101604052809291908181526020018280546100b29061026a565b80156100ff5780601f106100d4576101008083540402835291602001916100ff565b820191906000526020600020905b8154815290600101906020018083116100e257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600081519050919050565b600082825260208201905092915050565b60005b8381101561016757808201518184015260208101905061014c565b60008484015250505050565b6000601f19601f8301169050919050565b600061018f8261012d565b6101998185610138565b93506101a9818560208601610149565b6101b281610173565b840191505092915050565b600060208201905081810360008301526101d78184610184565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020a826101df565b9050919050565b61021a816101ff565b82525050565b60006020820190506102356000830184610211565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061028257607f821691505b6020821081036102955761029461023b565b5b5091905056fea264697066735822122049d311e90db3a44f73eb3cf557d8a87356f3317e1459eda82ef2a687d7a18b3a64736f6c63430008180033a26469706673582212202aef06f0c7e6b0968848c66b5849bb66c58dda606e71a6ad568f4ab402c878bd64736f6c63430008180033 - gas 4700000 + gas 600000 build block_build b01 parent g00 - gasLimit 6500000 transactions txCreateContractFactory build @@ -64,12 +63,11 @@ transaction_build txCreateContractViaOpCode contract txCreateContractFactory # created in txCreateContractFactory value 0 data 5d828e9a - gas 1000000 + gas 600000 build block_build b02 parent b01 - gasLimit 6500000 transactions txCreateContractViaOpCode build diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_success_without_initcode_cost.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_success_without_initcode_cost.txt index 64afce4caba..c5c14d1221c 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_success_without_initcode_cost.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/CREATE_opcode_validation_success_without_initcode_cost.txt @@ -40,12 +40,11 @@ transaction_build txCreateContractFactory receiverAddress 00 value 0 data 608060405234801561001057600080fd5b5061081b806100206000396000f3fe6080604052600436106100295760003560e01c806313a0432e1461002e5780635d828e9a1461004c575b600080fd5b610036610077565b6040516100439190610133565b60405180910390f35b34801561005857600080fd5b506100616100b5565b60405161006e9190610133565b60405180910390f35b600080600160001b90508060405161008e906100e5565b8190604051809103906000f59050801580156100ae573d6000803e3d6000fd5b5091505090565b60006040516100c3906100e5565b604051809103906000f0801580156100df573d6000803e3d6000fd5b50905090565b6106978061014f83390190565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061011d826100f2565b9050919050565b61012d81610112565b82525050565b60006020820190506101486000830184610124565b9291505056fe608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506040518060400160405280600781526020017901000000000000000000000000000000000000000000000000008152506001908161008f91906102e5565b506103b7565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061011657607f821691505b602082108103610129576101286100cf565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b6000600883026101917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82610154565b61019b8683610154565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006101e26101dd6101d8846101b3565b6101bd565b6101b3565b9050919050565b6000819050919050565b6101fc836101c7565b610210610208826101e9565b848454610161565b825550505050565b600090565b610225610218565b6102308184846101f3565b505050565b5b818110156102545761024960008261021d565b600181019050610236565b5050565b601f8211156102995761026a8161012f565b61027384610144565b81016020851015610282578190505b61029661028e85610144565b830182610235565b50505b505050565b600082821c905092915050565b60006102bc6000198460080261029e565b1980831691505092915050565b60006102d583836102ab565b9150826002028217905092915050565b6102ee82610095565b67ffffffffffffffff811115610307576103066100a0565b5b61031182546100fe565b61031c828285610258565b600060209050601f83116001811461034f576000841561033d578287015190505b61034785826102c9565b8655506103af565b601f19841661035d8661012f565b60005b8281101561038557848901518255600182019150602085019450602081019050610360565b868310156103a2578489015161039e601f8916826102ab565b8355505b6001600288020188555050505b505050505050565b6102d1806103c66000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636d4ce63c1461003b5780638da5cb5b14610059575b600080fd5b610043610077565b60405161005091906101bd565b60405180910390f35b610061610109565b60405161006e9190610220565b60405180910390f35b6060600180546100869061026a565b80601f01602080910402602001604051908101604052809291908181526020018280546100b29061026a565b80156100ff5780601f106100d4576101008083540402835291602001916100ff565b820191906000526020600020905b8154815290600101906020018083116100e257829003601f168201915b5050505050905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600081519050919050565b600082825260208201905092915050565b60005b8381101561016757808201518184015260208101905061014c565b60008484015250505050565b6000601f19601f8301169050919050565b600061018f8261012d565b6101998185610138565b93506101a9818560208601610149565b6101b281610173565b840191505092915050565b600060208201905081810360008301526101d78184610184565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061020a826101df565b9050919050565b61021a816101ff565b82525050565b60006020820190506102356000830184610211565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168061028257607f821691505b6020821081036102955761029461023b565b5b5091905056fea264697066735822122049d311e90db3a44f73eb3cf557d8a87356f3317e1459eda82ef2a687d7a18b3a64736f6c63430008180033a26469706673582212202aef06f0c7e6b0968848c66b5849bb66c58dda606e71a6ad568f4ab402c878bd64736f6c63430008180033 - gas 4700000 + gas 600000 build block_build b01 parent g00 - gasLimit 6500000 transactions txCreateContractFactory build @@ -64,12 +63,11 @@ transaction_build txCreateContractViaOpCodeCreate contract txCreateContractFactory # created in txCreateContractFactory value 0 data 5d828e9a - gas 1000000 + gas 600000 build block_build b02 parent b01 - gasLimit 6500000 transactions txCreateContractViaOpCodeCreate build diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_doesnt_fails_due_not_enough_gas_with_rskip_deactivated.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_doesnt_fails_due_not_enough_gas_with_rskip_deactivated.txt index 828794eb76f..88b426499de 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_doesnt_fails_due_not_enough_gas_with_rskip_deactivated.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_doesnt_fails_due_not_enough_gas_with_rskip_deactivated.txt @@ -32,7 +32,7 @@ transaction_build txCreateContract block_build b01 parent g00 - gasLimit 6500000 + gasLimit 15000000 transactions txCreateContract build diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_activated.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_activated.txt index caa4dde29e5..8b1b8330446 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_activated.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_activated.txt @@ -32,7 +32,7 @@ transaction_build txCreateContract block_build b01 parent g00 - gasLimit 6500000 + gasLimit 15000000 transactions txCreateContract build diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_deactivated.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_deactivated.txt index a244da6d7c5..94a5b270041 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_deactivated.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_deactivated.txt @@ -32,7 +32,7 @@ transaction_build txCreateContract block_build b01 parent g00 - gasLimit 6500000 + gasLimit 15000000 transactions txCreateContract build diff --git a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_deactivated_with_initcode_size_max_reached.txt b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_deactivated_with_initcode_size_max_reached.txt index 826b18d09eb..20b4e32f8e4 100644 --- a/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_deactivated_with_initcode_size_max_reached.txt +++ b/rskj-core/src/test/resources/dsl/initcode_size_rskip438/tx_creation_validation_success_with_rskip_deactivated_with_initcode_size_max_reached.txt @@ -32,7 +32,7 @@ transaction_build txCreateContract block_build b01 parent g00 - gasLimit 30000000 + gasLimit 90000000 transactions txCreateContract build From bb5fb5c86f1b9db3c42fdb30cc55a2c3e6b80dc8 Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 27 Sep 2024 11:19:16 -0300 Subject: [PATCH 74/89] Update test in BitcoinUtilsTest to use deterministic keys --- .../co/rsk/peg/bitcoin/BitcoinTestUtils.java | 23 +++++++++---------- .../co/rsk/peg/bitcoin/BitcoinUtilsTest.java | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinTestUtils.java b/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinTestUtils.java index 4ba69f1fc7c..146d389f73f 100644 --- a/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinTestUtils.java +++ b/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinTestUtils.java @@ -1,28 +1,27 @@ package co.rsk.peg.bitcoin; +import static co.rsk.bitcoinj.script.ScriptBuilder.createP2SHOutputScript; +import static co.rsk.peg.bitcoin.BitcoinUtils.extractRedeemScriptFromInput; + import co.rsk.bitcoinj.core.*; import co.rsk.bitcoinj.crypto.TransactionSignature; import co.rsk.bitcoinj.script.*; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; - -import co.rsk.bitcoinj.script.ScriptChunk; import org.bouncycastle.util.encoders.Hex; import org.ethereum.crypto.HashUtil; -import static co.rsk.bitcoinj.script.ScriptBuilder.createP2SHOutputScript; -import static co.rsk.peg.bitcoin.BitcoinUtils.extractRedeemScriptFromInput; - public class BitcoinTestUtils { + public static BtcECKey getBtcEcKeyFromSeed(String seed) { + byte[] serializedSeed = HashUtil.keccak256(seed.getBytes(StandardCharsets.UTF_8)); + return BtcECKey.fromPrivate(serializedSeed); + } + public static List getBtcEcKeysFromSeeds(String[] seeds, boolean sorted) { - List keys = Arrays - .stream(seeds) - .map(seed -> BtcECKey.fromPrivate( - HashUtil.keccak256(seed.getBytes(StandardCharsets.UTF_8)))) + List keys = Arrays.stream(seeds) + .map(BitcoinTestUtils::getBtcEcKeyFromSeed) .collect(Collectors.toList()); if (sorted) { diff --git a/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java b/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java index 5740f641af0..4d908493635 100644 --- a/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java +++ b/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java @@ -333,7 +333,7 @@ void removeSignaturesFromTransaction_whenTransactionIsSegwit_shouldThrowIllegalA void removeSignaturesFromTransaction_whenTransactionInputsDoNotHaveP2shMultiSigInputScript_shouldThrowIllegalArgumentException() { // arrange BtcTransaction transaction = new BtcTransaction(btcMainnetParams); - BtcECKey pubKey = new BtcECKey(); + BtcECKey pubKey = BitcoinTestUtils.getBtcEcKeyFromSeed("abc"); Script p2pkhScriptSig = ScriptBuilder.createInputScript(null, pubKey); transaction.addInput(BitcoinTestUtils.createHash(1), 0, p2pkhScriptSig); From 8388572b89abfad40fdb382e009d1c04e7c69440 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 27 Sep 2024 13:35:41 +0300 Subject: [PATCH 75/89] build: run rit check overnight; allow plus symbol in branch names --- .github/workflows/rit.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rit.yml b/.github/workflows/rit.yml index 889ea657130..9fac1b2cf9d 100644 --- a/.github/workflows/rit.yml +++ b/.github/workflows/rit.yml @@ -1,6 +1,8 @@ name: Rootstock Integration Tests on: + schedule: + - cron: '0 0 * * *' pull_request: types: [ opened, synchronize, reopened ] branches: ["master", "*-rc"] @@ -37,7 +39,7 @@ jobs: run: | PR_DESCRIPTION=pr-description.txt - ALLOWED_BRANCH_CHARACTERS='[-./0-9A-Z_a-z]' + ALLOWED_BRANCH_CHARACTERS='[-+./0-9A-Z_a-z]' default_rskj_branch=master default_powpeg_branch=master From 5511ec4f81179664bc5794a73c53bdfcb310b00c Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 26 Jul 2024 15:16:53 +0300 Subject: [PATCH 76/89] refactor: update Bytes and BytesSlice to 'mimic' System.arraycopy and Arrays.copyOfRange; add more tests --- .../java/co/rsk/core/types/bytes/Bytes.java | 5 +- .../co/rsk/core/types/bytes/BytesSlice.java | 84 +++++++++++++++-- .../core/types/bytes/HexPrintableBytes.java | 2 +- .../main/java/co/rsk/util/StringUtils.java | 2 + .../rsk/core/types/bytes/BytesSliceTest.java | 85 +++++++++++++++++- .../co/rsk/core/types/bytes/BytesTest.java | 89 ++++++++++++++++++- .../src/test/java/co/rsk/util/Functions.java | 30 +++++++ 7 files changed, 279 insertions(+), 18 deletions(-) create mode 100644 rskj-core/src/test/java/co/rsk/util/Functions.java diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java index ce988888068..cb4c16d17d3 100644 --- a/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java +++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/Bytes.java @@ -23,7 +23,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Arrays; import java.util.Objects; /** @@ -140,8 +139,8 @@ public byte byteAt(int index) { } @Override - public byte[] copyArrayOfRange(int from, int to) { - return Arrays.copyOfRange(byteArray, from, to); + public void arraycopy(int srcPos, byte[] dest, int destPos, int length) { + System.arraycopy(byteArray, srcPos, dest, destPos, length); } @Override diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java index 5210ff8eea0..ac3616fd8f0 100644 --- a/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java +++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/BytesSlice.java @@ -18,11 +18,62 @@ package co.rsk.core.types.bytes; +import java.util.Arrays; + /** * A {@link BytesSlice} is a subsequence of bytes backed by another broader byte sequence. */ public interface BytesSlice extends HexPrintableBytes { + /** + * Copies an array from the {@link BytesSlice} source, beginning at the + * specified position, to the specified position of the destination array. + * A subsequence of array components are copied from this instance to the + * destination array referenced by {@code dest}. The number of components + * copied is equal to the {@code length} argument. The components at + * positions {@code srcPos} through {@code srcPos+length-1} in the source + * array are copied into positions {@code destPos} through + * {@code destPos+length-1}, respectively, of the destination + * array. + *

+ * If the underlying byte array and {@code dest} argument refer to the + * same array object, then the copying is performed as if the + * components at positions {@code srcPos} through + * {@code srcPos+length-1} were first copied to a temporary + * array with {@code length} components and then the contents of + * the temporary array were copied into positions + * {@code destPos} through {@code destPos+length-1} of the + * destination array. + *

+ * If {@code dest} is {@code null}, then a + * {@code NullPointerException} is thrown. + *

+ * Otherwise, if any of the following is true, an + * {@code IndexOutOfBoundsException} is + * thrown and the destination is not modified: + *

    + *
  • The {@code srcPos} argument is negative. + *
  • The {@code destPos} argument is negative. + *
  • The {@code length} argument is negative. + *
  • {@code srcPos+length} is greater than + * {@code src.length}, the length of the source array. + *
  • {@code destPos+length} is greater than + * {@code dest.length}, the length of the destination array. + *
+ * + *

+ * Note: this method mimics behaviour of {@link System#arraycopy(Object, int, Object, int, int)} + * + * @param srcPos starting position in the source array. + * @param dest the destination array. + * @param destPos starting position in the destination data. + * @param length the number of array elements to be copied. + * @throws IndexOutOfBoundsException if copying would cause + * access of data outside array bounds. + * @throws NullPointerException if {@code dest} is {@code null}. + */ + void arraycopy(int srcPos, byte[] dest, int destPos, int length); + /** * Copies the specified range of the specified array into a new array. * The initial index of the range (from) must lie between zero @@ -37,17 +88,30 @@ public interface BytesSlice extends HexPrintableBytes { * greater than or equal to original.length - from. The length * of the returned array will be to - from. * + *

+ * Note: this method mimics behaviour of {@link Arrays#copyOfRange(Object[], int, int)} + * * @param from the initial index of the range to be copied, inclusive * @param to the final index of the range to be copied, exclusive. * (This index may lie outside the array.) * @return a new array containing the specified range from the original array, * truncated or padded with zeros to obtain the required length - * @throws ArrayIndexOutOfBoundsException if {@code from < 0} + * @throws IndexOutOfBoundsException if {@code from < 0} * or {@code from > original.length} * @throws IllegalArgumentException if from > to - * @throws NullPointerException if original is null */ - byte[] copyArrayOfRange(int from, int to); + default byte[] copyArrayOfRange(int from, int to) { + if (from < 0 || from > length()) { + throw new IndexOutOfBoundsException("invalid 'from': " + from); + } + int newLength = to - from; + if (newLength < 0) { + throw new IllegalArgumentException(from + " > " + to); + } + byte[] copy = new byte[newLength]; + arraycopy(from, copy, 0, Math.min(length() - from, newLength)); + return copy; + } default byte[] copyArray() { return copyArrayOfRange(0, length()); @@ -104,11 +168,17 @@ public byte byteAt(int index) { } @Override - public byte[] copyArrayOfRange(int from, int to) { - if (from < 0 || from > to || to > length()) { - throw new IndexOutOfBoundsException("invalid 'from' and/or 'to': [" + from + ";" + to + ")"); + public void arraycopy(int srcPos, byte[] dest, int destPos, int length) { + if (length < 0) { + throw new IndexOutOfBoundsException("invalid 'length': " + length); + } + if (srcPos < 0 || srcPos + length > length()) { + throw new IndexOutOfBoundsException("invalid 'srcPos' and/or 'length': [" + srcPos + ";" + length + ")"); + } + if (destPos < 0 || destPos + length > dest.length) { + throw new IndexOutOfBoundsException("invalid 'destPos' and/or 'length': [" + destPos + ";" + length + ")"); } - return originBytes.copyArrayOfRange(this.from + from, this.from + to); + originBytes.arraycopy(this.from + srcPos, dest, destPos, length); } @Override diff --git a/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java b/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java index d700c0cc6a6..3f91f918eda 100644 --- a/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java +++ b/rskj-core/src/main/java/co/rsk/core/types/bytes/HexPrintableBytes.java @@ -77,7 +77,7 @@ public String toFormattedString(@Nonnull HexPrintableBytes printableBytes, int o } if (length > 32) { - return printableBytes.toHexString(off, 15) + ".." + printableBytes.toHexString(off + length - 15, 15); + return printableBytes.toHexString(off, 16) + ".." + printableBytes.toHexString(off + length - 15, 15); } return printableBytes.toHexString(off, length); } diff --git a/rskj-core/src/main/java/co/rsk/util/StringUtils.java b/rskj-core/src/main/java/co/rsk/util/StringUtils.java index a7c58a322b3..02ef4106555 100644 --- a/rskj-core/src/main/java/co/rsk/util/StringUtils.java +++ b/rskj-core/src/main/java/co/rsk/util/StringUtils.java @@ -24,6 +24,8 @@ public class StringUtils { private static final int DEFAULT_MAX_LEN = 64; + private StringUtils() { /* hidden */ } + public static String trim(@Nullable String src) { return trim(src, DEFAULT_MAX_LEN); } diff --git a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java index 93707953604..001aee51f35 100644 --- a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java +++ b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java @@ -19,17 +19,56 @@ package co.rsk.core.types.bytes; +import co.rsk.util.Functions; import org.junit.jupiter.api.Test; +import java.util.Arrays; + import static org.junit.jupiter.api.Assertions.*; class BytesSliceTest { + @Test + void testBytesLength() { + assertEquals(0, Bytes.of(new byte[]{}).slice(0, 0).length()); + assertEquals(0, Bytes.of(new byte[]{1}).slice(0, 0).length()); + assertEquals(1, Bytes.of(new byte[]{1}).slice(0, 1).length()); + assertEquals(0, Bytes.of(new byte[]{1,2,3}).slice(1, 1).length()); + assertEquals(1, Bytes.of(new byte[]{1,2,3}).slice(1, 2).length()); + assertEquals(2, Bytes.of(new byte[]{1,2,3}).slice(0, 2).length()); + assertEquals(3, Bytes.of(new byte[]{1,2,3}).slice(0, 3).length()); + } + + @Test + void testBytesAt() { + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{}).slice(0, 0).byteAt(0)); + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).slice(0, 1).byteAt(1)); + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).slice(0, 1).byteAt(-1)); + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1,2,3}).slice(1, 2).byteAt(1)); + assertEquals(1, Bytes.of(new byte[]{1}).slice(0, 1).byteAt(0)); + assertEquals(2, Bytes.of(new byte[]{1,2}).slice(0, 2).byteAt(1)); + assertEquals(2, Bytes.of(new byte[]{1,2,3}).slice(1, 2).byteAt(0)); + assertEquals(4, Bytes.of(new byte[]{1,2,3,4}).slice(2, 4).byteAt(1)); + } + + @Test + void testBytesSliceArraycopy() { + checkArraycopy((src, srcPos, dest, destPos, length) -> Bytes.of((byte[]) src).slice(1, 4).arraycopy(srcPos, (byte[]) dest, destPos, length)); + } + + @Test + void testBytesSliceArraycopyMimicsSystemOne() { + checkArraycopy((src, srcPos, dest, destPos, length) -> System.arraycopy(Arrays.copyOfRange((byte[]) src, 1, 4), srcPos, dest, destPos, length)); + } + @Test void testCopyArrayOfRange() { - byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6}; - byte[] expectedResult = new byte[]{3, 4, 5}; - assertArrayEquals(expectedResult, Bytes.of(bArray).slice(0, bArray.length).copyArrayOfRange(2, 5)); + checkCopyOfRange(BytesSlice::copyArrayOfRange, (origin, from, to) -> Bytes.of(origin).slice(from, to)); + } + + @Test + void testCopyArrayOfRangeMimicsSystemOne() { + checkCopyOfRange(Arrays::copyOfRange, Arrays::copyOfRange); } @Test @@ -74,4 +113,44 @@ void testEmptySlice() { assertEquals(0, actualResult.length()); assertArrayEquals(expectedResult, actualResult.copyArray()); } + + private static void checkArraycopy(Functions.Action5 fun) { + byte[] dest = new byte[3]; + byte[] origin = new byte[]{1,2,3,4,5}; + + assertThrows(NullPointerException.class, () -> fun.apply(origin, 0, null, 0, 3)); + + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, -1, dest, 0, 3)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 0, dest, -1, 3)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 0, dest, 0, -1)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 0, dest, 0, 4)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 1, dest, 0, 3)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 0, dest, 1, 3)); + + assertArrayEquals(new byte[3], dest); // yet unmodified + + fun.apply(origin, 0, dest, 0, 3); + assertArrayEquals(new byte[]{2,3,4}, dest); + + byte[] dest2 = new byte[3]; + fun.apply(origin, 1, dest2, 1, 1); + assertArrayEquals(new byte[]{0,3,0}, dest2); + } + + private static void checkCopyOfRange(Functions.Function3 fun, + Functions.Function3 slicer) { + byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6}; + + assertEquals(bArray.length, fun.apply(slicer.apply(bArray, 0, 6), 0, 6).length); + assertNotSame(bArray, fun.apply(slicer.apply(bArray, 0, 6), 0, 6)); + + assertArrayEquals(new byte[]{3, 4, 5}, fun.apply(slicer.apply(bArray, 0, 6), 2, 5)); + assertArrayEquals(new byte[]{3, 4}, fun.apply(slicer.apply(bArray, 1, 5), 1, 3)); + assertArrayEquals(new byte[]{3, 4, 5, 0, 0, 0, 0}, fun.apply(slicer.apply(bArray, 1, 5), 1, 8)); + assertArrayEquals(new byte[]{}, fun.apply(slicer.apply(bArray, 1, 5), 4, 4)); + + assertThrows(IllegalArgumentException.class, () -> fun.apply(slicer.apply(bArray, 1, 5), 1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(slicer.apply(bArray, 1, 5), -1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(slicer.apply(bArray, 1, 5), 5, 5)); + } } diff --git a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java index b60ca431ddc..7cce5532f17 100644 --- a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java +++ b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java @@ -19,10 +19,13 @@ package co.rsk.core.types.bytes; +import co.rsk.util.Functions; import org.ethereum.TestUtils; import org.ethereum.util.ByteUtil; import org.junit.jupiter.api.Test; +import java.util.Arrays; + import static org.junit.jupiter.api.Assertions.*; class BytesTest { @@ -34,6 +37,41 @@ void testBytesOf() { assertNotNull(Bytes.of(new byte[]{1})); } + @Test + void testBytesLength() { + assertEquals(0, Bytes.of(new byte[]{}).length()); + assertEquals(1, Bytes.of(new byte[]{1}).length()); + } + + @Test + void testBytesAt() { + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{}).byteAt(0)); + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).byteAt(1)); + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).byteAt(-1)); + assertEquals(1, Bytes.of(new byte[]{1}).byteAt(0)); + assertEquals(2, Bytes.of(new byte[]{1,2}).byteAt(1)); + } + + @Test + void testBytesArraycopy() { + checkArraycopy((src, srcPos, dest, destPos, length) -> Bytes.of((byte[]) src).arraycopy(srcPos, (byte[]) dest, destPos, length)); + } + + @Test + void testBytesArraycopyMimicsSystemOne() { + checkArraycopy(System::arraycopy); + } + + @Test + void testCopyArrayOfRange() { + checkCopyOfRange((original, from, to) -> Bytes.of(original).copyArrayOfRange(from, to)); + } + + @Test + void testCopyArrayOfRangeMimicsSystemOne() { + checkCopyOfRange(Arrays::copyOfRange); + } + @Test void testToPrintableString() { assertEquals("0a", Bytes.toPrintableString(new byte[]{10})); @@ -83,13 +121,17 @@ void testShortEnoughBytesToString() { @Test void testLongBytesToString() { - byte[] bArray1 = TestUtils.generateBytes("hash1",15); + byte[] bArray1 = TestUtils.generateBytes("hash1",16); byte[] bArray2 = TestUtils.generateBytes("hash2",15); byte[] finalArray = ByteUtil.merge(bArray1, new byte[]{1, 2, 3}, bArray2); - assertEquals(33, finalArray.length); + assertEquals(34, finalArray.length); + + Bytes bytes = Bytes.of(finalArray); + + assertEquals(64, String.format("%s", bytes).length()); - String actualMessage = String.format("Some '%s' hex", Bytes.of(finalArray)); + String actualMessage = String.format("Some '%s' hex", bytes); String expectedMessage = "Some '" + ByteUtil.toHexString(bArray1) + ".." @@ -116,4 +158,43 @@ void testEmptyBytesToString() { assertEquals(expectedMessage, actualMessage); } -} \ No newline at end of file + + private static void checkArraycopy(Functions.Action5 fun) { + byte[] dest = new byte[5]; + byte[] origin = new byte[]{1,2,3,4,5}; + + assertThrows(NullPointerException.class, () -> fun.apply(origin, 0, null, 0, 5)); + + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, -1, dest, 0, 5)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 0, dest, -1, 5)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 0, dest, 0, -1)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 0, dest, 0, 6)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 1, dest, 0, 5)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(origin, 0, dest, 1, 5)); + + assertArrayEquals(new byte[5], dest); // yet unmodified + + fun.apply(origin, 0, dest, 0, 5); + assertArrayEquals(new byte[]{1,2,3,4,5}, dest); + + byte[] dest2 = new byte[5]; + fun.apply(origin, 1, dest2, 1, 3); + assertArrayEquals(new byte[]{0,2,3,4,0}, dest2); + } + + private static void checkCopyOfRange(Functions.Function3 fun) { + byte[] bArray = new byte[]{1, 2, 3, 4, 5}; + + assertEquals(bArray.length, fun.apply(bArray, 0, 5).length); + assertNotSame(bArray, fun.apply(bArray, 0, 5)); + + assertArrayEquals(new byte[]{2, 3, 4}, fun.apply(bArray, 1, 4)); + assertArrayEquals(new byte[]{2}, fun.apply(bArray, 1, 2)); + assertArrayEquals(new byte[]{2, 3, 4, 5, 0, 0, 0}, fun.apply(bArray, 1, 8)); + assertArrayEquals(new byte[]{}, fun.apply(bArray, 3, 3)); + + assertThrows(IllegalArgumentException.class, () -> fun.apply(bArray, 1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(bArray, -1, 0)); + assertThrows(IndexOutOfBoundsException.class, () -> fun.apply(bArray, 6, 6)); + } +} diff --git a/rskj-core/src/test/java/co/rsk/util/Functions.java b/rskj-core/src/test/java/co/rsk/util/Functions.java new file mode 100644 index 00000000000..de687407e28 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/util/Functions.java @@ -0,0 +1,30 @@ +/* + * This file is part of RskJ + * Copyright (C) 2024 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package co.rsk.util; + +public interface Functions { + interface Function3 { + TResult apply(T1 arg1, T2 arg2, T3 arg3); + } + + interface Action5 { + void apply(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + } +} From acf6287c51c3d8ea9cc5823574c4addf5117cf37 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 26 Jul 2024 15:20:58 +0300 Subject: [PATCH 77/89] perf: improve decoding in CallTransaction --- .../org/ethereum/core/CallTransaction.java | 23 ++++++++-------- .../org/ethereum/solidity/SolidityType.java | 27 ++++++++++--------- .../main/java/org/ethereum/util/Utils.java | 9 ++++--- .../java/co/rsk/core/CallTransactionTest.java | 3 ++- .../java/co/rsk/core/TransactionTest.java | 3 ++- .../org/ethereum/core/TransactionTest.java | 3 ++- .../ethereum/solidity/SolidityTypeTest.java | 21 ++++++++------- .../java/org/ethereum/util/UtilsTest.java | 27 +++++++++---------- 8 files changed, 61 insertions(+), 55 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/core/CallTransaction.java b/rskj-core/src/main/java/org/ethereum/core/CallTransaction.java index 4c6cf7a8296..50750a55ba2 100644 --- a/rskj-core/src/main/java/org/ethereum/core/CallTransaction.java +++ b/rskj-core/src/main/java/org/ethereum/core/CallTransaction.java @@ -20,6 +20,8 @@ package org.ethereum.core; import co.rsk.core.RskAddress; +import co.rsk.core.types.bytes.Bytes; +import co.rsk.core.types.bytes.BytesSlice; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonInclude; @@ -36,7 +38,6 @@ import java.util.Arrays; import static java.lang.String.format; -import static org.apache.commons.lang3.ArrayUtils.subarray; import static org.apache.commons.lang3.StringUtils.stripEnd; import static org.ethereum.util.ByteUtil.longToBytesNoLeadZeroes; @@ -119,9 +120,9 @@ public static Type getType(String typeName) { */ public abstract byte[] encode(Object value); - public abstract Object decode(byte[] encoded, int offset); + public abstract Object decode(BytesSlice encoded, int offset); - public Object decode(byte[] encoded) { + public Object decode(BytesSlice encoded) { return decode(encoded, 0); } @@ -187,12 +188,12 @@ public byte[] encode(Object value) { } @Override - public Object decode(byte[] encoded, int offset) { + public Object decode(BytesSlice encoded, int offset) { return decodeInt(encoded, offset); } - public static BigInteger decodeInt(byte[] encoded, int offset) { - return new BigInteger(Arrays.copyOfRange(encoded, offset, offset + 32)); + public static BigInteger decodeInt(BytesSlice encoded, int offset) { + return new BigInteger(encoded.copyArrayOfRange(offset, offset + 32)); } public static byte[] encodeInt(int i) { @@ -222,7 +223,7 @@ public byte[] encode(Object value) { } @Override - public Object decode(byte[] encoded, int offset) { + public Object decode(BytesSlice encoded, int offset) { return Boolean.valueOf(((Number) super.decode(encoded, offset)).intValue() != 0); } } @@ -350,7 +351,7 @@ public Object[] decodeEventData(byte[] encodedData) { checkFunctionType(FunctionType.event); Param[] dataInputs = Arrays.stream(inputs).filter(i -> !i.indexed).toArray(Param[]::new); - return decode(encodedData, dataInputs); + return decode(Bytes.of(encodedData), dataInputs); } private void checkFunctionType(FunctionType expected) { @@ -405,7 +406,7 @@ public byte[] encodeOutputs(Object... args) { return encodeArguments(outputs, args); } - private Object[] decode(byte[] encoded, Param[] params) { + private Object[] decode(BytesSlice encoded, Param[] params) { Object[] ret = new Object[params.length]; int off = 0; @@ -421,11 +422,11 @@ private Object[] decode(byte[] encoded, Param[] params) { } public Object[] decode(byte[] encoded) { - return decode(subarray(encoded, 4, encoded.length), inputs); + return decode(Bytes.of(encoded).slice(4, encoded.length), inputs); } public Object[] decodeResult(byte[] encodedRet) { - return decode(encodedRet, outputs); + return decode(Bytes.of(encodedRet), outputs); } public String formatSignature() { diff --git a/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java b/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java index a4ef7aa34be..23d6197fc6c 100644 --- a/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java +++ b/rskj-core/src/main/java/org/ethereum/solidity/SolidityType.java @@ -20,6 +20,7 @@ package org.ethereum.solidity; import co.rsk.core.types.bytes.Bytes; +import co.rsk.core.types.bytes.BytesSlice; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import org.ethereum.util.ByteUtil; @@ -101,9 +102,9 @@ public static SolidityType getType(String typeName) { */ public abstract byte[] encode(Object value); - public abstract Object decode(byte[] encoded, int offset); + public abstract Object decode(BytesSlice encoded, int offset); - public Object decode(byte[] encoded) { + public Object decode(BytesSlice encoded) { return decode(encoded, 0); } @@ -196,7 +197,7 @@ public byte[] encodeList(List l) { } @Override - public Object[] decode(byte[] encoded, int offset) { + public Object[] decode(BytesSlice encoded, int offset) { Utils.validateArrayAllegedSize(encoded, offset, getFixedSize()); Object[] result = new Object[size]; for (int i = 0; i < size; i++) { @@ -247,8 +248,8 @@ public byte[] encodeList(List l) { } @Override - public Object decode(byte[] encoded, int origOffset) { - if (encoded.length == 0) { + public Object decode(BytesSlice encoded, int origOffset) { + if (encoded.length() == 0) { return new Object[0]; } int len = IntType.decodeInt(encoded, origOffset).intValue(); @@ -299,7 +300,7 @@ public byte[] encode(Object value) { } @Override - public Object decode(byte[] encoded, int offset) { + public Object decode(BytesSlice encoded, int offset) { int len = IntType.decodeInt(encoded, offset).intValue(); offset += IntType.INT_SIZE; return Utils.safeCopyOfRange(encoded, offset, len); @@ -325,7 +326,7 @@ public byte[] encode(Object value) { } @Override - public Object decode(byte[] encoded, int offset) { + public Object decode(BytesSlice encoded, int offset) { return new String((byte[]) super.decode(encoded, offset), StandardCharsets.UTF_8); } } @@ -357,7 +358,7 @@ public byte[] encode(Object value) { } @Override - public Object decode(byte[] encoded, int offset) { + public Object decode(BytesSlice encoded, int offset) { return Utils.safeCopyOfRange(encoded, offset, getFixedSize()); } } @@ -383,7 +384,7 @@ public byte[] encode(Object value) { } @Override - public Object decode(byte[] encoded, int offset) { + public Object decode(BytesSlice encoded, int offset) { BigInteger asBigInteger = (BigInteger) super.decode(encoded, offset); return DataWord.valueOf(asBigInteger.toByteArray()); } @@ -434,14 +435,14 @@ public byte[] encode(Object value) { } @Override - public Object decode(byte[] encoded, int offset) { + public Object decode(BytesSlice encoded, int offset) { return decodeInt(encoded, offset); } - public static BigInteger decodeInt(byte[] encoded, int offset) { + public static BigInteger decodeInt(BytesSlice encoded, int offset) { // This is here because getGasForData might send an empty payload which will produce an exception // But currently the bridge would return the cost of RELEASE_BTC in this situation - if (encoded.length == 0) { + if (encoded.length() == 0) { return BigInteger.ZERO; } return new BigInteger(Utils.safeCopyOfRange(encoded, offset, INT_SIZE)); @@ -474,7 +475,7 @@ public byte[] encode(Object value) { } @Override - public Object decode(byte[] encoded, int offset) { + public Object decode(BytesSlice encoded, int offset) { return Boolean.valueOf(((Number) super.decode(encoded, offset)).intValue() != 0); } } diff --git a/rskj-core/src/main/java/org/ethereum/util/Utils.java b/rskj-core/src/main/java/org/ethereum/util/Utils.java index 0738853cc32..5f1682cea68 100644 --- a/rskj-core/src/main/java/org/ethereum/util/Utils.java +++ b/rskj-core/src/main/java/org/ethereum/util/Utils.java @@ -19,6 +19,7 @@ package org.ethereum.util; +import co.rsk.core.types.bytes.BytesSlice; import org.bouncycastle.util.encoders.DecoderException; import org.bouncycastle.util.encoders.Hex; import java.lang.reflect.Array; @@ -206,15 +207,15 @@ public static boolean contains(List list, byte[] valueToFind) { return false; } - public static void validateArrayAllegedSize(byte[] data, int offset, int allegedSize) { - if (data.length < Math.addExact(allegedSize, offset)) { + public static void validateArrayAllegedSize(BytesSlice data, int offset, int allegedSize) { + if (data.length() < Math.addExact(allegedSize, offset)) { throw new IllegalArgumentException("The specified size exceeds the size of the payload"); } } - public static byte[] safeCopyOfRange(byte[] data, int from, int size) { + public static byte[] safeCopyOfRange(BytesSlice data, int from, int size) { validateArrayAllegedSize(data, from, size); - return Arrays.copyOfRange(data, from, from + size); + return data.copyArrayOfRange(from, from + size); } public static boolean isDecimalString(String s) { diff --git a/rskj-core/src/test/java/co/rsk/core/CallTransactionTest.java b/rskj-core/src/test/java/co/rsk/core/CallTransactionTest.java index e3320bf3380..4fe0c0c870b 100644 --- a/rskj-core/src/test/java/co/rsk/core/CallTransactionTest.java +++ b/rskj-core/src/test/java/co/rsk/core/CallTransactionTest.java @@ -18,6 +18,7 @@ package co.rsk.core; +import co.rsk.core.types.bytes.Bytes; import org.bouncycastle.util.encoders.Hex; import org.ethereum.core.CallTransaction; import org.ethereum.solidity.SolidityType; @@ -115,7 +116,7 @@ void decodeString() { // string 104, 101, 108, 108, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - Assertions.assertEquals("hello", type.decode(toDecode)); + Assertions.assertEquals("hello", type.decode(Bytes.of(toDecode))); } @Test diff --git a/rskj-core/src/test/java/co/rsk/core/TransactionTest.java b/rskj-core/src/test/java/co/rsk/core/TransactionTest.java index 9d357c6fe38..a5a208c8854 100644 --- a/rskj-core/src/test/java/co/rsk/core/TransactionTest.java +++ b/rskj-core/src/test/java/co/rsk/core/TransactionTest.java @@ -19,6 +19,7 @@ package co.rsk.core; import co.rsk.config.TestSystemProperties; +import co.rsk.core.types.bytes.Bytes; import co.rsk.peg.BridgeSupportFactory; import co.rsk.peg.RepositoryBtcBlockStoreWithCache; import co.rsk.peg.constants.BridgeMainNetConstants; @@ -282,7 +283,7 @@ protected ProgramResult executeTransaction() { track.rollback(); - System.out.println("Return value: " + new CallTransaction.IntType("uint").decode(executor.getResult().getHReturn())); + System.out.println("Return value: " + new CallTransaction.IntType("uint").decode(Bytes.of(executor.getResult().getHReturn()))); } // now executing the JSON test transaction diff --git a/rskj-core/src/test/java/org/ethereum/core/TransactionTest.java b/rskj-core/src/test/java/org/ethereum/core/TransactionTest.java index 7374a146754..05c4719b63c 100644 --- a/rskj-core/src/test/java/org/ethereum/core/TransactionTest.java +++ b/rskj-core/src/test/java/org/ethereum/core/TransactionTest.java @@ -23,6 +23,7 @@ import co.rsk.core.RskAddress; import co.rsk.core.TransactionExecutorFactory; import co.rsk.core.genesis.TestGenesisLoader; +import co.rsk.core.types.bytes.Bytes; import co.rsk.crypto.Keccak256; import co.rsk.db.HashMapBlocksIndex; import co.rsk.db.MutableTrieImpl; @@ -504,7 +505,7 @@ protected ProgramResult executeTransaction() { track.rollback(); - System.out.println("Return value: " + new CallTransaction.IntType("uint").decode(executor.getResult().getHReturn())); + System.out.println("Return value: " + new CallTransaction.IntType("uint").decode(Bytes.of(executor.getResult().getHReturn()))); } // now executing the JSON test transaction diff --git a/rskj-core/src/test/java/org/ethereum/solidity/SolidityTypeTest.java b/rskj-core/src/test/java/org/ethereum/solidity/SolidityTypeTest.java index 23258e97cfd..36659028521 100644 --- a/rskj-core/src/test/java/org/ethereum/solidity/SolidityTypeTest.java +++ b/rskj-core/src/test/java/org/ethereum/solidity/SolidityTypeTest.java @@ -1,5 +1,6 @@ package org.ethereum.solidity; +import co.rsk.core.types.bytes.Bytes; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -16,7 +17,7 @@ void TestDynamicArrayTypeWithInvalidDataSize() { input[31] = 0x10; // Indicating we should have 16 elements in the array should fail try { - dat.decode(input, 0); + dat.decode(Bytes.of(input), 0); Assertions.fail(); } catch (IllegalArgumentException e) { // Only acceptable exception @@ -35,7 +36,7 @@ void TestDynamicArrayTypeWithInvalidDataSize() { input[97] = 0x69; try { - dat.decode(input, 0); + dat.decode(Bytes.of(input), 0); Assertions.fail(); } catch (IllegalArgumentException e) { // Only acceptable exception @@ -60,7 +61,7 @@ void TestDynamicArrayTypeWithInvalidDataSize() { input[163] = 0x69; try { - dat.decode(input, 0); + dat.decode(Bytes.of(input), 0); Assertions.fail(); } catch (IllegalArgumentException e) { // Only acceptable exception @@ -96,7 +97,7 @@ void TestDynamicArrayTypeWithValidDataSize() { input[229] = 0x68; input[230] = 0x75; - Object[] ret = (Object[])dat.decode(input, 0); + Object[] ret = (Object[])dat.decode(Bytes.of(input), 0); Assertions.assertEquals(3, ret.length); Assertions.assertTrue(ret[0].toString().contains("hi")); Assertions.assertTrue(ret[1].toString().contains("ih")); @@ -114,7 +115,7 @@ void TestStaticArrayTypeWithInvalidSize() { // the actual data input[32] = 0x68; input[33] = 0x69; - dat.decode(input, 0); + dat.decode(Bytes.of(input), 0); Assertions.fail("should have failed"); } catch (IllegalArgumentException e) { @@ -130,7 +131,7 @@ void TestStaticArrayTypeWithInvalidSize() { // the actual data input[32] = 0x68; input[33] = 0x69; - dat.decode(input, 0); + dat.decode(Bytes.of(input), 0); Assertions.fail("should have failed"); } catch (IllegalArgumentException e) { @@ -149,7 +150,7 @@ void TestStaticArrayType() { input[32] = 0x68; input[33] = 0x69; - Object[] ret = dat.decode(input, 0); + Object[] ret = dat.decode(Bytes.of(input), 0); Assertions.assertEquals(1, ret.length); Assertions.assertTrue(ret[0].toString().contains("hi")); } @@ -159,7 +160,7 @@ void TestIntType() { // Should fail, the array is smaller than the offset we define try { byte[] input = new byte[] {0x4f, 0x4f}; - SolidityType.IntType.decodeInt(input, 12); + SolidityType.IntType.decodeInt(Bytes.of(input), 12); Assertions.fail("should have failed to deserialize the array"); } catch (IllegalArgumentException e) { // Only acceptable exception @@ -168,11 +169,11 @@ void TestIntType() { // Should get a valid number input[31] = 0x01; - BigInteger value = SolidityType.IntType.decodeInt(input, 0); + BigInteger value = SolidityType.IntType.decodeInt(Bytes.of(input), 0); Assertions.assertEquals(1, value.intValue()); // Should get a valid number - value = SolidityType.IntType.decodeInt(input, 32); + value = SolidityType.IntType.decodeInt(Bytes.of(input), 32); Assertions.assertEquals(0, value.intValue()); } diff --git a/rskj-core/src/test/java/org/ethereum/util/UtilsTest.java b/rskj-core/src/test/java/org/ethereum/util/UtilsTest.java index a42711cb4ec..bb301d035e1 100644 --- a/rskj-core/src/test/java/org/ethereum/util/UtilsTest.java +++ b/rskj-core/src/test/java/org/ethereum/util/UtilsTest.java @@ -19,12 +19,11 @@ package org.ethereum.util; -import org.apache.commons.lang3.tuple.Pair; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - +import co.rsk.core.types.bytes.Bytes; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.math.BigInteger; @@ -112,7 +111,7 @@ void testAddressStringToBytes() { @Test void TestValidateArrayWithOffset() { - byte[] data = new byte[10]; + Bytes data = Bytes.of(new byte[10]); // Valid indices Utils.validateArrayAllegedSize(data, 1, 0); Utils.validateArrayAllegedSize(data, 8, 1); @@ -137,13 +136,13 @@ void TestValidateArrayWithOffset() { // Only type of exception expected } try { - Utils.validateArrayAllegedSize(new byte[0], 1, 0); + Utils.validateArrayAllegedSize(Bytes.of(new byte[0]), 1, 0); fail("should have failed"); } catch (IllegalArgumentException e) { // Only type of exception expected } - byte[] noData = null; + Bytes noData = null; try { Utils.validateArrayAllegedSize(noData, 1, 1); fail("should have failed"); @@ -156,33 +155,33 @@ void TestValidateArrayWithOffset() { @Test void TestSafeCopyOfRangeWithValidArrays() { - Assertions.assertDoesNotThrow(() -> Utils.safeCopyOfRange(new byte[2], 0, 1)); - Assertions.assertDoesNotThrow(() -> Utils.safeCopyOfRange(new byte[100], 97, 3)); - Assertions.assertDoesNotThrow(() -> Utils.safeCopyOfRange(new byte[0], 0, 0)); + Assertions.assertDoesNotThrow(() -> Utils.safeCopyOfRange(Bytes.of(new byte[2]), 0, 1)); + Assertions.assertDoesNotThrow(() -> Utils.safeCopyOfRange(Bytes.of(new byte[100]), 97, 3)); + Assertions.assertDoesNotThrow(() -> Utils.safeCopyOfRange(Bytes.of(new byte[0]), 0, 0)); } @Test void TestSafeCopyOfRangeWithInvalidArrays() { try { - Utils.safeCopyOfRange(new byte[2], 1, 2); + Utils.safeCopyOfRange(Bytes.of(new byte[2]), 1, 2); fail("should have failed"); } catch (IllegalArgumentException e){ } try { - Utils.safeCopyOfRange(new byte[100], 98, 3); + Utils.safeCopyOfRange(Bytes.of(new byte[100]), 98, 3); fail("should have failed"); } catch (IllegalArgumentException e){ } try { - Utils.safeCopyOfRange(new byte[0], 0, 1); + Utils.safeCopyOfRange(Bytes.of(new byte[0]), 0, 1); fail("should have failed"); } catch (IllegalArgumentException e){ } try { - Utils.safeCopyOfRange(new byte[0], 1, 0); + Utils.safeCopyOfRange(Bytes.of(new byte[0]), 1, 0); fail("should have failed"); } catch (IllegalArgumentException e){ From b97230f7343bf94a9b6a27ae9befe63d907d81df Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 26 Jul 2024 18:03:17 +0300 Subject: [PATCH 78/89] perf: improve decoding in RLP --- .../src/main/java/org/ethereum/util/RLP.java | 176 ++++-------------- .../main/java/org/ethereum/util/RLPItem.java | 21 ++- .../main/java/org/ethereum/util/RLPList.java | 9 +- .../test/java/org/ethereum/util/RLPTest.java | 99 ++++++++++ 4 files changed, 154 insertions(+), 151 deletions(-) diff --git a/rskj-core/src/main/java/org/ethereum/util/RLP.java b/rskj-core/src/main/java/org/ethereum/util/RLP.java index 5caf38f071f..4a0e1728baa 100644 --- a/rskj-core/src/main/java/org/ethereum/util/RLP.java +++ b/rskj-core/src/main/java/org/ethereum/util/RLP.java @@ -22,7 +22,10 @@ import co.rsk.core.BlockDifficulty; import co.rsk.core.Coin; import co.rsk.core.RskAddress; +import co.rsk.core.types.bytes.Bytes; +import co.rsk.core.types.bytes.BytesSlice; import co.rsk.util.RLPException; +import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.tuple.Pair; import org.bouncycastle.util.BigIntegers; import org.ethereum.db.ByteArrayWrapper; @@ -100,7 +103,8 @@ public class RLP { * byte with value 0x80 plus the length of the string followed by the * string. The range of the first byte is thus [0x80, 0xb7]. */ - private static final int OFFSET_SHORT_ITEM = 0x80; + @VisibleForTesting + static final int OFFSET_SHORT_ITEM = 0x80; /** * [0xb7] @@ -111,7 +115,8 @@ public class RLP { * \xb9\x04\x00 followed by the string. The range of the first byte is thus * [0xb8, 0xbf]. */ - private static final int OFFSET_LONG_ITEM = 0xb7; + @VisibleForTesting + static final int OFFSET_LONG_ITEM = 0xb7; /** * [0xc0] @@ -121,7 +126,8 @@ public class RLP { * of the RLP encodings of the items. The range of the first byte is thus * [0xc0, 0xf7]. */ - private static final int OFFSET_SHORT_LIST = 0xc0; + @VisibleForTesting + static final int OFFSET_SHORT_LIST = 0xc0; /** * [0xf7] @@ -131,29 +137,14 @@ public class RLP { * followed by the concatenation of the RLP encodings of the items. The * range of the first byte is thus [0xf8, 0xff]. */ - private static final int OFFSET_LONG_LIST = 0xf7; + @VisibleForTesting + static final int OFFSET_LONG_LIST = 0xf7; /* ****************************************************** * DECODING * * ******************************************************/ - private static byte decodeOneByteItem(byte[] data, int index) { - // null item - if ((data[index] & 0xFF) == OFFSET_SHORT_ITEM) { - return (byte) (data[index] - OFFSET_SHORT_ITEM); - } - // single byte item - if ((data[index] & 0xFF) < OFFSET_SHORT_ITEM) { - return data[index]; - } - // single byte item - if ((data[index] & 0xFF) == OFFSET_SHORT_ITEM + 1) { - return data[index + 1]; - } - return 0; - } - public static int decodeInt(byte[] data, int index) { // NOTE: there are two ways zero can be encoded - 0x00 and OFFSET_SHORT_ITEM @@ -239,108 +230,7 @@ public static int getNextElementIndex(byte[] payload, int pos) { return -1; } - /** - * Get exactly one message payload - */ - public static void fullTraverse(byte[] msgData, int level, int startPos, - int endPos, int levelToIndex, Queue index) { - - try { - - if (msgData == null || msgData.length == 0) { - return; - } - int pos = startPos; - - while (pos < endPos) { - - if (level == levelToIndex) { - index.add(pos); - } - - // It's a list with a payload more than 55 bytes - // data[0] - 0xF7 = how many next bytes allocated - // for the length of the list - if ((msgData[pos] & 0xFF) >= OFFSET_LONG_LIST) { - - byte lengthOfLength = (byte) (msgData[pos] - OFFSET_LONG_LIST); - int length = calcLength(lengthOfLength, msgData, pos); - - // now we can parse an item for data[1]..data[length] - System.out.println("-- level: [" + level - + "] Found big list length: " + length); - - fullTraverse(msgData, level + 1, pos + lengthOfLength + 1, - pos + lengthOfLength + length, levelToIndex, index); - - pos += lengthOfLength + length + 1; - continue; - } - // It's a list with a payload less than 55 bytes - if ((msgData[pos] & 0xFF) >= OFFSET_SHORT_LIST - && (msgData[pos] & 0xFF) < OFFSET_LONG_LIST) { - - byte length = (byte) ((msgData[pos] & 0xFF) - OFFSET_SHORT_LIST); - - System.out.println("-- level: [" + level - + "] Found small list length: " + length); - - fullTraverse(msgData, level + 1, pos + 1, pos + length + 1, - levelToIndex, index); - - pos += 1 + length; - continue; - } - // It's an item with a payload more than 55 bytes - // data[0] - 0xB7 = how much next bytes allocated for - // the length of the string - if ((msgData[pos] & 0xFF) >= OFFSET_LONG_ITEM - && (msgData[pos] & 0xFF) < OFFSET_SHORT_LIST) { - - byte lengthOfLength = (byte) (msgData[pos] - OFFSET_LONG_ITEM); - int length = calcLength(lengthOfLength, msgData, pos); - - // now we can parse an item for data[1]..data[length] - System.out.println("-- level: [" + level - + "] Found big item length: " + length); - pos += lengthOfLength + length + 1; - - continue; - } - // It's an item less than 55 bytes long, - // data[0] - 0x80 == length of the item - if ((msgData[pos] & 0xFF) > OFFSET_SHORT_ITEM - && (msgData[pos] & 0xFF) < OFFSET_LONG_ITEM) { - - byte length = (byte) ((msgData[pos] & 0xFF) - OFFSET_SHORT_ITEM); - - System.out.println("-- level: [" + level - + "] Found small item length: " + length); - pos += 1 + length; - continue; - } - // null item - if ((msgData[pos] & 0xFF) == OFFSET_SHORT_ITEM) { - System.out.println("-- level: [" + level - + "] Found null item: "); - pos += 1; - continue; - } - // single byte item - if ((msgData[pos] & 0xFF) < OFFSET_SHORT_ITEM) { - System.out.println("-- level: [" + level - + "] Found single item: "); - pos += 1; - continue; - } - } - } catch (Throwable th) { - throw new RuntimeException("RLP wrong encoding", - th.fillInStackTrace()); - } - } - - private static int calcLength(int lengthOfLength, byte[] msgData, int pos) { + static int calcLength(int lengthOfLength, byte[] msgData, int pos) { byte pow = (byte) (lengthOfLength - 1); int length = 0; for (int i = 1; i <= lengthOfLength; ++i) { @@ -350,6 +240,11 @@ private static int calcLength(int lengthOfLength, byte[] msgData, int pos) { return length; } + @Nonnull + public static ArrayList decode2(@CheckForNull byte[] msgData) { + return decode2(Bytes.of(msgData)); + } + /** * Parse wire byte[] message into RLP elements * @@ -358,14 +253,14 @@ private static int calcLength(int lengthOfLength, byte[] msgData, int pos) { * - outcome of recursive RLP structure */ @Nonnull - public static ArrayList decode2(@CheckForNull byte[] msgData) { + public static ArrayList decode2(@CheckForNull BytesSlice msgData) { ArrayList elements = new ArrayList<>(); if (msgData == null) { return elements; } - int tlength = msgData.length; + int tlength = msgData.length(); int position = 0; while (position < tlength) { @@ -382,11 +277,11 @@ public static RLPElement decodeFirstElement(@CheckForNull byte[] msgData, int po return null; } - return decodeElement(msgData, position).getKey(); + return decodeElement(Bytes.of(msgData), position).getKey(); } - private static Pair decodeElement(byte[] msgData, int position) { // NOSONAR - int b0 = msgData[position] & 0xff; + private static Pair decodeElement(BytesSlice msgData, int position) { + int b0 = msgData.byteAt(position) & 0xff; if (b0 >= 192) { int length; @@ -403,24 +298,23 @@ private static Pair decodeElement(byte[] msgData, int posit int endingIndex = safeAdd(length, position); - if (endingIndex > msgData.length) { + if (endingIndex > msgData.length()) { throw new RLPException("The RLP byte array doesn't have enough space to hold an element with the specified length"); } - byte[] bytes = Arrays.copyOfRange(msgData, position, endingIndex); - RLPList list = new RLPList(bytes, offset); + RLPList list = new RLPList(msgData.slice(position, endingIndex), offset); return Pair.of(list, endingIndex); } if (b0 == EMPTY_MARK) { - return Pair.of(new RLPItem(ByteUtil.EMPTY_BYTE_ARRAY), position + 1); + return Pair.of(new RLPItem(Bytes.of(ByteUtil.EMPTY_BYTE_ARRAY)), position + 1); } if (b0 < EMPTY_MARK) { byte[] data = new byte[1]; - data[0] = msgData[position]; - return Pair.of(new RLPItem(data), position + 1); + data[0] = msgData.byteAt(position); + return Pair.of(new RLPItem(Bytes.of(data)), position + 1); } int length; @@ -439,15 +333,13 @@ private static Pair decodeElement(byte[] msgData, int posit } int endingIndex = position + offset + length; - if ( endingIndex < 0 || endingIndex > msgData.length) { + if (endingIndex < 0 || endingIndex > msgData.length()) { throw new RLPException("The RLP byte array doesn't have enough space to hold an element with the specified length"); } - byte[] decoded = new byte[length]; - - System.arraycopy(msgData, position + offset, decoded, 0, length); - - return Pair.of(new RLPItem(decoded), position + offset + length); + int from = position + offset; + int to = from + length; + return Pair.of(new RLPItem(msgData.slice(from, to)), to); } private static int safeAdd(int a, int b) { @@ -458,8 +350,8 @@ private static int safeAdd(int a, int b) { } } - private static int bytesToLength(byte[] bytes, int position, int size) { - if (position + size > bytes.length) { + private static int bytesToLength(BytesSlice bytes, int position, int size) { + if (position + size > bytes.length()) { throw new RLPException("The length of the RLP item length can't possibly fit the data byte array"); } @@ -467,7 +359,7 @@ private static int bytesToLength(byte[] bytes, int position, int size) { for (int k = 0; k < size; k++) { length <<= 8; - length += bytes[position + k] & 0xff; + length += bytes.byteAt(position + k) & 0xff; } if (length < 0) { diff --git a/rskj-core/src/main/java/org/ethereum/util/RLPItem.java b/rskj-core/src/main/java/org/ethereum/util/RLPItem.java index 2ca8f7f81e3..49ce56965f1 100644 --- a/rskj-core/src/main/java/org/ethereum/util/RLPItem.java +++ b/rskj-core/src/main/java/org/ethereum/util/RLPItem.java @@ -19,29 +19,40 @@ package org.ethereum.util; +import co.rsk.core.types.bytes.BytesSlice; + /** * @author Roman Mandeleil * @since 21.04.14 */ public class RLPItem implements RLPElement { - private final byte[] rlpData; + private final BytesSlice rlpData; - public RLPItem(byte[] rlpData) { + public RLPItem(BytesSlice rlpData) { this.rlpData = rlpData; } + protected BytesSlice getRLPBytes() { + if (rlpData.length() == 0) { + return null; + } + + return rlpData; + } + @Override public byte[] getRLPData() { - if (rlpData.length == 0) { + BytesSlice rlpBytes = getRLPBytes(); + if (rlpBytes == null) { return null; } - return rlpData; + return rlpBytes.copyArray(); } @Override public byte[] getRLPRawData() { - return rlpData; + return rlpData.copyArray(); } } diff --git a/rskj-core/src/main/java/org/ethereum/util/RLPList.java b/rskj-core/src/main/java/org/ethereum/util/RLPList.java index c4a5f035f53..e9ec39a2dc0 100644 --- a/rskj-core/src/main/java/org/ethereum/util/RLPList.java +++ b/rskj-core/src/main/java/org/ethereum/util/RLPList.java @@ -19,7 +19,8 @@ package org.ethereum.util; -import java.util.Arrays; +import co.rsk.core.types.bytes.BytesSlice; + import java.util.List; /** @@ -30,7 +31,7 @@ public class RLPList extends RLPItem implements RLPElement { private List elements; private final int offset; - public RLPList(byte[] rlpData, int offset) { + public RLPList(BytesSlice rlpData, int offset) { super(rlpData); this.offset = offset; } @@ -52,8 +53,8 @@ private void checkElements() { return; } - byte[] bytes = this.getRLPData(); - byte[] content = Arrays.copyOfRange(bytes, offset, bytes.length); + BytesSlice bytes = getRLPBytes(); + BytesSlice content = bytes.slice(offset, bytes.length()); this.elements = RLP.decode2(content); } diff --git a/rskj-core/src/test/java/org/ethereum/util/RLPTest.java b/rskj-core/src/test/java/org/ethereum/util/RLPTest.java index 58bbaa5218f..6c5b9d3a2df 100644 --- a/rskj-core/src/test/java/org/ethereum/util/RLPTest.java +++ b/rskj-core/src/test/java/org/ethereum/util/RLPTest.java @@ -1071,4 +1071,103 @@ void testIncorrectDecodeInt(){ byte[] payload = {(byte) 0x84, (byte) 0x00, (byte) 0x00, (byte) 0x84, (byte) 0x00, (byte) 0x0f, (byte) 0xab}; assertThrows(RLPException.class, () -> RLP.decodeInt(payload, 3)); } + + /** + * Get exactly one message payload + */ + private static void fullTraverse(byte[] msgData, int level, int startPos, int endPos, int levelToIndex, Queue index) { + try { + + if (msgData == null || msgData.length == 0) { + return; + } + int pos = startPos; + + while (pos < endPos) { + + if (level == levelToIndex) { + index.add(pos); + } + + // It's a list with a payload more than 55 bytes + // data[0] - 0xF7 = how many next bytes allocated + // for the length of the list + if ((msgData[pos] & 0xFF) >= OFFSET_LONG_LIST) { + + byte lengthOfLength = (byte) (msgData[pos] - OFFSET_LONG_LIST); + int length = calcLength(lengthOfLength, msgData, pos); + + // now we can parse an item for data[1]..data[length] + System.out.println("-- level: [" + level + + "] Found big list length: " + length); + + fullTraverse(msgData, level + 1, pos + lengthOfLength + 1, + pos + lengthOfLength + length, levelToIndex, index); + + pos += lengthOfLength + length + 1; + continue; + } + // It's a list with a payload less than 55 bytes + if ((msgData[pos] & 0xFF) >= OFFSET_SHORT_LIST + && (msgData[pos] & 0xFF) < OFFSET_LONG_LIST) { + + byte length = (byte) ((msgData[pos] & 0xFF) - OFFSET_SHORT_LIST); + + System.out.println("-- level: [" + level + + "] Found small list length: " + length); + + fullTraverse(msgData, level + 1, pos + 1, pos + length + 1, + levelToIndex, index); + + pos += 1 + length; + continue; + } + // It's an item with a payload more than 55 bytes + // data[0] - 0xB7 = how much next bytes allocated for + // the length of the string + if ((msgData[pos] & 0xFF) >= OFFSET_LONG_ITEM + && (msgData[pos] & 0xFF) < OFFSET_SHORT_LIST) { + + byte lengthOfLength = (byte) (msgData[pos] - OFFSET_LONG_ITEM); + int length = calcLength(lengthOfLength, msgData, pos); + + // now we can parse an item for data[1]..data[length] + System.out.println("-- level: [" + level + + "] Found big item length: " + length); + pos += lengthOfLength + length + 1; + + continue; + } + // It's an item less than 55 bytes long, + // data[0] - 0x80 == length of the item + if ((msgData[pos] & 0xFF) > OFFSET_SHORT_ITEM + && (msgData[pos] & 0xFF) < OFFSET_LONG_ITEM) { + + byte length = (byte) ((msgData[pos] & 0xFF) - OFFSET_SHORT_ITEM); + + System.out.println("-- level: [" + level + + "] Found small item length: " + length); + pos += 1 + length; + continue; + } + // null item + if ((msgData[pos] & 0xFF) == OFFSET_SHORT_ITEM) { + System.out.println("-- level: [" + level + + "] Found null item: "); + pos += 1; + continue; + } + // single byte item + if ((msgData[pos] & 0xFF) < OFFSET_SHORT_ITEM) { + System.out.println("-- level: [" + level + + "] Found single item: "); + pos += 1; + continue; + } + } + } catch (Throwable th) { + throw new RuntimeException("RLP wrong encoding", + th.fillInStackTrace()); + } + } } From b8fdf84c29ff096c887f48a15c777fa889969853 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 9 Aug 2024 15:32:52 +0300 Subject: [PATCH 79/89] perf: refactor some tests --- .../rsk/core/types/bytes/BytesSliceTest.java | 42 ++++++++++++++++--- .../co/rsk/core/types/bytes/BytesTest.java | 19 +++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java index 001aee51f35..5abc98a8838 100644 --- a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java +++ b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java @@ -29,7 +29,7 @@ class BytesSliceTest { @Test - void testBytesLength() { + void testBytesLength() { assertEquals(0, Bytes.of(new byte[]{}).slice(0, 0).length()); assertEquals(0, Bytes.of(new byte[]{1}).slice(0, 0).length()); assertEquals(1, Bytes.of(new byte[]{1}).slice(0, 1).length()); @@ -41,16 +41,20 @@ void testBytesLength() { @Test void testBytesAt() { - assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{}).slice(0, 0).byteAt(0)); - assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).slice(0, 1).byteAt(1)); - assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).slice(0, 1).byteAt(-1)); - assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1,2,3}).slice(1, 2).byteAt(1)); assertEquals(1, Bytes.of(new byte[]{1}).slice(0, 1).byteAt(0)); assertEquals(2, Bytes.of(new byte[]{1,2}).slice(0, 2).byteAt(1)); assertEquals(2, Bytes.of(new byte[]{1,2,3}).slice(1, 2).byteAt(0)); assertEquals(4, Bytes.of(new byte[]{1,2,3,4}).slice(2, 4).byteAt(1)); } + @Test + void testBytesAtIndexOutOfBoundsException() { + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{}).slice(0, 0).byteAt(0)); + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).slice(0, 1).byteAt(1)); + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1}).slice(0, 1).byteAt(-1)); + assertThrows(IndexOutOfBoundsException.class, () -> Bytes.of(new byte[]{1,2,3}).slice(1, 2).byteAt(1)); + } + @Test void testBytesSliceArraycopy() { checkArraycopy((src, srcPos, dest, destPos, length) -> Bytes.of((byte[]) src).slice(1, 4).arraycopy(srcPos, (byte[]) dest, destPos, length)); @@ -115,6 +119,14 @@ void testEmptySlice() { } private static void checkArraycopy(Functions.Action5 fun) { + /* + 'fun' signature: + @src – the source array. + @srcPos – starting position in the source array. + @dest – the destination array. + @destPos – starting position in the destination data. + @length – the number of array elements to be copied. + */ byte[] dest = new byte[3]; byte[] origin = new byte[]{1,2,3,4,5}; @@ -139,6 +151,26 @@ private static void checkArraycopy(Functions.Action5 void checkCopyOfRange(Functions.Function3 fun, Functions.Function3 slicer) { + /* + 'fun' signature: + @original – the array from which a range is to be copied + @from – the initial index of the range to be copied, inclusive + @to – the final index of the range to be copied, exclusive. (This index may lie outside the array.) + + @return a new array containing the specified range from the original array, truncated or padded with zeros + to obtain the required length + */ + + /* + 'slicer' signature: + @original – the array from which a range is to be copied + @from – the initial index of the range to be copied, inclusive + @to – the final index of the range to be copied, exclusive. (This index may lie outside the array.) + + @return a new entity containing the specified range from the original array, truncated or padded with zeros + to obtain the required length + */ + byte[] bArray = new byte[]{1, 2, 3, 4, 5, 6}; assertEquals(bArray.length, fun.apply(slicer.apply(bArray, 0, 6), 0, 6).length); diff --git a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java index 7cce5532f17..dd28d5b2e63 100644 --- a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java +++ b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesTest.java @@ -160,6 +160,15 @@ void testEmptyBytesToString() { } private static void checkArraycopy(Functions.Action5 fun) { + /* + 'fun' signature: + @src – the source array. + @srcPos – starting position in the source array. + @dest – the destination array. + @destPos – starting position in the destination data. + @length – the number of array elements to be copied. + */ + byte[] dest = new byte[5]; byte[] origin = new byte[]{1,2,3,4,5}; @@ -183,6 +192,16 @@ private static void checkArraycopy(Functions.Action5 fun) { + /* + 'fun' signature: + @original – the array from which a range is to be copied + @from – the initial index of the range to be copied, inclusive + @to – the final index of the range to be copied, exclusive. (This index may lie outside the array.) + + @return a new array containing the specified range from the original array, truncated or padded with zeros + to obtain the required length + */ + byte[] bArray = new byte[]{1, 2, 3, 4, 5}; assertEquals(bArray.length, fun.apply(bArray, 0, 5).length); From 39f9c5672f1e084588c058a6bffaf2d2c6d2dbf5 Mon Sep 17 00:00:00 2001 From: fmacleal <157636304+fmacleal@users.noreply.github.com> Date: Wed, 21 Aug 2024 15:25:57 +0200 Subject: [PATCH 80/89] Update rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nazaret García Revetria --- .../src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java index 5abc98a8838..cdea74403bd 100644 --- a/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java +++ b/rskj-core/src/test/java/co/rsk/core/types/bytes/BytesSliceTest.java @@ -29,7 +29,7 @@ class BytesSliceTest { @Test - void testBytesLength() { + void testBytesLength() { assertEquals(0, Bytes.of(new byte[]{}).slice(0, 0).length()); assertEquals(0, Bytes.of(new byte[]{1}).slice(0, 0).length()); assertEquals(1, Bytes.of(new byte[]{1}).slice(0, 1).length()); From 77131aa9d78f1cd401084e0b651d1796d8699b6e Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 8 Oct 2024 09:56:06 -0300 Subject: [PATCH 81/89] Update test in BitcoinUtilsTest to be deterministic --- .../src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java b/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java index 4d908493635..c6cff78fc17 100644 --- a/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java +++ b/rskj-core/src/test/java/co/rsk/peg/bitcoin/BitcoinUtilsTest.java @@ -348,7 +348,7 @@ void removeSignaturesFromTransaction_whenNotAllTransactionInputsHaveP2shMultiSig // arrange Federation federation = P2shErpFederationBuilder.builder().build(); Script p2shMultiSigScriptSig = federation.getP2SHScript().createEmptyInputScript(null, federation.getRedeemScript()); - BtcECKey pubKey = new BtcECKey(); + BtcECKey pubKey = BitcoinTestUtils.getBtcEcKeyFromSeed("abc"); Script p2pkhScriptSig = ScriptBuilder.createInputScript(null, pubKey); BtcTransaction transaction = new BtcTransaction(btcMainnetParams); From b18fd9682ce1cf256ffd45e05edf4e127c2d82b0 Mon Sep 17 00:00:00 2001 From: Antonio Pancorbo <48168255+apancorb@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:13:05 +0000 Subject: [PATCH 82/89] fix(peg): ensure type safety for bridge contract methods --- .../main/java/co/rsk/peg/BridgeMethods.java | 154 +++++++++++------- 1 file changed, 96 insertions(+), 58 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeMethods.java b/rskj-core/src/main/java/co/rsk/peg/BridgeMethods.java index 2fbdb688a55..80458a14139 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeMethods.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeMethods.java @@ -19,6 +19,7 @@ import static org.ethereum.config.blockchain.upgrades.ConsensusRule.*; +import java.math.BigInteger; import java.util.Map; import java.util.Optional; import java.util.function.Function; @@ -31,7 +32,11 @@ import org.ethereum.vm.MessageCall.MsgType; /** - * This enum holds the basic information of the Bridge contract methods: the ABI, the cost and the implementation. + * Represents the methods of the Bridge contract, encapsulating details such as + * the Application Binary Interface (ABI), execution costs, and method implementations. + * + * Each enum constant corresponds to a specific method of the Bridge contract, + * defining its signature and providing the necessary information for execution. */ public enum BridgeMethods { ADD_FEDERATOR_PUBLIC_KEY( @@ -41,7 +46,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(13000L), - (BridgeMethodExecutorTyped) Bridge::addFederatorPublicKey, + (BridgeMethodExecutorTyped) Bridge::addFederatorPublicKey, activations -> !activations.isActive(RSKIP123), fixedPermission(false) ), @@ -52,7 +57,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(13000L), - (BridgeMethodExecutorTyped) Bridge::addFederatorPublicKeyMultikey, + (BridgeMethodExecutorTyped) Bridge::addFederatorPublicKeyMultikey, activations -> activations.isActive(RSKIP123), fixedPermission(false) ), @@ -63,7 +68,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(25000L), - (BridgeMethodExecutorTyped) Bridge::addOneOffLockWhitelistAddress, + (BridgeMethodExecutorTyped) Bridge::addOneOffLockWhitelistAddress, activations -> !activations.isActive(RSKIP87), fixedPermission(false) ), @@ -74,7 +79,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(25000L), // using same gas estimation as ADD_LOCK_WHITELIST_ADDRESS - (BridgeMethodExecutorTyped) Bridge::addOneOffLockWhitelistAddress, + (BridgeMethodExecutorTyped) Bridge::addOneOffLockWhitelistAddress, activations -> activations.isActive(RSKIP87), fixedPermission(false) ), @@ -85,7 +90,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(25000L), // using same gas estimation as ADD_LOCK_WHITELIST_ADDRESS - (BridgeMethodExecutorTyped) Bridge::addUnlimitedLockWhitelistAddress, + (BridgeMethodExecutorTyped) Bridge::addUnlimitedLockWhitelistAddress, activations -> activations.isActive(RSKIP87), fixedPermission(false) ), @@ -106,7 +111,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(38000L), - (BridgeMethodExecutorTyped) Bridge::commitFederation, + (BridgeMethodExecutorTyped) Bridge::commitFederation, fixedPermission(false) ), CREATE_FEDERATION( @@ -116,7 +121,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(11000L), - (BridgeMethodExecutorTyped) Bridge::createFederation, + (BridgeMethodExecutorTyped) Bridge::createFederation, fixedPermission(false) ), GET_BTC_BLOCKCHAIN_BEST_CHAIN_HEIGHT( @@ -126,7 +131,7 @@ public enum BridgeMethods { new String[]{"int"} ), fixedCost(19000L), - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBestChainHeight, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBestChainHeight, fromMethod(Bridge::getBtcBlockchainBestChainHeightOnlyAllowsLocalCalls), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -137,7 +142,7 @@ public enum BridgeMethods { new String[]{"int"} ), fixedCost(20000L), - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainInitialBlockHeight, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainInitialBlockHeight, activations -> activations.isActive(RSKIP89), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -149,7 +154,7 @@ public enum BridgeMethods { new String[]{"string[]"} ), fixedCost(76000L), - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockLocator, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockLocator, activations -> !activations.isActive(RSKIP89), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -161,7 +166,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(20000L), - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockHashAtDepth, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockHashAtDepth, activations -> activations.isActive(RSKIP89), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -173,7 +178,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fromMethod(Bridge::getBtcTransactionConfirmationsGetCost), - (BridgeMethodExecutorTyped) Bridge::getBtcTransactionConfirmations, + (BridgeMethodExecutorTyped) Bridge::getBtcTransactionConfirmations, activations -> activations.isActive(RSKIP122), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -185,7 +190,7 @@ public enum BridgeMethods { new String[]{"int64"} ), fixedCost(22000L), - (BridgeMethodExecutorTyped) Bridge::getBtcTxHashProcessedHeight, + (BridgeMethodExecutorTyped) Bridge::getBtcTxHashProcessedHeight, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -196,7 +201,7 @@ public enum BridgeMethods { new String[]{"string"} ), fixedCost(11000L), - (BridgeMethodExecutorTyped) Bridge::getFederationAddress, + (BridgeMethodExecutorTyped) Bridge::getFederationAddress, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -207,7 +212,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(10000L), - (BridgeMethodExecutorTyped) Bridge::getFederationCreationBlockNumber, + (BridgeMethodExecutorTyped) Bridge::getFederationCreationBlockNumber, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -218,7 +223,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(10000L), - (BridgeMethodExecutorTyped) Bridge::getFederationCreationTime, + (BridgeMethodExecutorTyped) Bridge::getFederationCreationTime, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -229,7 +234,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(10000L), - (BridgeMethodExecutorTyped) Bridge::getFederationSize, + (BridgeMethodExecutorTyped) Bridge::getFederationSize, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -240,7 +245,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(11000L), - (BridgeMethodExecutorTyped) Bridge::getFederationThreshold, + (BridgeMethodExecutorTyped) Bridge::getFederationThreshold, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -251,7 +256,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(10000L), - (BridgeMethodExecutorTyped) Bridge::getFederatorPublicKey, + (BridgeMethodExecutorTyped) Bridge::getFederatorPublicKey, activations -> !activations.isActive(RSKIP123), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -263,7 +268,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(10000L), - (BridgeMethodExecutorTyped) Bridge::getFederatorPublicKeyOfType, + (BridgeMethodExecutorTyped) Bridge::getFederatorPublicKeyOfType, activations -> activations.isActive(RSKIP123), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -275,7 +280,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(2000L), - (BridgeMethodExecutorTyped) Bridge::getFeePerKb, + (BridgeMethodExecutorTyped) Bridge::getFeePerKb, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -286,7 +291,7 @@ public enum BridgeMethods { new String[]{"string"} ), fixedCost(16000L), - (BridgeMethodExecutorTyped) Bridge::getLockWhitelistAddress, + (BridgeMethodExecutorTyped) Bridge::getLockWhitelistAddress, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -297,7 +302,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(16000L), - (BridgeMethodExecutorTyped) Bridge::getLockWhitelistEntryByAddress, + (BridgeMethodExecutorTyped) Bridge::getLockWhitelistEntryByAddress, activations -> activations.isActive(RSKIP87), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -309,7 +314,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(16000L), - (BridgeMethodExecutorTyped) Bridge::getLockWhitelistSize, + (BridgeMethodExecutorTyped) Bridge::getLockWhitelistSize, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -320,7 +325,7 @@ public enum BridgeMethods { new String[]{"int"} ), fixedCost(2000L), - (BridgeMethodExecutorTyped) Bridge::getMinimumLockTxValue, + (BridgeMethodExecutorTyped) Bridge::getMinimumLockTxValue, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -331,7 +336,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getPendingFederationHashSerialized, + (BridgeMethodExecutorTyped) Bridge::getPendingFederationHashSerialized, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -342,7 +347,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getPendingFederationSize, + (BridgeMethodExecutorTyped) Bridge::getPendingFederationSize, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -353,7 +358,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getPendingFederatorPublicKey, + (BridgeMethodExecutorTyped) Bridge::getPendingFederatorPublicKey, activations -> !activations.isActive(RSKIP123), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -365,7 +370,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getPendingFederatorPublicKeyOfType, + (BridgeMethodExecutorTyped) Bridge::getPendingFederatorPublicKeyOfType, activations -> activations.isActive(RSKIP123), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -377,7 +382,7 @@ public enum BridgeMethods { new String[]{"string"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationAddress, + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationAddress, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -388,7 +393,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationCreationBlockNumber, + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationCreationBlockNumber, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -399,7 +404,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationCreationTime, + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationCreationTime, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -410,7 +415,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationSize, + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationSize, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -421,7 +426,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getRetiringFederationThreshold, + (BridgeMethodExecutorTyped) Bridge::getRetiringFederationThreshold, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -432,7 +437,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getRetiringFederatorPublicKey, + (BridgeMethodExecutorTyped) Bridge::getRetiringFederatorPublicKey, activations -> !activations.isActive(RSKIP123), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -444,7 +449,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(3000L), - (BridgeMethodExecutorTyped) Bridge::getRetiringFederatorPublicKeyOfType, + (BridgeMethodExecutorTyped) Bridge::getRetiringFederatorPublicKeyOfType, activations -> activations.isActive(RSKIP123), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -456,7 +461,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(4000L), - (BridgeMethodExecutorTyped) Bridge::getStateForBtcReleaseClient, + (BridgeMethodExecutorTyped) Bridge::getStateForBtcReleaseClient, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -467,7 +472,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(3_000_000L), - (BridgeMethodExecutorTyped) Bridge::getStateForDebugging, + (BridgeMethodExecutorTyped) Bridge::getStateForDebugging, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -478,7 +483,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(3_000L), - (BridgeMethodExecutorTyped) Bridge::getLockingCap, + (BridgeMethodExecutorTyped) Bridge::getLockingCap, activations -> activations.isActive(RSKIP134), fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL @@ -490,7 +495,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(30_000L), - (BridgeMethodExecutorTyped) Bridge::getActivePowpegRedeemScript, + (BridgeMethodExecutorTyped) Bridge::getActivePowpegRedeemScript, activations -> activations.isActive(RSKIP293), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -502,7 +507,7 @@ public enum BridgeMethods { new String[]{"uint256"} ), fixedCost(3_000L), - (BridgeMethodExecutorTyped) Bridge::getActiveFederationCreationBlockHeight, + (BridgeMethodExecutorTyped) Bridge::getActiveFederationCreationBlockHeight, activations -> activations.isActive(RSKIP186), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -514,7 +519,7 @@ public enum BridgeMethods { new String[]{"bool"} ), fixedCost(8_000L), - (BridgeMethodExecutorTyped) Bridge::increaseLockingCap, + (BridgeMethodExecutorTyped) Bridge::increaseLockingCap, activations -> activations.isActive(RSKIP134), fixedPermission(false) ), @@ -525,7 +530,7 @@ public enum BridgeMethods { new String[]{"bool"} ), fixedCost(23000L), - (BridgeMethodExecutorTyped) Bridge::isBtcTxHashAlreadyProcessed, + (BridgeMethodExecutorTyped) Bridge::isBtcTxHashAlreadyProcessed, fixedPermission(true), CallTypeHelper.ALLOW_STATIC_CALL ), @@ -550,7 +555,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(10_600L), - (BridgeMethodExecutorTyped) Bridge::receiveHeader, + (BridgeMethodExecutorTyped) Bridge::receiveHeader, activations -> activations.isActive(RSKIP200), fixedPermission(false) ), @@ -585,7 +590,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(24000L), - (BridgeMethodExecutorTyped) Bridge::removeLockWhitelistAddress, + (BridgeMethodExecutorTyped) Bridge::removeLockWhitelistAddress, fixedPermission(false) ), ROLLBACK_FEDERATION( @@ -595,7 +600,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(12000L), - (BridgeMethodExecutorTyped) Bridge::rollbackFederation, + (BridgeMethodExecutorTyped) Bridge::rollbackFederation, fixedPermission(false) ), SET_LOCK_WHITELIST_DISABLE_BLOCK_DELAY( @@ -605,7 +610,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(24000L), - (BridgeMethodExecutorTyped) Bridge::setLockWhitelistDisableBlockDelay, + (BridgeMethodExecutorTyped) Bridge::setLockWhitelistDisableBlockDelay, fixedPermission(false) ), UPDATE_COLLECTIONS( @@ -625,7 +630,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(10000L), - (BridgeMethodExecutorTyped) Bridge::voteFeePerKbChange, + (BridgeMethodExecutorTyped) Bridge::voteFeePerKbChange, fixedPermission(false) ), REGISTER_BTC_COINBASE_TRANSACTION( @@ -646,7 +651,7 @@ public enum BridgeMethods { new String[]{"bool"} ), fixedCost(5000L), - (BridgeMethodExecutorTyped) Bridge::hasBtcBlockCoinbaseTransactionInformation, + (BridgeMethodExecutorTyped) Bridge::hasBtcBlockCoinbaseTransactionInformation, activations -> activations.isActive(RSKIP143), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -658,7 +663,7 @@ public enum BridgeMethods { new String[]{"int256"} ), fixedCost(25_000L), - (BridgeMethodExecutorTyped) Bridge::registerFlyoverBtcTransaction, + (BridgeMethodExecutorTyped) Bridge::registerFlyoverBtcTransaction, activations -> activations.isActive(RSKIP176), fixedPermission(false) ), @@ -669,7 +674,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(3_800L), - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBestBlockHeader, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBestBlockHeader, activations -> activations.isActive(RSKIP220), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -681,7 +686,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(4_600L), - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockHeaderByHash, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockHeaderByHash, activations -> activations.isActive(RSKIP220), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -693,7 +698,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(5_000L), - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockHeaderByHeight, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainBlockHeaderByHeight, activations -> activations.isActive(RSKIP220), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -705,7 +710,7 @@ public enum BridgeMethods { new String[]{"bytes"} ), fixedCost(4_900L), - (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainParentBlockHeaderByHash, + (BridgeMethodExecutorTyped) Bridge::getBtcBlockchainParentBlockHeaderByHash, activations -> activations.isActive(RSKIP220), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -717,7 +722,7 @@ public enum BridgeMethods { new String[]{"uint256"} ), fixedCost(3_000L), - (BridgeMethodExecutorTyped) Bridge::getNextPegoutCreationBlockNumber, + (BridgeMethodExecutorTyped) Bridge::getNextPegoutCreationBlockNumber, activations -> activations.isActive(RSKIP271), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -729,7 +734,7 @@ public enum BridgeMethods { new String[]{"uint256"} ), fixedCost(3_000L), - (BridgeMethodExecutorTyped) Bridge::getQueuedPegoutsCount, + (BridgeMethodExecutorTyped) Bridge::getQueuedPegoutsCount, activations -> activations.isActive(RSKIP271), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -741,7 +746,7 @@ public enum BridgeMethods { new String[]{"uint256"} ), fixedCost(10_000L), - (BridgeMethodExecutorTyped) Bridge::getEstimatedFeesForNextPegOutEvent, + (BridgeMethodExecutorTyped) Bridge::getEstimatedFeesForNextPegOutEvent, activations -> activations.isActive(RSKIP271), fixedPermission(false), CallTypeHelper.ALLOW_STATIC_CALL @@ -858,10 +863,33 @@ public interface BridgeCondition { boolean isTrue(Bridge bridge); } + /** + * Interface for executing methods in the Bridge context. + * + *

+ * This interface defines a single method, {@code execute}, which takes a + * {@link Bridge} instance and an array of arguments, returning an + * {@code Optional} result. Implementations of this interface should handle + * the execution logic and manage potential exceptions. + *

+ */ public interface BridgeMethodExecutor { Optional execute(Bridge self, Object[] args) throws Exception; } + /** + * A typed variant of {@link BridgeMethodExecutor} that allows for specific + * return types. + * + *

+ * This interface extends {@code BridgeMethodExecutor} and provides a default + * implementation of the {@code execute} method, delegating the call to a typed + * execution method {@code executeTyped}. Implementations must define this + * method to specify the expected return type. + *

+ * + * @param the return type of the executed method + */ private interface BridgeMethodExecutorTyped extends BridgeMethodExecutor { @Override default Optional execute(Bridge self, Object[] args) throws Exception { @@ -871,6 +899,16 @@ default Optional execute(Bridge self, Object[] args) throws Exception { T executeTyped(Bridge self, Object[] args) throws Exception; } + /** + * A variant of {@link BridgeMethodExecutor} for void methods. + * + *

+ * This interface overrides the {@code execute} method to perform an action + * without returning a result. Implementations should define the + * {@code executeVoid} method, which executes the intended action using the + * provided {@link Bridge} instance and arguments. + *

+ */ private interface BridgeMethodExecutorVoid extends BridgeMethodExecutor { @Override default Optional execute(Bridge self, Object[] args) throws Exception { From fded77a19ef191bd267cdb6db5a1687c025fcf59 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Tue, 17 Sep 2024 17:38:06 +0300 Subject: [PATCH 83/89] refactor: enhance input params parsing --- .../co/rsk/rpc/Web3InformationRetriever.java | 21 ++++++++++++++----- .../main/java/co/rsk/util/StringUtils.java | 2 +- .../ethereum/rpc/parameters/HashParam32.java | 13 +++++++++--- .../rpc/parameters/HexAddressParam.java | 12 +++++++++-- .../rpc/parameters/HexNumberParam.java | 12 +++++++++++ .../rpc/parameters/HexStringParam.java | 9 +++++++- ...actPublicKeyFromExtendedPublicKeyTest.java | 2 +- .../rsk/pcc/bto/HDWalletUtilsHelperTest.java | 4 ++-- .../java/co/rsk/util/StringUtilsTest.java | 12 +++++------ 9 files changed, 66 insertions(+), 21 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java b/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java index 40af9360099..141dd68f0bc 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java +++ b/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java @@ -21,19 +21,22 @@ import co.rsk.core.bc.AccountInformationProvider; import co.rsk.db.RepositoryLocator; import co.rsk.util.HexUtils; +import co.rsk.util.StringUtils; import org.bouncycastle.util.encoders.DecoderException; import org.ethereum.core.Block; import org.ethereum.core.Blockchain; import org.ethereum.core.Transaction; import org.ethereum.core.TransactionPool; import org.ethereum.rpc.exception.RskJsonRpcRequestException; +import org.ethereum.rpc.parameters.BlockHashParam; +import org.ethereum.rpc.parameters.HexNumberParam; import java.util.List; import java.util.Optional; -import static co.rsk.crypto.Keccak256.HASH_LEN; import static org.ethereum.rpc.exception.RskJsonRpcRequestException.blockNotFound; import static org.ethereum.rpc.exception.RskJsonRpcRequestException.invalidParamError; +import static org.ethereum.rpc.parameters.HashParam32.*; /** * Retrieves information requested by web3 based on the block identifier: @@ -84,10 +87,18 @@ public Optional getBlock(String identifier) { block = blockchain.getBlockByNumber(0); break; default: - byte[] hash = getBlockHash(identifier); - block = hash.length == HASH_LEN ? - blockchain.getBlockByHash(hash) - : blockchain.getBlockByNumber(getBlockNumber(identifier)); + if (BlockHashParam.isHash32HexLengthValid(identifier) + && HexUtils.isHex(identifier, HexUtils.hasHexPrefix(identifier) ? 2 : 0)) { + byte[] hash = getBlockHash(identifier); + if (hash.length != HASH_BYTE_LENGTH) { + throw invalidParamError(String.format("invalid block hash %s", identifier)); + } + block = blockchain.getBlockByHash(hash); + } else if (HexNumberParam.isHexNumberLengthValid(identifier)) { + block = blockchain.getBlockByNumber(getBlockNumber(identifier)); + } else { + throw invalidParamError(String.format("invalid block identifier %s", StringUtils.trim(identifier))); + } } return Optional.ofNullable(block); diff --git a/rskj-core/src/main/java/co/rsk/util/StringUtils.java b/rskj-core/src/main/java/co/rsk/util/StringUtils.java index 02ef4106555..73ff8c56c65 100644 --- a/rskj-core/src/main/java/co/rsk/util/StringUtils.java +++ b/rskj-core/src/main/java/co/rsk/util/StringUtils.java @@ -22,7 +22,7 @@ public class StringUtils { - private static final int DEFAULT_MAX_LEN = 64; + private static final int DEFAULT_MAX_LEN = 66; // 0x + 32 bytes, where each byte is represented by 2 hex characters private StringUtils() { /* hidden */ } diff --git a/rskj-core/src/main/java/org/ethereum/rpc/parameters/HashParam32.java b/rskj-core/src/main/java/org/ethereum/rpc/parameters/HashParam32.java index 4ff85034314..2de92a57e7e 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/parameters/HashParam32.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/parameters/HashParam32.java @@ -24,12 +24,15 @@ import static co.rsk.util.HexUtils.stringHexToByteArray; public abstract class HashParam32 { - private static final int HASH_BYTE_LENGTH = 32; + public static final int HASH_BYTE_LENGTH = Keccak256.HASH_LEN; + public static final int MIN_HASH_HEX_LEN = 2 * HASH_BYTE_LENGTH; // 2 hex characters per byte + public static final int MAX_HASH_HEX_LEN = MIN_HASH_HEX_LEN + 2; // 2 bytes for 0x prefix + private final Keccak256 hash; HashParam32(String hashType, String hash) { - if (hash == null || hash.isEmpty()) { - throw RskJsonRpcRequestException.invalidParamError("Invalid " + hashType + ": empty or null."); + if (!isHash32HexLengthValid(hash)) { + throw RskJsonRpcRequestException.invalidParamError("Invalid " + hashType + ": incorrect length."); } byte[] hashBytes; @@ -50,4 +53,8 @@ public abstract class HashParam32 { public Keccak256 getHash() { return hash; } + + public static boolean isHash32HexLengthValid(String hex) { + return hex != null && hex.length() >= MIN_HASH_HEX_LEN && hex.length() <= MAX_HASH_HEX_LEN; + } } diff --git a/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexAddressParam.java b/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexAddressParam.java index 863a682018c..338755defa9 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexAddressParam.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexAddressParam.java @@ -31,11 +31,15 @@ public class HexAddressParam implements Serializable { private static final long serialVersionUID = 1L; + public static final int HEX_ADDR_BYTE_LENGTH = RskAddress.LENGTH_IN_BYTES; + public static final int MIN_HEX_ADDR_LEN = 2 * HEX_ADDR_BYTE_LENGTH; // 2 hex characters per byte + public static final int MAX_HEX_ADDR_LEN = MIN_HEX_ADDR_LEN + 2; // 2 bytes for 0x prefix + private transient final RskAddress address; public HexAddressParam(String hexAddress) { - if (hexAddress == null || hexAddress.isEmpty()) { - throw RskJsonRpcRequestException.invalidParamError("Invalid address: empty or null."); + if (!isHexAddressLengthValid(hexAddress)) { + throw RskJsonRpcRequestException.invalidParamError("Invalid address: null, empty or invalid hex value."); } try { @@ -53,6 +57,10 @@ public RskAddress getAddress() { public String toString() { return address.toString(); } + + public static boolean isHexAddressLengthValid(String hex) { + return hex != null && hex.length() >= MIN_HEX_ADDR_LEN && hex.length() <= MAX_HEX_ADDR_LEN; + } public static class Deserializer extends StdDeserializer { private static final long serialVersionUID = 1L; diff --git a/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexNumberParam.java b/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexNumberParam.java index bbb3539fdd7..14f206cb038 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexNumberParam.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexNumberParam.java @@ -18,6 +18,7 @@ package org.ethereum.rpc.parameters; import co.rsk.util.HexUtils; +import co.rsk.util.StringUtils; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; @@ -32,9 +33,16 @@ public class HexNumberParam implements Serializable { private static final long serialVersionUID = 1L; + public static final int HEX_NUM_BYTE_LENGTH = 32; + public static final int MAX_HEX_NUM_LEN = 2 + 2 * HEX_NUM_BYTE_LENGTH; // 2 bytes for 0x prefix; 2 hex characters per byte + private final String hexNumber; public HexNumberParam(String hexNumber) { + if (!isHexNumberLengthValid(hexNumber)) { + throw RskJsonRpcRequestException.invalidParamError("Invalid param: " + StringUtils.trim(hexNumber)); + } + boolean hasPrefix = HexUtils.hasHexPrefix(hexNumber); if (!HexUtils.isHex(hexNumber.toLowerCase(), hasPrefix ? 2 : 0)) { try { @@ -56,6 +64,10 @@ public String toString() { return this.hexNumber; } + public static boolean isHexNumberLengthValid(String hex) { + return hex != null && hex.length() <= MAX_HEX_NUM_LEN; + } + public static class Deserializer extends StdDeserializer { private static final long serialVersionUID = 1L; diff --git a/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexStringParam.java b/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexStringParam.java index c36d838ed86..55b70177f19 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexStringParam.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/parameters/HexStringParam.java @@ -18,14 +18,21 @@ package org.ethereum.rpc.parameters; import co.rsk.util.HexUtils; +import co.rsk.util.StringUtils; import org.ethereum.rpc.exception.RskJsonRpcRequestException; +import static org.ethereum.rpc.parameters.HexNumberParam.isHexNumberLengthValid; + public abstract class HexStringParam { HexStringParam(String hexString) { - if(hexString.isEmpty()) { + if (hexString.isEmpty()) { return; } + if (!isHexNumberLengthValid(hexString)) { + throw RskJsonRpcRequestException.invalidParamError("Invalid argument: " + StringUtils.trim(hexString)); + } + if (!HexUtils.hasHexPrefix(hexString) || !HexUtils.isHex(hexString,2)) { throw RskJsonRpcRequestException.invalidParamError(String.format("Invalid argument \"%s\": param should be a hex value string.", hexString)); } diff --git a/rskj-core/src/test/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKeyTest.java b/rskj-core/src/test/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKeyTest.java index 1fbe8101c0b..274226115c1 100644 --- a/rskj-core/src/test/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKeyTest.java +++ b/rskj-core/src/test/java/co/rsk/pcc/bto/ExtractPublicKeyFromExtendedPublicKeyTest.java @@ -91,7 +91,7 @@ void failsUponInvalidPublicKey() { }); Assertions.fail(); } catch (NativeContractIllegalArgumentException e) { - Assertions.assertEquals("Invalid extended public key 'tpubD6NzVbkrYhZ4YHQqwWz3Tm1ESZ9AidobeyLG4mEezB6hN8gFFWrcjczyF77L...'", e.getMessage()); + Assertions.assertEquals("Invalid extended public key 'tpubD6NzVbkrYhZ4YHQqwWz3Tm1ESZ9AidobeyLG4mEezB6hN8gFFWrcjczyF77Lw3...'", e.getMessage()); } } diff --git a/rskj-core/src/test/java/co/rsk/pcc/bto/HDWalletUtilsHelperTest.java b/rskj-core/src/test/java/co/rsk/pcc/bto/HDWalletUtilsHelperTest.java index 7722487229c..c295bed1a83 100644 --- a/rskj-core/src/test/java/co/rsk/pcc/bto/HDWalletUtilsHelperTest.java +++ b/rskj-core/src/test/java/co/rsk/pcc/bto/HDWalletUtilsHelperTest.java @@ -63,7 +63,7 @@ void validateAndExtractNetworkFromExtendedPublicKeyWithInvalidXpub() { void validateAndExtractNetworkFromExtendedPublicKeyWithInvalidLongXpub() { NativeContractIllegalArgumentException exception = Assertions.assertThrows( NativeContractIllegalArgumentException.class, - () -> helper.validateAndExtractNetworkFromExtendedPublicKey("completelyInvalidLongLongLongLongLongLongLongLongLongLongLonStuff")); - assertEquals("Invalid extended public key 'completelyInvalidLongLongLongLongLongLongLongLongLongLongLonStuf...'", exception.getMessage()); + () -> helper.validateAndExtractNetworkFromExtendedPublicKey("completelyInvalidLongLongLongLongLongLongLongLongLongLongLongLStuff")); + assertEquals("Invalid extended public key 'completelyInvalidLongLongLongLongLongLongLongLongLongLongLongLStuf...'", exception.getMessage()); } } diff --git a/rskj-core/src/test/java/co/rsk/util/StringUtilsTest.java b/rskj-core/src/test/java/co/rsk/util/StringUtilsTest.java index 2cd58093e6f..6cfaaf5c5b8 100644 --- a/rskj-core/src/test/java/co/rsk/util/StringUtilsTest.java +++ b/rskj-core/src/test/java/co/rsk/util/StringUtilsTest.java @@ -24,8 +24,8 @@ class StringUtilsTest { - private static final String STR_64_CHARS = "ee5c851e70650111887bb6c04e18ef4353391abe37846234c17895a9ca2b33d5"; - private static final String STR_65_CHARS = STR_64_CHARS + "a"; + private static final String STR_66_CHARS = "0xee5c851e70650111887bb6c04e18ef4353391abe37846234c17895a9ca2b33d5"; + private static final String STR_67_CHARS = STR_66_CHARS + "a"; @Test void testTrim() { @@ -33,11 +33,11 @@ void testTrim() { assertEquals("", StringUtils.trim("")); assertEquals("a", StringUtils.trim("a")); - assertEquals(64, STR_64_CHARS.length()); - assertEquals(65, STR_65_CHARS.length()); + assertEquals(66, STR_66_CHARS.length()); + assertEquals(67, STR_67_CHARS.length()); - assertEquals(STR_64_CHARS, StringUtils.trim(STR_64_CHARS)); - assertEquals(STR_64_CHARS + "...", StringUtils.trim(STR_65_CHARS)); + assertEquals(STR_66_CHARS, StringUtils.trim(STR_66_CHARS)); + assertEquals(STR_66_CHARS + "...", StringUtils.trim(STR_67_CHARS)); } @Test From be9de732c3ef321f33a3cc65b107c3359028378e Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 20 Sep 2024 11:44:01 +0300 Subject: [PATCH 84/89] refactor: apply suggestions --- .../java/co/rsk/rpc/Web3InformationRetriever.java | 6 +++--- rskj-core/src/main/java/co/rsk/util/HexUtils.java | 11 ----------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java b/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java index 141dd68f0bc..0f8dc1f69cb 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java +++ b/rskj-core/src/main/java/co/rsk/rpc/Web3InformationRetriever.java @@ -28,7 +28,7 @@ import org.ethereum.core.Transaction; import org.ethereum.core.TransactionPool; import org.ethereum.rpc.exception.RskJsonRpcRequestException; -import org.ethereum.rpc.parameters.BlockHashParam; +import org.ethereum.rpc.parameters.HashParam32; import org.ethereum.rpc.parameters.HexNumberParam; import java.util.List; @@ -36,7 +36,7 @@ import static org.ethereum.rpc.exception.RskJsonRpcRequestException.blockNotFound; import static org.ethereum.rpc.exception.RskJsonRpcRequestException.invalidParamError; -import static org.ethereum.rpc.parameters.HashParam32.*; +import static org.ethereum.rpc.parameters.HashParam32.HASH_BYTE_LENGTH; /** * Retrieves information requested by web3 based on the block identifier: @@ -87,7 +87,7 @@ public Optional getBlock(String identifier) { block = blockchain.getBlockByNumber(0); break; default: - if (BlockHashParam.isHash32HexLengthValid(identifier) + if (HashParam32.isHash32HexLengthValid(identifier) && HexUtils.isHex(identifier, HexUtils.hasHexPrefix(identifier) ? 2 : 0)) { byte[] hash = getBlockHash(identifier); if (hash.length != HASH_BYTE_LENGTH) { diff --git a/rskj-core/src/main/java/co/rsk/util/HexUtils.java b/rskj-core/src/main/java/co/rsk/util/HexUtils.java index f04aae64aef..55bcef88128 100644 --- a/rskj-core/src/main/java/co/rsk/util/HexUtils.java +++ b/rskj-core/src/main/java/co/rsk/util/HexUtils.java @@ -361,15 +361,4 @@ public static int jsonHexToInt(final String param) { return Integer.parseInt(preResult, 16); } - - public static int jsonHexToIntOptionalPrefix(final String param) { - if (!hasHexPrefix(param) && !HexUtils.isHex(param)) { - throw invalidParamError(INCORRECT_HEX_SYNTAX); - } - - String preResult = removeHexPrefix(param); - - return Integer.parseInt(preResult, 16); - } - } From e8b4a62327bc7c03f31dbd4c989a393671b92747 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 11 Oct 2024 15:21:19 +0300 Subject: [PATCH 85/89] build: bump version to 6.5.0 --- rskj-core/src/main/resources/version.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rskj-core/src/main/resources/version.properties b/rskj-core/src/main/resources/version.properties index f6a49d73be6..8a6347af516 100644 --- a/rskj-core/src/main/resources/version.properties +++ b/rskj-core/src/main/resources/version.properties @@ -1,2 +1,2 @@ -versionNumber='6.4.0' +versionNumber='6.5.0' modifier="SNAPSHOT" From dc060e02bd11848ee667f81fa1f2e7fe3bbc4e2d Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Mon, 30 Sep 2024 10:33:22 -0400 Subject: [PATCH 86/89] Skipping SonarQube analysis on fork repos --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index dbfec017604..1167b72a40d 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -105,7 +105,7 @@ jobs: GH_REF: ${{ github.ref }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} run: | - if [ "$GH_EVENT" = pull_request ]; then + if [ "$GH_EVENT" = pull_request ] && [ "${{ github.event.pull_request.head.repo.fork }}" != "true" ]; then ./gradlew -Dorg.gradle.jvmargs=-Xmx5g sonarqube --no-daemon -x build -x test \ -Dsonar.pullrequest.base="$GH_PR_BASE_REF" \ -Dsonar.pullrequest.branch="$GH_PR_HEAD_REF" \ From 8f53b4cd33d5fba03167865733734283992a9d01 Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Mon, 30 Sep 2024 10:35:16 -0400 Subject: [PATCH 87/89] Adding REPO_OWNER env variable --- .github/workflows/rit.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/rit.yml b/.github/workflows/rit.yml index 9fac1b2cf9d..3daa9a81c74 100644 --- a/.github/workflows/rit.yml +++ b/.github/workflows/rit.yml @@ -96,6 +96,14 @@ jobs: exit 1 fi + # Set the Repo Owner + if [ -n "${{ github.event.pull_request.head.repo.owner.login }}" ]; then + REPO_OWNER=${{ github.event.pull_request.head.repo.owner.login }} + else + REPO_OWNER=${{ github.repository_owner }} + fi + + echo "REPO_OWNER=$REPO_OWNER" >> $GITHUB_ENV echo "RSKJ_BRANCH=$RSKJ_BRANCH" >> $GITHUB_ENV echo "RIT_BRANCH=$RIT_BRANCH" >> $GITHUB_ENV echo "POWPEG_BRANCH=$POWPEG_BRANCH" >> $GITHUB_ENV @@ -121,6 +129,7 @@ jobs: rskj-branch: ${{ env.RSKJ_BRANCH }} powpeg-node-branch: ${{ env.POWPEG_BRANCH }} rit-branch: ${{ env.RIT_BRANCH }} + repo-owner: ${{ env.REPO_OWNER }} - name: Send Slack Notification on Success if: success() && github.event.pull_request.head.repo.owner.login == 'rsksmart' From 2f64677e238453f46245c4348a1839be4d385973 Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Wed, 2 Oct 2024 10:10:46 -0400 Subject: [PATCH 88/89] addressing comments --- .github/workflows/rit.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rit.yml b/.github/workflows/rit.yml index 3daa9a81c74..c7ca53999dd 100644 --- a/.github/workflows/rit.yml +++ b/.github/workflows/rit.yml @@ -36,6 +36,8 @@ jobs: github_event_pull_request_number: ${{ github.event.pull_request.number }} github_head_ref: ${{ github.head_ref }} github_ref_name: ${{ github.ref_name }} + github_event_pull_request_head_repo_owner_login: ${{ github.event.pull_request.head.repo.owner.login }} + github_repository_owner: ${{ github.repository_owner }} run: | PR_DESCRIPTION=pr-description.txt @@ -97,11 +99,8 @@ jobs: fi # Set the Repo Owner - if [ -n "${{ github.event.pull_request.head.repo.owner.login }}" ]; then - REPO_OWNER=${{ github.event.pull_request.head.repo.owner.login }} - else - REPO_OWNER=${{ github.repository_owner }} - fi + REPO_OWNER="${github_event_pull_request_head_repo_owner_login:-$github_repository_owner}" + echo "REPO_OWNER=$REPO_OWNER" >> $GITHUB_ENV echo "RSKJ_BRANCH=$RSKJ_BRANCH" >> $GITHUB_ENV From 9f433cf2568ef68550ecff9a77f7ee4056117471 Mon Sep 17 00:00:00 2001 From: Reynold Morel Date: Fri, 11 Oct 2024 10:35:04 -0400 Subject: [PATCH 89/89] Updating rit tag --- .github/workflows/rit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rit.yml b/.github/workflows/rit.yml index c7ca53999dd..b5d76120548 100644 --- a/.github/workflows/rit.yml +++ b/.github/workflows/rit.yml @@ -123,7 +123,7 @@ jobs: echo "SAFE_BRANCH_NAME=$SAFE_BRANCH_NAME" >> $GITHUB_ENV - name: Run Rootstock Integration Tests - uses: rsksmart/rootstock-integration-tests@497172fd38dcfaf48c77f9bb1eeb6617eef5eed6 #v1 + uses: rsksmart/rootstock-integration-tests@e86332474179a63f027d0fe969687d3d24f34c29 #v1 with: rskj-branch: ${{ env.RSKJ_BRANCH }} powpeg-node-branch: ${{ env.POWPEG_BRANCH }}