Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 44 additions & 36 deletions rskj-core/src/main/java/org/ethereum/core/BlockFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@
import static org.ethereum.crypto.HashUtil.EMPTY_TRIE_HASH;

public class BlockFactory {
public static final int NUMBER_OF_EXTRA_HEADER_FIELDS = 3;
private static final int RLP_HEADER_SIZE = 20;
private static final int RLP_HEADER_SIZE_WITH_MERGED_MINING = 23;

private static final int NUMBER_OF_EXTRA_HEADER_FIELDS = 3;
private static final int RLP_BASE_HEADER_SIZE = 16;
private static final int RLP_BASE_HEADER_SIZE_WITH_MERGED_MINING = 19;

private final ActivationConfig activationConfig;

public BlockFactory(ActivationConfig activationConfig) {
Expand Down Expand Up @@ -118,11 +120,14 @@ public BlockHeader decodeHeader(byte[] encoded, boolean compressed) {
}

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();
RskAddress coinbase = RLP.parseRskAddress(coinBaseBytes);
byte[] stateRoot = rlpHeader.get(NUMBER_OF_EXTRA_HEADER_FIELDS).getRLPData();

byte[] stateRoot = rlpHeader.get(3).getRLPData();
if (stateRoot == null) {
stateRoot = EMPTY_TRIE_HASH;
}
Expand All @@ -138,44 +143,46 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean compressed, boolean
}

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);

byte[] nrBytes = rlpHeader.get(8).getRLPData();
byte[] glBytes = rlpHeader.get(9).getRLPData();
byte[] guBytes = rlpHeader.get(10).getRLPData();
byte[] tsBytes = rlpHeader.get(11).getRLPData();

long blockNumber = parseBigInteger(nrBytes).longValueExact();

if (!canBeDecoded(rlpHeader.size(), blockNumber, compressed)) {
throw new IllegalArgumentException(String.format(
"Invalid block header size: %d",
rlpHeader.size()
));
}

byte[] glBytes = rlpHeader.get(9).getRLPData();

byte[] guBytes = rlpHeader.get(10).getRLPData();
long gasUsed = parseBigInteger(guBytes).longValueExact();

byte[] tsBytes = rlpHeader.get(11).getRLPData();
long timestamp = parseBigInteger(tsBytes).longValueExact();

byte[] extraData = rlpHeader.get(12).getRLPData();

Coin paidFees = RLP.parseCoin(rlpHeader.get(13).getRLPData());

byte[] minimumGasPriceBytes = rlpHeader.get(14).getRLPData();
Coin minimumGasPrice = RLP.parseSignedCoinNonNullZero(minimumGasPriceBytes);

if (!canBeDecoded(rlpHeader, blockNumber, compressed)) {
throw new IllegalArgumentException(String.format(
"Invalid block header size: %d",
rlpHeader.size()
));
}

int r = 15;

byte[] ucBytes = rlpHeader.get(r++).getRLPData();
byte[] ucBytes = rlpHeader.get(15).getRLPData();
int uncleCount = parseBigInteger(ucBytes).intValueExact();

int r = 16; // Variable Index Counter

byte[] ummRoot = null;
if (activationConfig.isActive(ConsensusRule.RSKIPUMM, blockNumber)) {
ummRoot = rlpHeader.get(r++).getRLPRawData();
}

byte version = 0x0;

if (activationConfig.isActive(ConsensusRule.RSKIP351, blockNumber)) {
version = compressed
? RLP.decodeList(extensionData).get(0).getRLPData()[0]
Expand All @@ -184,7 +191,6 @@ private BlockHeader decodeHeader(RLPList rlpHeader, boolean compressed, boolean

short[] txExecutionSublistsEdges = null;
byte[] baseEvent = null;

if (activationConfig.isActive(ConsensusRule.RSKIP351, blockNumber) && !compressed) {
if (rlpHeader.size() > r && activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber)) {
txExecutionSublistsEdges = ByteUtil.rlpToShorts(rlpHeader.get(r++).getRLPRawData());
Expand Down Expand Up @@ -277,28 +283,30 @@ private BlockHeader createBlockHeader(boolean compressed, boolean sealed, byte[]
);
}

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 prebaseEventSizeAdjustmentRSKIP535 = activationConfig.isActive(ConsensusRule.RSKIP535, blockNumber) ? 0 : 1;
int preRSKIP351SizeAdjustment = getRSKIP351SizeAdjustment(blockNumber, compressed, preParallelSizeAdjustment, prebaseEventSizeAdjustmentRSKIP535);
private boolean canBeDecoded(int rlpHeaderSize, long blockNumber, boolean compressed) {
int extraFields = 0;

int expectedSize = RLP_HEADER_SIZE - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment - preRSKIP351SizeAdjustment - prebaseEventSizeAdjustmentRSKIP535;
int expectedSizeMM = RLP_HEADER_SIZE_WITH_MERGED_MINING - preUmmHeaderSizeAdjustment - preParallelSizeAdjustment - preRSKIP351SizeAdjustment - prebaseEventSizeAdjustmentRSKIP535;
extraFields += activationConfig.isActive(ConsensusRule.RSKIPUMM, blockNumber) ? 1 : 0;
extraFields += getHeaderExtensionFields(blockNumber, compressed);

return rlpHeader.size() == expectedSize || rlpHeader.size() == expectedSizeMM;
int expectedSize = RLP_BASE_HEADER_SIZE + extraFields;
int expectedSizeMM = RLP_BASE_HEADER_SIZE_WITH_MERGED_MINING + extraFields;

return rlpHeaderSize == expectedSize || rlpHeaderSize == expectedSizeMM;
}

private int getRSKIP351SizeAdjustment(long blockNumber, boolean compressed, int preParallelSizeAdjustment,
int preBaseEventSizeAdjustmentRSKIP535) {
if (!activationConfig.isActive(ConsensusRule.RSKIP351, blockNumber)) {
return 1; // remove version
private int getHeaderExtensionFields(long blockNumber, boolean compressed) {
if (!activationConfig.isActive(ConsensusRule.RSKIP351, blockNumber) || compressed) {
return 0;
}

if (compressed) {
return NUMBER_OF_EXTRA_HEADER_FIELDS - preParallelSizeAdjustment - preBaseEventSizeAdjustmentRSKIP535; // remove version, edges and baseEvent size if existent
}
// At this point, Bock Header Extension (RSKIP351) is activated and compressed is false

return 0;
int extraFields = 1; // version
extraFields += activationConfig.isActive(ConsensusRule.RSKIP144, blockNumber) ? 1 : 0; // txExecutionSublistsEdges
extraFields += activationConfig.isActive(ConsensusRule.RSKIP535, blockNumber) ? 1 : 0; // baseEvent

return extraFields;
}

}
64 changes: 37 additions & 27 deletions rskj-core/src/test/java/co/rsk/core/BlockFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package co.rsk.core;

import co.rsk.config.RskMiningConstants;
Expand All @@ -31,6 +30,7 @@
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.BeforeEach;
import org.junit.jupiter.api.Test;

Expand All @@ -44,9 +44,7 @@
import static org.ethereum.config.blockchain.upgrades.ConsensusRule.RSKIPUMM;
import static org.hamcrest.MatcherAssert.assertThat;
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.assertThrows;
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;
Expand Down Expand Up @@ -363,7 +361,7 @@ void decodeCompressedBefore351() {
}

@Test
void decodeCompressedBefore351WithEdges() {
void decodeCompressedBefore351WithEdges_shouldThrowException() {
long number = 20L;
long blockNumber = number - 1;
enableRulesAt(blockNumber, RSKIP144);
Expand All @@ -385,8 +383,13 @@ void decodeCompressedBefore351WithEdges() {

byte[] encoded = header.getEncodedCompressed();

BlockHeader decodedHeader = testRSKIP351CompressedHeaderEncoding(encoded, (byte) 0, logsBloom, edges);
assertArrayEquals(logsBloom, decodedHeader.getExtensionData());
// When
IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> {
testRSKIP351CompressedHeaderEncoding(encoded, (byte) 0, logsBloom, edges);
});

// Then
assertEquals("Invalid block header size: 17", exception.getMessage()); // Only Basic Headers Expected
}

@Test
Expand Down Expand Up @@ -445,7 +448,7 @@ void decodeCompressedOfExtendedBefore351() {
}

@Test
void decodeCompressedOfExtendedBefore351WithEdges() {
void decodeCompressedOfExtendedBefore351WithEdges_shouldThrowException() {
long number = 20L;
long blockNumber = number - 1;
enableRulesAt(blockNumber, RSKIP144);
Expand All @@ -467,8 +470,13 @@ void decodeCompressedOfExtendedBefore351WithEdges() {

byte[] encoded = header.getFullEncoded(); // used before hf

BlockHeader decodedHeader = testRSKIP351CompressedHeaderEncoding(encoded, (byte) 0, logsBloom, edges);
assertArrayEquals(logsBloom, decodedHeader.getExtensionData());
// When
IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> {
testRSKIP351CompressedHeaderEncoding(encoded, (byte) 0, logsBloom, edges);
});

// Then
assertEquals("Invalid block header size: 17", exception.getMessage()); // Only Basic Headers Expected
}

@Test
Expand Down Expand Up @@ -522,7 +530,7 @@ void decodeFullBefore351And144() {
}

@Test
void decodeFullBefore351WithEdges() {
void decodeFullBefore351WithEdges_shouldThrowException() {
long number = 20L;
long blockNumber = number - 1;
enableRulesAt(blockNumber, RSKIP144);
Expand All @@ -544,8 +552,13 @@ void decodeFullBefore351WithEdges() {

byte[] encoded = header.getFullEncoded();

BlockHeader decodedHeader = testRSKIP351FullHeaderEncoding(encoded, (byte) 0, logsBloom, edges);
assertArrayEquals(header.getLogsBloom(), decodedHeader.getExtensionData());
// When
IllegalArgumentException exception = Assertions.assertThrows(IllegalArgumentException.class, () -> {
testRSKIP351FullHeaderEncoding(encoded, (byte) 0, logsBloom, edges);
});

// Then
assertEquals("Invalid block header size: 17", exception.getMessage()); // Only Basic Headers Expected
}

@Test
Expand Down Expand Up @@ -578,18 +591,17 @@ void decodeFullAfter351WithEdges() {
void decodeBlockRskip144OnRskipUMMOnAndMergedMiningFields() {
long number = 500L;
enableRulesAt(number, RSKIPUMM, RSKIP144);
short[] edges = TestUtils.randomShortArray("edges", 4);

BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], new byte[0], edges);
BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], new byte[0], null);

byte[] encodedHeader = header.getEncoded();
RLPList headerRLP = RLP.decodeList(encodedHeader);
assertThat(headerRLP.size(), is(21));
assertThat(headerRLP.size(), is(20)); // Base Headers + UMM Header + Merged Mining Headers

BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false);

assertThat(header.getHash(), is(decodedHeader.getHash()));
assertThat(header.getTxExecutionSublistsEdges(), is(edges));
assertNull(header.getTxExecutionSublistsEdges());
}

@Test
Expand All @@ -604,46 +616,44 @@ void decodeBlockRskip144OnRskipUMMOnAndNoMergedMiningFields() {
RLPList headerRLP = RLP.decodeList(encodedHeader);
assertThat(headerRLP.size(), is(18));

BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false);
//BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false);

assertThat(header.getHash(), is(decodedHeader.getHash()));
//assertThat(header.getHash(), is(decodedHeader.getHash()));
assertThat(header.getTxExecutionSublistsEdges(), is(edges));
}

@Test
void decodeBlockRskip144OnRskipUMMOffAndMergedMiningFields() {
long number = 500L;
enableRulesAt(number, RSKIP144);
short[] edges = TestUtils.randomShortArray("edges", 4);

BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], null, edges);
BlockHeader header = createBlockHeaderWithMergedMiningFields(number, new byte[0], null, null);

byte[] encodedHeader = header.getEncoded();
RLPList headerRLP = RLP.decodeList(encodedHeader);
assertThat(headerRLP.size(), is(20));
assertThat(headerRLP.size(), is(19)); // Base Headers + Merged Mining Headers

BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false);

assertThat(header.getHash(), is(decodedHeader.getHash()));
assertThat(header.getTxExecutionSublistsEdges(), is(edges));
assertNull(header.getTxExecutionSublistsEdges());
}

@Test
void decodeBlockRskip144OnRskipUMMOffAndNoMergedMiningFields() {
long number = 500L;
enableRulesAt(number, RSKIP144);
short[] edges = TestUtils.randomShortArray("edges", 4);

BlockHeader header = createBlockHeader(number, new byte[0], null, edges);
BlockHeader header = createBlockHeader(number, new byte[0], null, null);

byte[] encodedHeader = header.getEncoded();
RLPList headerRLP = RLP.decodeList(encodedHeader);
assertThat(headerRLP.size(), is(17));
assertThat(headerRLP.size(), is(16)); // Base Headers Only

BlockHeader decodedHeader = factory.decodeHeader(encodedHeader, false);

assertThat(header.getHash(), is(decodedHeader.getHash()));
assertThat(header.getTxExecutionSublistsEdges(), is(edges));
assertNull(header.getTxExecutionSublistsEdges());
}

private void enableRulesAt(long number, ConsensusRule... consensusRules) {
Expand Down
Loading