Remove RLPUtils from ProofOfWorkValidationRule (#83)

mbaxter 6 years ago committed by GitHub
parent 1f479f75fd
commit 2ede24fe95
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/BlockHeaderBuilder.java
  2. 72
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/headervalidationrules/ProofOfWorkValidationRule.java
  3. 115
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/mainnet/headervalidationrules/ProofOfWorkValidationRuleTest.java

@ -63,6 +63,25 @@ public class BlockHeaderBuilder {
return new BlockHeaderBuilder(); return new BlockHeaderBuilder();
} }
public static BlockHeaderBuilder fromHeader(final BlockHeader header) {
return create()
.parentHash(header.getParentHash())
.ommersHash(header.getOmmersHash())
.coinbase(header.getCoinbase())
.stateRoot(header.getStateRoot())
.transactionsRoot(header.getTransactionsRoot())
.receiptsRoot(header.getReceiptsRoot())
.logsBloom(header.getLogsBloom())
.difficulty(header.getDifficulty())
.number(header.getNumber())
.gasLimit(header.getGasLimit())
.gasUsed(header.getGasUsed())
.timestamp(header.getTimestamp())
.extraData(header.getExtraData())
.mixHash(header.getMixHash())
.nonce(header.getNonce());
}
public BlockHeader buildBlockHeader() { public BlockHeader buildBlockHeader() {
validateBlockHeader(); validateBlockHeader();

@ -12,64 +12,34 @@
*/ */
package tech.pegasys.pantheon.ethereum.mainnet.headervalidationrules; package tech.pegasys.pantheon.ethereum.mainnet.headervalidationrules;
import tech.pegasys.pantheon.crypto.BouncyCastleMessageDigestFactory;
import tech.pegasys.pantheon.ethereum.core.BlockHeader; import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.Hash; import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.mainnet.DetachedBlockHeaderValidationRule; import tech.pegasys.pantheon.ethereum.mainnet.DetachedBlockHeaderValidationRule;
import tech.pegasys.pantheon.ethereum.mainnet.EthHasher; import tech.pegasys.pantheon.ethereum.mainnet.EthHasher;
import tech.pegasys.pantheon.ethereum.rlp.RLP; import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.rlp.RlpUtils;
import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.Bytes32;
import tech.pegasys.pantheon.util.bytes.BytesValue; import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.BytesValues; import tech.pegasys.pantheon.util.bytes.BytesValues;
import tech.pegasys.pantheon.util.uint.UInt256; import tech.pegasys.pantheon.util.uint.UInt256;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
public final class ProofOfWorkValidationRule implements DetachedBlockHeaderValidationRule { public final class ProofOfWorkValidationRule implements DetachedBlockHeaderValidationRule {
private static final Logger LOG = LogManager.getLogger(ProofOfWorkValidationRule.class); private static final Logger LOG = LogManager.getLogger();
private static final int SERIALIZED_HASH_SIZE = 33;
private static final int SERIALIZED_NONCE_SIZE = 9;
private static final BigInteger ETHHASH_TARGET_UPPER_BOUND = BigInteger.valueOf(2).pow(256); private static final BigInteger ETHHASH_TARGET_UPPER_BOUND = BigInteger.valueOf(2).pow(256);
private static final EthHasher HASHER = new EthHasher.Light(); private static final EthHasher HASHER = new EthHasher.Light();
private static final ThreadLocal<MessageDigest> KECCAK_256 =
ThreadLocal.withInitial(
() -> {
try {
return BouncyCastleMessageDigestFactory.create(
tech.pegasys.pantheon.crypto.Hash.KECCAK256_ALG);
} catch (final NoSuchAlgorithmException ex) {
throw new IllegalStateException(ex);
}
});
@Override @Override
public boolean validate(final BlockHeader header, final BlockHeader parent) { public boolean validate(final BlockHeader header, final BlockHeader parent) {
final MessageDigest keccak256 = KECCAK_256.get();
final byte[] bytes = RLP.encode(header::writeTo).extractArray();
final int listOffset = RlpUtils.decodeOffset(bytes, 0);
final int length = RlpUtils.decodeLength(bytes, 0);
final byte[] listHeadBuff = new byte[10];
final int newLength = length - SERIALIZED_HASH_SIZE - SERIALIZED_NONCE_SIZE;
final int sizeLen = writeListPrefix(newLength - listOffset, listHeadBuff);
keccak256.update(listHeadBuff, 0, sizeLen);
keccak256.update(bytes, listOffset, newLength - sizeLen);
final byte[] hashBuffer = new byte[64]; final byte[] hashBuffer = new byte[64];
HASHER.hash(hashBuffer, header.getNonce(), header.getNumber(), keccak256.digest()); final Hash headerHash = hashHeader(header);
HASHER.hash(hashBuffer, header.getNonce(), header.getNumber(), headerHash.extractArray());
if (header.getDifficulty().isZero()) { if (header.getDifficulty().isZero()) {
LOG.trace("Rejecting header because difficulty is 0"); LOG.trace("Rejecting header because difficulty is 0");
@ -105,19 +75,31 @@ public final class ProofOfWorkValidationRule implements DetachedBlockHeaderValid
return true; return true;
} }
private Hash hashHeader(final BlockHeader header) {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
// Encode header without nonce and mixhash
out.startList();
out.writeBytesValue(header.getParentHash());
out.writeBytesValue(header.getOmmersHash());
out.writeBytesValue(header.getCoinbase());
out.writeBytesValue(header.getStateRoot());
out.writeBytesValue(header.getTransactionsRoot());
out.writeBytesValue(header.getReceiptsRoot());
out.writeBytesValue(header.getLogsBloom().getBytes());
out.writeUInt256Scalar(header.getDifficulty());
out.writeLongScalar(header.getNumber());
out.writeLongScalar(header.getGasLimit());
out.writeLongScalar(header.getGasUsed());
out.writeLongScalar(header.getTimestamp());
out.writeBytesValue(header.getExtraData());
out.endList();
return Hash.hash(out.encoded());
}
@Override @Override
public boolean includeInLightValidation() { public boolean includeInLightValidation() {
return false; return false;
} }
private static int writeListPrefix(final int size, final byte[] target) {
final int sizeLength = 4 - Integer.numberOfLeadingZeros(size) / 8;
target[0] = (byte) (0xf7 + sizeLength);
int shift = 0;
for (int i = 0; i < sizeLength; i++) {
target[sizeLength - i] = (byte) (size >> shift);
shift += 8;
}
return 1 + sizeLength;
}
} }

@ -0,0 +1,115 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.ethereum.mainnet.headervalidationrules;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.ethereum.core.BlockHashFunction;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderBuilder;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ScheduleBasedBlockHashFunction;
import tech.pegasys.pantheon.ethereum.mainnet.ValidationTestUtils;
import tech.pegasys.pantheon.util.uint.UInt256;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ProofOfWorkValidationRuleTest {
private final BlockHeader blockHeader;
private final BlockHeader parentHeader;
private final ProofOfWorkValidationRule validationRule;
public ProofOfWorkValidationRuleTest(final long parentBlockNum, final long blockNum)
throws IOException {
blockHeader = ValidationTestUtils.readHeader(parentBlockNum);
parentHeader = ValidationTestUtils.readHeader(blockNum);
validationRule = new ProofOfWorkValidationRule();
}
@Parameters(name = "block {1}")
public static Collection<Object[]> data() {
return Arrays.asList(
new Object[][] {
{300005, 300006},
{1200000, 1200001},
{4400000, 4400001},
{4400001, 4400002}
});
}
@Test
public void validatesValidBlocks() {
assertThat(validationRule.validate(blockHeader, parentHeader)).isTrue();
}
@Test
public void failsBlockWithZeroValuedDifficulty() {
BlockHeader header =
BlockHeaderBuilder.fromHeader(blockHeader)
.difficulty(UInt256.ZERO)
.blockHashFunction(mainnetBlockHashFunction())
.buildBlockHeader();
assertThat(validationRule.validate(header, parentHeader)).isFalse();
}
@Test
public void failsWithVeryLargeDifficulty() {
UInt256 largeDifficulty = UInt256.of(BigInteger.valueOf(2).pow(255));
BlockHeader header =
BlockHeaderBuilder.fromHeader(blockHeader)
.difficulty(largeDifficulty)
.blockHashFunction(mainnetBlockHashFunction())
.buildBlockHeader();
assertThat(validationRule.validate(header, parentHeader)).isFalse();
}
@Test
public void failsWithMisMatchedMixHash() {
Hash updateMixHash = Hash.wrap(blockHeader.getMixHash().asUInt256().minus(1L).getBytes());
BlockHeader header =
BlockHeaderBuilder.fromHeader(blockHeader)
.mixHash(updateMixHash)
.blockHashFunction(mainnetBlockHashFunction())
.buildBlockHeader();
assertThat(validationRule.validate(header, parentHeader)).isFalse();
}
@Test
public void failsWithMisMatchedNonce() {
long updatedNonce = blockHeader.getNonce() + 1;
BlockHeader header =
BlockHeaderBuilder.fromHeader(blockHeader)
.nonce(updatedNonce)
.blockHashFunction(mainnetBlockHashFunction())
.buildBlockHeader();
assertThat(validationRule.validate(header, parentHeader)).isFalse();
}
private BlockHashFunction mainnetBlockHashFunction() {
ProtocolSchedule<Void> protocolSchedule = MainnetProtocolSchedule.create();
return ScheduleBasedBlockHashFunction.create(protocolSchedule);
}
}
Loading…
Cancel
Save