[23.4] Remove IBFT1 consensus mechanism (#5302)

* remove controller and config

* remove ibftlegacy module

* remove further config options

* run non-mainnet ATs just to be sure

* remove temp change to circle config

---------

Signed-off-by: Sally MacFarlane <macfarla.github@gmail.com>
pull/5329/head
Sally MacFarlane 2 years ago committed by GitHub
parent 14407ade0b
commit 795b7c4dfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      CHANGELOG.md
  2. 2
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningIbft2StallAcceptanceTest.java
  3. 1
      besu/build.gradle
  4. 6
      besu/src/main/java/org/hyperledger/besu/controller/BesuController.java
  5. 154
      besu/src/main/java/org/hyperledger/besu/controller/IbftLegacyBesuControllerBuilder.java
  6. 45
      besu/src/test/java/org/hyperledger/besu/chainimport/RlpBlockImporterTest.java
  7. 28
      besu/src/test/java/org/hyperledger/besu/controller/BesuControllerTest.java
  8. 38
      besu/src/test/resources/ibftlegacy_genesis.json
  9. 11
      config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java
  10. 104
      config/src/main/java/org/hyperledger/besu/config/IbftLegacyConfigOptions.java
  11. 12
      config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java
  12. 8
      config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java
  13. 17
      config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java
  14. 30
      config/src/test/java/org/hyperledger/besu/config/JsonGenesisConfigOptionsTest.java
  15. 51
      consensus/ibftlegacy/build.gradle
  16. 169
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftBlockHashing.java
  17. 85
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftBlockHeaderValidationRulesetFactory.java
  18. 184
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftExtraData.java
  19. 36
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftHelpers.java
  20. 105
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftLegacyBlockInterface.java
  21. 104
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/IbftProtocolSchedule.java
  22. 38
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/LegacyIbftBlockHeaderFunctions.java
  23. 132
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/blockcreation/IbftBlockCreator.java
  24. 147
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/headervalidationrules/IbftExtraDataValidationRule.java
  25. 45
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/headervalidationrules/VoteValidationRule.java
  26. 130
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/protocol/Istanbul99Protocol.java
  27. 90
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/protocol/Istanbul99ProtocolManager.java
  28. 133
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/BftBlockHashingTest.java
  29. 157
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/BftExtraDataCodecTest.java
  30. 166
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/IbftBlockHeaderValidationRulesetFactoryTest.java
  31. 133
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/IbftLegacyBlockInterfaceTest.java
  32. 41
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/IbftLegacyContextBuilder.java
  33. 59
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/TestHelpers.java
  34. 131
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/blockcreation/BftBlockCreatorTest.java
  35. 330
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/headervalidationrules/BftExtraDataValidationRuleTest.java
  36. 159
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/protocol/Istanbul99ProtocolManagerTest.java
  37. 2
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/GenesisFileModule.java
  38. 1
      settings.gradle

@ -2,6 +2,9 @@
## 23.4-RC ## 23.4-RC
### Breaking Changes
- Removed IBFT1 feature [#5302](https://github.com/hyperledger/besu/pull/5302)
### Additions and Improvements ### Additions and Improvements
- Update most dependencies to latest version [#5269](https://github.com/hyperledger/besu/pull/5269) - Update most dependencies to latest version [#5269](https://github.com/hyperledger/besu/pull/5269)
@ -15,9 +18,8 @@
- In `--ethstats`, if the port is not specified in the URI, it will default to 443 and 80 for ssl and non-ssl connections respectively instead of 3000. [#5301](https://github.com/hyperledger/besu/pull/5301) - In `--ethstats`, if the port is not specified in the URI, it will default to 443 and 80 for ssl and non-ssl connections respectively instead of 3000. [#5301](https://github.com/hyperledger/besu/pull/5301)
### Additions and Improvements ### Additions and Improvements
- An alternate build target for the EVM using GraalVM AOT compilation was added. [#5192](https://github.com/hyperledger/besu/pull/5192)
- An alternate build target for the EVM using GraalVM AOT compilaiton was added. [#5192](https://github.com/hyperledger/besu/pull/5192) - To generate the binary install and use GraalVM 23.3.r17 or higher and run `./gradlew nativeCompile`. The binary will be located in `ethereum/evmtool/build/native/nativeCompile`
- To generate the binary install and use GraalVM 23.3.r17 or higher and run `./gradlew naticeCompile`. The binary will be located in `ethereum/evmtool/build/native/nativeCompile`
- Upgrade RocksDB version from 7.7.3 to 8.0.0. Besu Team [contributed](https://github.com/facebook/rocksdb/pull/11099) to this release to make disabling checksum verification work. - Upgrade RocksDB version from 7.7.3 to 8.0.0. Besu Team [contributed](https://github.com/facebook/rocksdb/pull/11099) to this release to make disabling checksum verification work.
- Log an error with stacktrace when RPC responds with internal error [#5288](https://github.com/hyperledger/besu/pull/5288) - Log an error with stacktrace when RPC responds with internal error [#5288](https://github.com/hyperledger/besu/pull/5288)
- `--ethstats-cacert` to specify root CA of ethstats server (useful for non-production environments). [#5301](https://github.com/hyperledger/besu/pull/5301) - `--ethstats-cacert` to specify root CA of ethstats server (useful for non-production environments). [#5301](https://github.com/hyperledger/besu/pull/5301)

@ -24,7 +24,7 @@ import java.util.Optional;
import org.junit.Test; import org.junit.Test;
public class NodeSmartContractPermissioningIbftStallAcceptanceTest public class NodeSmartContractPermissioningIbft2StallAcceptanceTest
extends NodeSmartContractPermissioningAcceptanceTestBase { extends NodeSmartContractPermissioningAcceptanceTestBase {
private static final String GENESIS_FILE = private static final String GENESIS_FILE =

@ -35,7 +35,6 @@ dependencies {
implementation project(':consensus:clique') implementation project(':consensus:clique')
implementation project(':consensus:common') implementation project(':consensus:common')
implementation project(':consensus:ibft') implementation project(':consensus:ibft')
implementation project(':consensus:ibftlegacy')
implementation project(':consensus:merge') implementation project(':consensus:merge')
implementation project(':consensus:qbft') implementation project(':consensus:qbft')
implementation project(':crypto:services') implementation project(':crypto:services')

@ -325,7 +325,8 @@ public class BesuController implements java.io.Closeable {
} else if (configOptions.isIbft2()) { } else if (configOptions.isIbft2()) {
builder = new IbftBesuControllerBuilder(); builder = new IbftBesuControllerBuilder();
} else if (configOptions.isIbftLegacy()) { } else if (configOptions.isIbftLegacy()) {
builder = new IbftLegacyBesuControllerBuilder(); throw new IllegalStateException(
"IBFT1 (legacy) is no longer supported. Consider using IBFT2 or QBFT.");
} else if (configOptions.isQbft()) { } else if (configOptions.isQbft()) {
builder = new QbftBesuControllerBuilder(); builder = new QbftBesuControllerBuilder();
} else if (configOptions.isClique()) { } else if (configOptions.isClique()) {
@ -358,7 +359,8 @@ public class BesuController implements java.io.Closeable {
if (configOptions.isIbft2()) { if (configOptions.isIbft2()) {
originalControllerBuilder = new IbftBesuControllerBuilder(); originalControllerBuilder = new IbftBesuControllerBuilder();
} else if (configOptions.isIbftLegacy()) { } else if (configOptions.isIbftLegacy()) {
originalControllerBuilder = new IbftLegacyBesuControllerBuilder(); throw new IllegalStateException(
"IBFT1 (legacy) is no longer supported. Consider using IBFT2 or QBFT.");
} else { } else {
throw new IllegalStateException( throw new IllegalStateException(
"Invalid genesis migration config. Migration is supported from IBFT (legacy) or IBFT2 to QBFT)"); "Invalid genesis migration config. Migration is supported from IBFT (legacy) or IBFT2 to QBFT)");

@ -1,154 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.controller;
import org.hyperledger.besu.config.IbftLegacyConfigOptions;
import org.hyperledger.besu.consensus.common.BlockInterface;
import org.hyperledger.besu.consensus.common.EpochManager;
import org.hyperledger.besu.consensus.common.validator.ValidatorProvider;
import org.hyperledger.besu.consensus.common.validator.blockbased.BlockValidatorProvider;
import org.hyperledger.besu.consensus.ibft.IbftLegacyContext;
import org.hyperledger.besu.consensus.ibftlegacy.IbftLegacyBlockInterface;
import org.hyperledger.besu.consensus.ibftlegacy.IbftProtocolSchedule;
import org.hyperledger.besu.consensus.ibftlegacy.protocol.Istanbul99Protocol;
import org.hyperledger.besu.consensus.ibftlegacy.protocol.Istanbul99ProtocolManager;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.blockcreation.NoopMiningCoordinator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.MergePeerFilter;
import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** The Ibft legacy besu controller builder. */
public class IbftLegacyBesuControllerBuilder extends BesuControllerBuilder {
private static final Logger LOG = LoggerFactory.getLogger(IbftLegacyBesuControllerBuilder.class);
private final BlockInterface blockInterface = new IbftLegacyBlockInterface();
// TODO remove this warning once IBFT1 has been deprecated
/** Default constructor */
public IbftLegacyBesuControllerBuilder() {
LOG.warn(
"IBFT1 is being deprecated and will be removed in a future release. Consider using QBFT instead of IBFT1 or using GoQuorum instead of Besu if you need to use IBFT1");
}
@Override
protected SubProtocolConfiguration createSubProtocolConfiguration(
final EthProtocolManager ethProtocolManager,
final Optional<SnapProtocolManager> snapProtocolManage) {
return new SubProtocolConfiguration()
.withSubProtocol(Istanbul99Protocol.get(), ethProtocolManager);
}
@Override
protected MiningCoordinator createMiningCoordinator(
final ProtocolSchedule protocolSchedule,
final ProtocolContext protocolContext,
final TransactionPool transactionPool,
final MiningParameters miningParameters,
final SyncState syncState,
final EthProtocolManager ethProtocolManager) {
return new NoopMiningCoordinator(miningParameters);
}
@Override
protected ProtocolSchedule createProtocolSchedule() {
return IbftProtocolSchedule.create(
configOptionsSupplier.get(), privacyParameters, isRevertReasonEnabled, evmConfiguration);
}
@Override
protected IbftLegacyContext createConsensusContext(
final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final ProtocolSchedule protocolSchedule) {
final IbftLegacyConfigOptions ibftConfig =
configOptionsSupplier.get().getIbftLegacyConfigOptions();
final EpochManager epochManager = new EpochManager(ibftConfig.getEpochLength());
final ValidatorProvider validatorProvider =
BlockValidatorProvider.nonForkingValidatorProvider(
blockchain, epochManager, blockInterface);
return new IbftLegacyContext(validatorProvider, epochManager, blockInterface);
}
@Override
protected PluginServiceFactory createAdditionalPluginServices(
final Blockchain blockchain, final ProtocolContext protocolContext) {
return new NoopPluginServiceFactory();
}
@Override
protected void validateContext(final ProtocolContext context) {
final BlockHeader genesisBlockHeader = context.getBlockchain().getGenesisBlock().getHeader();
if (blockInterface.validatorsInBlock(genesisBlockHeader).isEmpty()) {
LOG.warn("Genesis block contains no signers - chain will not progress.");
}
}
@Override
protected String getSupportedProtocol() {
return Istanbul99Protocol.get().getName();
}
@Override
protected EthProtocolManager createEthProtocolManager(
final ProtocolContext protocolContext,
final SynchronizerConfiguration synchronizerConfiguration,
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthContext ethContext,
final EthMessages ethMessages,
final EthScheduler scheduler,
final List<PeerValidator> peerValidators,
final Optional<MergePeerFilter> mergePeerFilter) {
LOG.info("Operating on IBFT-1.0 network.");
return new Istanbul99ProtocolManager(
protocolContext.getBlockchain(),
networkId,
protocolContext.getWorldStateArchive(),
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators,
synchronizerConfiguration,
scheduler);
}
}

@ -14,7 +14,6 @@
*/ */
package org.hyperledger.besu.chainimport; package org.hyperledger.besu.chainimport;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
@ -37,12 +36,9 @@ import org.hyperledger.besu.testutil.TestClock;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import com.google.common.io.Resources;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -147,45 +143,4 @@ public final class RlpBlockImporterTest {
assertThat(result.count).isEqualTo(1); assertThat(result.count).isEqualTo(1);
assertThat(result.td).isEqualTo(UInt256.valueOf(34351349760L)); assertThat(result.td).isEqualTo(UInt256.valueOf(34351349760L));
} }
@Test
public void ibftImport() throws IOException {
final Path dataDir = folder.newFolder().toPath();
final Path source = dataDir.resolve("ibft.blocks");
final String config =
Resources.toString(this.getClass().getResource("/ibftlegacy_genesis.json"), UTF_8);
try {
Files.write(
source,
Resources.toByteArray(this.getClass().getResource("/ibft.blocks")),
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
} catch (final IOException ex) {
throw new IllegalStateException(ex);
}
final BesuController controller =
new BesuController.Builder()
.fromGenesisConfig(GenesisConfigFile.fromConfig(config), SyncMode.FULL)
.synchronizerConfiguration(SynchronizerConfiguration.builder().build())
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.storageProvider(new InMemoryKeyValueStorageProvider())
.networkId(BigInteger.valueOf(10))
.miningParameters(new MiningParameters.Builder().miningEnabled(false).build())
.nodeKey(NodeKeyUtils.generate())
.metricsSystem(new NoOpMetricsSystem())
.privacyParameters(PrivacyParameters.DEFAULT)
.dataDirectory(dataDir)
.clock(TestClock.fixed())
.transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT)
.gasLimitCalculator(GasLimitCalculator.constant())
.evmConfiguration(EvmConfiguration.DEFAULT)
.build();
final RlpBlockImporter.ImportResult result =
rlpBlockImporter.importBlockchain(source, controller, false);
// Don't count the Genesis block
assertThat(result.count).isEqualTo(958);
}
} }

@ -72,7 +72,7 @@ public class BesuControllerTest {
public void invalidConsensusCombination() { public void invalidConsensusCombination() {
when(genesisConfigFile.getConfigOptions(any())).thenReturn(genesisConfigOptions); when(genesisConfigFile.getConfigOptions(any())).thenReturn(genesisConfigOptions);
when(genesisConfigOptions.isConsensusMigration()).thenReturn(true); when(genesisConfigOptions.isConsensusMigration()).thenReturn(true);
// explicitly not setting isIbftLegacy() or isIbft2() for genesisConfigOptions // explicitly not setting isIbft2() for genesisConfigOptions
assertThatThrownBy( assertThatThrownBy(
() -> new BesuController.Builder().fromGenesisConfig(genesisConfigFile, SyncMode.FULL)) () -> new BesuController.Builder().fromGenesisConfig(genesisConfigFile, SyncMode.FULL))
@ -101,27 +101,6 @@ public class BesuControllerTest {
.isInstanceOf(QbftBesuControllerBuilder.class); .isInstanceOf(QbftBesuControllerBuilder.class);
} }
@Test
public void createConsensusScheduleBesuControllerBuilderWhenMigratingFromIbftLegacyToQbft() {
final long qbftStartBlock = 10L;
mockGenesisConfigForMigration("ibftLegacy", OptionalLong.of(qbftStartBlock));
final BesuControllerBuilder besuControllerBuilder =
new BesuController.Builder().fromGenesisConfig(genesisConfigFile, SyncMode.FULL);
assertThat(besuControllerBuilder).isInstanceOf(ConsensusScheduleBesuControllerBuilder.class);
final Map<Long, BesuControllerBuilder> besuControllerBuilderSchedule =
((ConsensusScheduleBesuControllerBuilder) besuControllerBuilder)
.getBesuControllerBuilderSchedule();
assertThat(besuControllerBuilderSchedule).containsKeys(0L, qbftStartBlock);
assertThat(besuControllerBuilderSchedule.get(0L))
.isInstanceOf(IbftLegacyBesuControllerBuilder.class);
assertThat(besuControllerBuilderSchedule.get(qbftStartBlock))
.isInstanceOf(QbftBesuControllerBuilder.class);
}
private void mockGenesisConfigForMigration( private void mockGenesisConfigForMigration(
final String consensus, final OptionalLong startBlock) { final String consensus, final OptionalLong startBlock) {
when(genesisConfigFile.getConfigOptions(any())).thenReturn(genesisConfigOptions); when(genesisConfigFile.getConfigOptions(any())).thenReturn(genesisConfigOptions);
@ -133,11 +112,6 @@ public class BesuControllerTest {
when(genesisConfigOptions.isIbft2()).thenReturn(true); when(genesisConfigOptions.isIbft2()).thenReturn(true);
break; break;
} }
case "ibftlegacy":
{
when(genesisConfigOptions.isIbftLegacy()).thenReturn(true);
break;
}
default: default:
fail("Invalid consensus algorithm"); fail("Invalid consensus algorithm");
} }

@ -1,38 +0,0 @@
{
"config": {
"chainId": 2017,
"homesteadBlock": 0,
"eip150Block": 0,
"eip158Block": 0,
"ibft": {
"epochLength": 30000,
"blockPeriodSeconds" : 1,
"requestTimeoutSeconds": 10
}
},
"nonce": "0x0",
"timestamp": "0x5b3c3d18",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000f89af85494c332d0db1704d18f89a590e7586811e36d37ce049424defc2d149861d3d245749b81fe0e6b28e04f31943814f17bd4b7ce47ab8146684b3443c0a4b2fc2c942a813d7db3de19b07f92268b6d4125ed295cbe00b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"24defc2d149861d3d245749b81fe0e6b28e04f31": {
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
},
"2a813d7db3de19b07f92268b6d4125ed295cbe00": {
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
},
"3814f17bd4b7ce47ab8146684b3443c0a4b2fc2c": {
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
},
"c332d0db1704d18f89a590e7586811e36d37ce04": {
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

@ -44,7 +44,7 @@ public interface GenesisConfigOptions {
boolean isKeccak256(); boolean isKeccak256();
/** /**
* Is ibft legacy boolean. * Is ibft legacy boolean (NOTE this is a deprecated feature).
* *
* @return the boolean * @return the boolean
*/ */
@ -77,7 +77,7 @@ public interface GenesisConfigOptions {
* @return the boolean * @return the boolean
*/ */
default boolean isConsensusMigration() { default boolean isConsensusMigration() {
return (isIbft2() || isIbftLegacy()) && isQbft(); return isIbft2() && isQbft();
} }
/** /**
@ -87,13 +87,6 @@ public interface GenesisConfigOptions {
*/ */
String getConsensusEngine(); String getConsensusEngine();
/**
* Gets ibft legacy config options.
*
* @return the ibft legacy config options
*/
IbftLegacyConfigOptions getIbftLegacyConfigOptions();
/** /**
* Gets checkpoint options. * Gets checkpoint options.
* *

@ -1,104 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.config;
import java.util.Map;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableMap;
/** The Ibft legacy config options. */
public class IbftLegacyConfigOptions {
/** The constant DEFAULT. */
public static final IbftLegacyConfigOptions DEFAULT =
new IbftLegacyConfigOptions(JsonUtil.createEmptyObjectNode());
private static final long DEFAULT_EPOCH_LENGTH = 30_000;
private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 1;
private static final int DEFAULT_ROUND_EXPIRY_SECONDS = 1;
private static final long DEFAULT_CEIL_2N_BY_3_BLOCK = 0L;
private final ObjectNode ibftConfigRoot;
/**
* Instantiates a new Ibft legacy config options.
*
* @param ibftConfigRoot the ibft config root
*/
IbftLegacyConfigOptions(final ObjectNode ibftConfigRoot) {
this.ibftConfigRoot = ibftConfigRoot;
}
/**
* Gets epoch length.
*
* @return the epoch length
*/
public long getEpochLength() {
return JsonUtil.getLong(ibftConfigRoot, "epochlength", DEFAULT_EPOCH_LENGTH);
}
/**
* Gets block period seconds.
*
* @return the block period seconds
*/
public int getBlockPeriodSeconds() {
return JsonUtil.getPositiveInt(
ibftConfigRoot, "blockperiodseconds", DEFAULT_BLOCK_PERIOD_SECONDS);
}
/**
* Gets request timeout seconds.
*
* @return the request timeout seconds
*/
public int getRequestTimeoutSeconds() {
return JsonUtil.getInt(ibftConfigRoot, "requesttimeoutseconds", DEFAULT_ROUND_EXPIRY_SECONDS);
}
/**
* Gets ceil 2N by 3 block.
*
* @return the ceil 2N by 3 block
*/
public long getCeil2Nby3Block() {
return JsonUtil.getLong(ibftConfigRoot, "ceil2nby3block", DEFAULT_CEIL_2N_BY_3_BLOCK);
}
/**
* As map.
*
* @return the map
*/
Map<String, Object> asMap() {
final ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
if (ibftConfigRoot.has("epochlength")) {
builder.put("epochLength", getEpochLength());
}
if (ibftConfigRoot.has("blockperiodseconds")) {
builder.put("blockPeriodSeconds", getBlockPeriodSeconds());
}
if (ibftConfigRoot.has("requesttimeoutseconds")) {
builder.put("requestTimeoutSeconds", getRequestTimeoutSeconds());
}
if (ibftConfigRoot.has("ceil2nby3block")) {
builder.put("ceil2nby3block", getCeil2Nby3Block());
}
return builder.build();
}
}

@ -113,8 +113,6 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
return KECCAK256_CONFIG_KEY; return KECCAK256_CONFIG_KEY;
} else if (isIbft2()) { } else if (isIbft2()) {
return IBFT2_CONFIG_KEY; return IBFT2_CONFIG_KEY;
} else if (isIbftLegacy()) {
return IBFT_LEGACY_CONFIG_KEY;
} else if (isQbft()) { } else if (isQbft()) {
return QBFT_CONFIG_KEY; return QBFT_CONFIG_KEY;
} else if (isClique()) { } else if (isClique()) {
@ -154,13 +152,6 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
return configRoot.has(QBFT_CONFIG_KEY); return configRoot.has(QBFT_CONFIG_KEY);
} }
@Override
public IbftLegacyConfigOptions getIbftLegacyConfigOptions() {
return JsonUtil.getObjectNode(configRoot, IBFT_LEGACY_CONFIG_KEY)
.map(IbftLegacyConfigOptions::new)
.orElse(IbftLegacyConfigOptions.DEFAULT);
}
@Override @Override
public BftConfigOptions getBftConfigOptions() { public BftConfigOptions getBftConfigOptions() {
final String fieldKey = isIbft2() ? IBFT2_CONFIG_KEY : QBFT_CONFIG_KEY; final String fieldKey = isIbft2() ? IBFT2_CONFIG_KEY : QBFT_CONFIG_KEY;
@ -503,9 +494,6 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
if (isKeccak256()) { if (isKeccak256()) {
builder.put("keccak256", getKeccak256ConfigOptions().asMap()); builder.put("keccak256", getKeccak256ConfigOptions().asMap());
} }
if (isIbftLegacy()) {
builder.put("ibft", getIbftLegacyConfigOptions().asMap());
}
if (isIbft2()) { if (isIbft2()) {
builder.put("ibft2", getBftConfigOptions().asMap()); builder.put("ibft2", getBftConfigOptions().asMap());
} }

@ -121,11 +121,6 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
return false; return false;
} }
@Override
public IbftLegacyConfigOptions getIbftLegacyConfigOptions() {
return IbftLegacyConfigOptions.DEFAULT;
}
@Override @Override
public CheckpointConfigOptions getCheckpointOptions() { public CheckpointConfigOptions getCheckpointOptions() {
return CheckpointConfigOptions.DEFAULT; return CheckpointConfigOptions.DEFAULT;
@ -402,9 +397,6 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
if (isKeccak256()) { if (isKeccak256()) {
builder.put("keccak256", getKeccak256ConfigOptions().asMap()); builder.put("keccak256", getKeccak256ConfigOptions().asMap());
} }
if (isIbftLegacy()) {
builder.put("ibft", getIbftLegacyConfigOptions().asMap());
}
if (isIbft2()) { if (isIbft2()) {
builder.put("ibft2", getBftConfigOptions().asMap()); builder.put("ibft2", getBftConfigOptions().asMap());
} }

@ -55,25 +55,9 @@ public class GenesisConfigOptionsTest {
assertThat(config.isKeccak256()).isFalse(); assertThat(config.isKeccak256()).isFalse();
} }
@Test
public void shouldUseIbftLegacyWhenIbftInConfig() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("ibft", emptyMap()));
assertThat(config.isIbftLegacy()).isTrue();
assertThat(config.getIbftLegacyConfigOptions()).isNotSameAs(IbftLegacyConfigOptions.DEFAULT);
assertThat(config.getConsensusEngine()).isEqualTo("ibft");
}
@Test
public void shouldNotUseIbftLegacyIfIbftNotPresent() {
final GenesisConfigOptions config = fromConfigOptions(emptyMap());
assertThat(config.isIbftLegacy()).isFalse();
assertThat(config.getIbftLegacyConfigOptions()).isSameAs(IbftLegacyConfigOptions.DEFAULT);
}
@Test @Test
public void shouldUseIbft2WhenIbft2InConfig() { public void shouldUseIbft2WhenIbft2InConfig() {
final GenesisConfigOptions config = fromConfigOptions(singletonMap("ibft2", emptyMap())); final GenesisConfigOptions config = fromConfigOptions(singletonMap("ibft2", emptyMap()));
assertThat(config.isIbftLegacy()).isFalse();
assertThat(config.isIbft2()).isTrue(); assertThat(config.isIbft2()).isTrue();
assertThat(config.getConsensusEngine()).isEqualTo("ibft2"); assertThat(config.getConsensusEngine()).isEqualTo("ibft2");
} }
@ -264,7 +248,6 @@ public class GenesisConfigOptionsTest {
public void shouldSupportEmptyGenesisConfig() { public void shouldSupportEmptyGenesisConfig() {
final GenesisConfigOptions config = GenesisConfigFile.fromConfig("{}").getConfigOptions(); final GenesisConfigOptions config = GenesisConfigFile.fromConfig("{}").getConfigOptions();
assertThat(config.isEthHash()).isFalse(); assertThat(config.isEthHash()).isFalse();
assertThat(config.isIbftLegacy()).isFalse();
assertThat(config.isClique()).isFalse(); assertThat(config.isClique()).isFalse();
assertThat(config.getHomesteadBlockNumber()).isEmpty(); assertThat(config.getHomesteadBlockNumber()).isEmpty();
} }

@ -233,28 +233,6 @@ public class JsonGenesisConfigOptionsTest {
assertThat(configOptions.isConsensusMigration()).isTrue(); assertThat(configOptions.isConsensusMigration()).isTrue();
} }
@Test
public void configWithMigrationFromIbftLegacyToQbft() {
final ObjectNode configNode = loadConfigWithMigrationFromIbftLegacyToQbft();
final JsonGenesisConfigOptions configOptions =
JsonGenesisConfigOptions.fromJsonObject(configNode);
assertThat(configOptions.isIbftLegacy()).isTrue();
assertThat(configOptions.isQbft()).isTrue();
assertThat(configOptions.isConsensusMigration()).isTrue();
}
@Test
public void configWithoutMigration() {
final ObjectNode configNode = loadCompleteDataSet();
final JsonGenesisConfigOptions configOptions =
JsonGenesisConfigOptions.fromJsonObject(configNode);
assertThat(configOptions.isIbftLegacy()).isFalse();
}
private ObjectNode loadConfigWithMigrationFromIbft2ToQbft() { private ObjectNode loadConfigWithMigrationFromIbft2ToQbft() {
try { try {
final String configText = final String configText =
@ -266,12 +244,4 @@ public class JsonGenesisConfigOptionsTest {
throw new RuntimeException("Failed to load resource", e); throw new RuntimeException("Failed to load resource", e);
} }
} }
private ObjectNode loadConfigWithMigrationFromIbftLegacyToQbft() {
ObjectNode configNode = loadConfigWithMigrationFromIbft2ToQbft();
JsonNode consensusConfig = configNode.get("ibft2");
configNode.remove("ibft2");
configNode.set("ibft", consensusConfig);
return configNode;
}
} }

@ -1,51 +0,0 @@
apply plugin: 'java-library'
jar {
archiveBaseName = 'besu-ibftlegacy'
manifest {
attributes(
'Specification-Title': archiveBaseName,
'Specification-Version': project.version,
'Implementation-Title': archiveBaseName,
'Implementation-Version': calculateVersion()
)
}
}
dependencies {
implementation project(':config')
implementation project(':consensus:common')
implementation project(':consensus:ibft')
implementation project(':crypto:algorithms')
implementation project(':datatypes')
implementation project(':ethereum:api')
implementation project(':ethereum:blockcreation')
implementation project(':ethereum:core')
implementation project(':ethereum:eth')
implementation project(':ethereum:p2p')
implementation project(':ethereum:rlp')
implementation project(':evm')
implementation project(':metrics:core')
implementation project(':services:kvstore')
implementation 'com.google.guava:guava'
implementation 'io.vertx:vertx-core'
implementation 'com.fasterxml.jackson.core:jackson-databind'
implementation 'org.apache.tuweni:tuweni-bytes'
implementation 'org.apache.tuweni:tuweni-units'
testImplementation project(path: ':consensus:common', configuration: 'testSupportArtifacts')
testImplementation project(path: ':consensus:ibft', configuration: 'testSupportArtifacts')
testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')
testImplementation project(path: ':ethereum:eth', configuration: 'testSupportArtifacts')
testImplementation project(':metrics:core')
testImplementation project(':testutil')
testImplementation 'junit:junit'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.awaitility:awaitility'
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.mockito:mockito-core'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'
}

@ -1,169 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.apache.tuweni.bytes.Bytes;
/** The Ibft block hashing. */
public class IbftBlockHashing {
private static final Bytes COMMIT_MSG_CODE = Bytes.wrap(new byte[] {2});
/**
* Constructs a hash of the block header, suitable for use when creating the proposer seal. The
* extra data is modified to have a null proposer seal and empty list of committed seals.
*
* @param header The header for which a proposer seal is to be calculated
* @param ibftExtraData The extra data block which is to be inserted to the header once seal is
* calculated
* @return the hash of the header suitable for signing as the proposer seal
*/
public static Hash calculateDataHashForProposerSeal(
final BlockHeader header, final IbftExtraData ibftExtraData) {
final Bytes headerRlp =
serializeHeader(header, () -> encodeExtraDataWithoutCommittedSeals(ibftExtraData, null));
// Proposer hash is the hash of the hash
return Hash.hash(Hash.hash(headerRlp));
}
/**
* Constructs a hash of the block header suitable for signing as a committed seal. The extra data
* in the hash uses an empty list for the committed seals.
*
* @param header The header for which a proposer seal is to be calculated (without extra data)
* @param ibftExtraData The extra data block which is to be inserted to the header once seal is
* calculated
* @return the hash of the header including the validator and proposer seal in the extra data
*/
public static Hash calculateDataHashForCommittedSeal(
final BlockHeader header, final IbftExtraData ibftExtraData) {
// The data signed by a committer is an array of [Hash, COMMIT_MSG_CODE]
final Hash dataHash = Hash.hash(serializeHeaderWithoutCommittedSeals(header, ibftExtraData));
final Bytes seal = Bytes.wrap(dataHash, COMMIT_MSG_CODE);
return Hash.hash(seal);
}
/**
* Constructs a hash of the block header, but omits the committerSeals (as this changes on each of
* the potentially circulated blocks at the current chain height).
*
* @param header The header for which a block hash is to be calculated
* @return the hash of the header including the validator and proposer seal in the extra data
*/
public static Hash calculateHashOfIbftBlockOnchain(final BlockHeader header) {
final IbftExtraData ibftExtraData = IbftExtraData.decode(header);
return Hash.hash(serializeHeaderWithoutCommittedSeals(header, ibftExtraData));
}
private static Bytes serializeHeaderWithoutCommittedSeals(
final BlockHeader header, final IbftExtraData ibftExtraData) {
return serializeHeader(
header,
() -> encodeExtraDataWithoutCommittedSeals(ibftExtraData, ibftExtraData.getProposerSeal()));
}
/**
* Recovers the proposer's {@link Address} from the proposer seal.
*
* @param header the block header that was signed by the proposer seal
* @param ibftExtraData the parsed IBftExtraData from the header
* @return the proposer address
*/
public static Address recoverProposerAddress(
final BlockHeader header, final IbftExtraData ibftExtraData) {
final Hash proposerHash = calculateDataHashForProposerSeal(header, ibftExtraData);
return Util.signatureToAddress(ibftExtraData.getProposerSeal(), proposerHash);
}
/**
* Recovers the {@link Address} for each validator that contributed a committed seal to the block.
*
* @param header the block header that was signed by the committed seals
* @param ibftExtraData the parsed IBftExtraData from the header
* @return the addresses of validators that provided a committed seal
*/
public static List<Address> recoverCommitterAddresses(
final BlockHeader header, final IbftExtraData ibftExtraData) {
final Hash committerHash =
IbftBlockHashing.calculateDataHashForCommittedSeal(header, ibftExtraData);
return ibftExtraData.getSeals().stream()
.map(p -> Util.signatureToAddress(p, committerHash))
.collect(Collectors.toList());
}
private static Bytes encodeExtraDataWithoutCommittedSeals(
final IbftExtraData ibftExtraData, final SECPSignature proposerSeal) {
final BytesValueRLPOutput extraDataEncoding = new BytesValueRLPOutput();
extraDataEncoding.startList();
extraDataEncoding.writeList(
ibftExtraData.getValidators(), (validator, rlp) -> rlp.writeBytes(validator));
if (proposerSeal != null) {
extraDataEncoding.writeBytes(proposerSeal.encodedBytes());
} else {
extraDataEncoding.writeNull();
}
// Represents an empty committer list (i.e this is not included in the hashing of the block)
extraDataEncoding.startList();
extraDataEncoding.endList();
extraDataEncoding.endList();
return Bytes.wrap(ibftExtraData.getVanityData(), extraDataEncoding.encoded());
}
private static Bytes serializeHeader(
final BlockHeader header, final Supplier<Bytes> extraDataSerializer) {
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeBytes(header.getParentHash());
out.writeBytes(header.getOmmersHash());
out.writeBytes(header.getCoinbase());
out.writeBytes(header.getStateRoot());
out.writeBytes(header.getTransactionsRoot());
out.writeBytes(header.getReceiptsRoot());
out.writeBytes(header.getLogsBloom());
out.writeBytes(header.getDifficulty().toMinimalBytes());
out.writeLongScalar(header.getNumber());
out.writeLongScalar(header.getGasLimit());
out.writeLongScalar(header.getGasUsed());
out.writeLongScalar(header.getTimestamp());
// Cannot decode an IbftExtraData on block 0 due to missing/illegal signatures
if (header.getNumber() == 0) {
out.writeBytes(header.getExtraData());
} else {
out.writeBytes(extraDataSerializer.get());
}
out.writeBytes(header.getMixHash());
out.writeLong(header.getNonce());
out.endList();
return out.encoded();
}
}

@ -1,85 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import static org.hyperledger.besu.ethereum.mainnet.AbstractGasLimitSpecification.DEFAULT_MAX_GAS_LIMIT;
import static org.hyperledger.besu.ethereum.mainnet.AbstractGasLimitSpecification.DEFAULT_MIN_GAS_LIMIT;
import org.hyperledger.besu.consensus.ibftlegacy.headervalidationrules.IbftExtraDataValidationRule;
import org.hyperledger.besu.consensus.ibftlegacy.headervalidationrules.VoteValidationRule;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.AncestryValidationRule;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.ConstantFieldValidationRule;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasLimitRangeAndDeltaValidationRule;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.GasUsageValidationRule;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampBoundedByFutureParameter;
import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent;
import org.apache.tuweni.units.bigints.UInt256;
/** The Ibft block header validation ruleset factory. */
public class IbftBlockHeaderValidationRulesetFactory {
/**
* Produces a BlockHeaderValidator configured for assessing ibft block headers which are to form
* part of the BlockChain (i.e. not proposed blocks, which do not contain commit seals)
*
* @param secondsBetweenBlocks the minimum number of seconds which must elapse between blocks.
* @param ceil2nBy3Block the block after which 2/3n commit seals must exist, rather than 2F+1
* @return BlockHeaderValidator configured for assessing ibft block headers
*/
public static BlockHeaderValidator.Builder ibftBlockHeaderValidator(
final long secondsBetweenBlocks, final long ceil2nBy3Block) {
return createValidator(secondsBetweenBlocks, true, ceil2nBy3Block);
}
/**
* Produces a BlockHeaderValidator configured for assessing IBFT proposed blocks (i.e. blocks
* which need to be vetted by the validators, and do not contain commit seals).
*
* @param secondsBetweenBlocks the minimum number of seconds which must elapse between blocks.
* @return BlockHeaderValidator configured for assessing ibft block headers
*/
public static BlockHeaderValidator.Builder ibftProposedBlockValidator(
final long secondsBetweenBlocks) {
return createValidator(secondsBetweenBlocks, false, 0);
}
private static BlockHeaderValidator.Builder createValidator(
final long secondsBetweenBlocks,
final boolean validateCommitSeals,
final long ceil2nBy3Block) {
return new BlockHeaderValidator.Builder()
.addRule(new AncestryValidationRule())
.addRule(new GasUsageValidationRule())
.addRule(
new GasLimitRangeAndDeltaValidationRule(DEFAULT_MIN_GAS_LIMIT, DEFAULT_MAX_GAS_LIMIT))
.addRule(new TimestampBoundedByFutureParameter(1))
.addRule(new TimestampMoreRecentThanParent(secondsBetweenBlocks))
.addRule(
new ConstantFieldValidationRule<>(
"MixHash", BlockHeader::getMixHash, IbftHelpers.EXPECTED_MIX_HASH))
.addRule(
new ConstantFieldValidationRule<>(
"OmmersHash", BlockHeader::getOmmersHash, Hash.EMPTY_LIST_HASH))
.addRule(
new ConstantFieldValidationRule<>(
"Difficulty", BlockHeader::getDifficulty, UInt256.ONE))
.addRule(new VoteValidationRule())
.addRule(new IbftExtraDataValidationRule(validateCommitSeals, ceil2nBy3Block));
}
}

@ -1,184 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.ParsedExtraData;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.util.Collection;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents the data structure stored in the extraData field of the BlockHeader used when
* operating under an IBFT consensus mechanism.
*/
public class IbftExtraData implements ParsedExtraData {
private static final Logger LOG = LoggerFactory.getLogger(IbftExtraData.class);
/** The constant EXTRA_VANITY_LENGTH. */
public static final int EXTRA_VANITY_LENGTH = 32;
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private final Bytes vanityData;
private final Collection<SECPSignature> seals;
private final SECPSignature proposerSeal;
private final Collection<Address> validators;
/**
* Instantiates a new Ibft extra data.
*
* @param vanityData the vanity data
* @param seals the seals
* @param proposerSeal the proposer seal
* @param validators the validators
*/
public IbftExtraData(
final Bytes vanityData,
final Collection<SECPSignature> seals,
final SECPSignature proposerSeal,
final Collection<Address> validators) {
checkNotNull(vanityData);
checkNotNull(seals);
checkNotNull(validators);
this.vanityData = vanityData;
this.seals = seals;
this.proposerSeal = proposerSeal;
this.validators = validators;
}
/**
* Decode header and return ibft extra data.
*
* @param header the header
* @return the ibft extra data
*/
public static IbftExtraData decode(final BlockHeader header) {
final Object inputExtraData = header.getParsedExtraData();
if (inputExtraData instanceof IbftExtraData) {
return (IbftExtraData) inputExtraData;
}
LOG.warn(
"Expected a IbftExtraData instance but got {}. Reparsing required.",
inputExtraData != null ? inputExtraData.getClass().getName() : "null");
return decodeRaw(header.getExtraData());
}
/**
* Decode raw input and return ibft extra data.
*
* @param input the input
* @return the ibft extra data
*/
static IbftExtraData decodeRaw(final Bytes input) {
checkArgument(
input.size() > EXTRA_VANITY_LENGTH,
"Invalid Bytes supplied - too short to produce a valid IBFT Extra Data object.");
final Bytes vanityData = input.slice(0, EXTRA_VANITY_LENGTH);
final Bytes rlpData = input.slice(EXTRA_VANITY_LENGTH);
final RLPInput rlpInput = new BytesValueRLPInput(rlpData, false);
rlpInput.enterList(); // This accounts for the "root node" which contains IBFT data items.
final Collection<Address> validators = rlpInput.readList(Address::readFrom);
final SECPSignature proposerSeal = parseProposerSeal(rlpInput);
final Collection<SECPSignature> seals =
rlpInput.readList(rlp -> SIGNATURE_ALGORITHM.get().decodeSignature(rlp.readBytes()));
rlpInput.leaveList();
return new IbftExtraData(vanityData, seals, proposerSeal, validators);
}
private static SECPSignature parseProposerSeal(final RLPInput rlpInput) {
final Bytes data = rlpInput.readBytes();
return data.isZero() ? null : SIGNATURE_ALGORITHM.get().decodeSignature(data);
}
/**
* Encode extra data to bytes.
*
* @return the bytes
*/
public Bytes encode() {
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
encoder.startList();
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytes(validator));
if (proposerSeal != null) {
encoder.writeBytes(proposerSeal.encodedBytes());
} else {
encoder.writeNull();
}
encoder.writeList(seals, (committer, rlp) -> rlp.writeBytes(committer.encodedBytes()));
encoder.endList();
return Bytes.wrap(vanityData, encoder.encoded());
}
/**
* Gets vanity data.
*
* @return the vanity data
*/
// Accessors
public Bytes getVanityData() {
return vanityData;
}
/**
* Gets seals.
*
* @return the seals
*/
public Collection<SECPSignature> getSeals() {
return seals;
}
/**
* Gets proposer seal.
*
* @return the proposer seal
*/
public SECPSignature getProposerSeal() {
return proposerSeal;
}
/**
* Gets validators.
*
* @return the validators
*/
public Collection<Address> getValidators() {
return validators;
}
}

@ -1,36 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import org.hyperledger.besu.datatypes.Hash;
/** The Ibft helpers utility class. */
public class IbftHelpers {
/** The constant EXPECTED_MIX_HASH. */
public static final Hash EXPECTED_MIX_HASH =
Hash.fromHexString("0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365");
/**
* Calculate required validator quorum.
*
* @param validatorCount the validator count
* @return the int
*/
public static int calculateRequiredValidatorQuorum(final int validatorCount) {
final int F = (validatorCount - 1) / 3;
return (2 * F) + 1;
}
}

@ -1,105 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import org.hyperledger.besu.consensus.common.BlockInterface;
import org.hyperledger.besu.consensus.common.validator.ValidatorVote;
import org.hyperledger.besu.consensus.common.validator.VoteType;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import java.util.Collection;
import java.util.Optional;
import com.google.common.collect.ImmutableBiMap;
import org.apache.tuweni.bytes.Bytes;
/** The Ibft legacy block interface. */
public class IbftLegacyBlockInterface implements BlockInterface {
/** The constant NO_VOTE_SUBJECT. */
public static final Address NO_VOTE_SUBJECT = Address.wrap(Bytes.wrap(new byte[Address.SIZE]));
/** The constant ADD_NONCE. */
public static final long ADD_NONCE = 0xFFFFFFFFFFFFFFFFL;
/** The constant DROP_NONCE. */
public static final long DROP_NONCE = 0x0L;
private static final ImmutableBiMap<VoteType, Long> voteToValue =
ImmutableBiMap.of(
VoteType.ADD, ADD_NONCE,
VoteType.DROP, DROP_NONCE);
@Override
public Address getProposerOfBlock(final BlockHeader header) {
final IbftExtraData ibftExtraData = IbftExtraData.decode(header);
return IbftBlockHashing.recoverProposerAddress(header, ibftExtraData);
}
@Override
public Address getProposerOfBlock(final org.hyperledger.besu.plugin.data.BlockHeader header) {
return getProposerOfBlock(
BlockHeader.convertPluginBlockHeader(header, new LegacyIbftBlockHeaderFunctions()));
}
@Override
public Optional<ValidatorVote> extractVoteFromHeader(final BlockHeader header) {
final Address candidate = header.getCoinbase();
if (!candidate.equals(NO_VOTE_SUBJECT)) {
final Address proposer = getProposerOfBlock(header);
final VoteType votePolarity = voteToValue.inverse().get(header.getNonce());
final Address recipient = header.getCoinbase();
return Optional.of(new ValidatorVote(votePolarity, proposer, recipient));
}
return Optional.empty();
}
/**
* Insert vote to header builder and return block header builder.
*
* @param builder the builder
* @param vote the vote
* @return the block header builder
*/
public static BlockHeaderBuilder insertVoteToHeaderBuilder(
final BlockHeaderBuilder builder, final Optional<ValidatorVote> vote) {
if (vote.isPresent()) {
final ValidatorVote voteToCast = vote.get();
builder.nonce(voteToValue.get(voteToCast.getVotePolarity()));
builder.coinbase(voteToCast.getRecipient());
} else {
builder.nonce(voteToValue.get(VoteType.DROP));
builder.coinbase(NO_VOTE_SUBJECT);
}
return builder;
}
@Override
public Collection<Address> validatorsInBlock(final BlockHeader header) {
return IbftExtraData.decode(header).getValidators();
}
/**
* Is valid vote value.
*
* @param value the value
* @return the boolean
*/
public static boolean isValidVoteValue(final long value) {
return voteToValue.values().contains(value);
}
}

@ -1,104 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import static org.hyperledger.besu.consensus.ibftlegacy.IbftBlockHeaderValidationRulesetFactory.ibftBlockHeaderValidator;
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.IbftLegacyConfigOptions;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockImporter;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolScheduleBuilder;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecAdapters;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpecBuilder;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import java.math.BigInteger;
/** Defines the protocol behaviours for a blockchain using IBFT. */
public class IbftProtocolSchedule {
private static final BigInteger DEFAULT_CHAIN_ID = BigInteger.ONE;
/**
* Create protocol schedule.
*
* @param config the config
* @param privacyParameters the privacy parameters
* @param isRevertReasonEnabled the is revert reason enabled
* @param evmConfiguration the evm configuration
* @return the protocol schedule
*/
public static ProtocolSchedule create(
final GenesisConfigOptions config,
final PrivacyParameters privacyParameters,
final boolean isRevertReasonEnabled,
final EvmConfiguration evmConfiguration) {
final IbftLegacyConfigOptions ibftConfig = config.getIbftLegacyConfigOptions();
final long blockPeriod = ibftConfig.getBlockPeriodSeconds();
return new ProtocolScheduleBuilder(
config,
DEFAULT_CHAIN_ID,
ProtocolSpecAdapters.create(
0,
builder ->
applyIbftChanges(
blockPeriod, builder, config.isQuorum(), ibftConfig.getCeil2Nby3Block())),
privacyParameters,
isRevertReasonEnabled,
config.isQuorum(),
evmConfiguration)
.createProtocolSchedule();
}
/**
* Create protocol schedule.
*
* @param config the config
* @param isRevertReasonEnabled the is revert reason enabled
* @param evmConfiguration the evm configuration
* @return the protocol schedule
*/
public static ProtocolSchedule create(
final GenesisConfigOptions config,
final boolean isRevertReasonEnabled,
final EvmConfiguration evmConfiguration) {
return create(config, PrivacyParameters.DEFAULT, isRevertReasonEnabled, evmConfiguration);
}
private static ProtocolSpecBuilder applyIbftChanges(
final long secondsBetweenBlocks,
final ProtocolSpecBuilder builder,
final boolean goQuorumMode,
final long ceil2nBy3Block) {
return builder
.blockHeaderValidatorBuilder(
feeMarket -> ibftBlockHeaderValidator(secondsBetweenBlocks, ceil2nBy3Block))
.ommerHeaderValidatorBuilder(
feeMarket -> ibftBlockHeaderValidator(secondsBetweenBlocks, ceil2nBy3Block))
.blockBodyValidatorBuilder(MainnetBlockBodyValidator::new)
.blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder(goQuorumMode))
.blockImporterBuilder(MainnetBlockImporter::new)
.difficultyCalculator((time, parent, protocolContext) -> BigInteger.ONE)
.blockReward(Wei.ZERO)
.skipZeroBlockRewards(true)
.blockHeaderFunctions(new LegacyIbftBlockHeaderFunctions());
}
}

@ -1,38 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
/** The Legacy ibft block header functions. */
public class LegacyIbftBlockHeaderFunctions implements BlockHeaderFunctions {
@Override
public Hash hash(final BlockHeader header) {
return IbftBlockHashing.calculateHashOfIbftBlockOnchain(header);
}
@Override
public IbftExtraData parseExtraData(final BlockHeader header) {
return IbftExtraData.decodeRaw(header.getExtraData());
}
@Override
public int getCheckPointWindowSize(final BlockHeader header) {
return IbftExtraData.decodeRaw(header.getExtraData()).getValidators().size();
}
}

@ -1,132 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy.blockcreation;
import org.hyperledger.besu.consensus.ibftlegacy.IbftBlockHashing;
import org.hyperledger.besu.consensus.ibftlegacy.IbftExtraData;
import org.hyperledger.besu.consensus.ibftlegacy.IbftHelpers;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.AbstractBlockCreator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
import org.hyperledger.besu.ethereum.core.SealableBlockHeader;
import org.hyperledger.besu.ethereum.core.Util;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import java.util.Optional;
import java.util.function.Supplier;
/**
* Responsible for producing a Block which conforms to IBFT validation rules (other than missing
* commit seals). Transactions and associated Hashes (stateroot, receipts etc.) are loaded into the
* Block in the base class as part of the transaction selection process.
*/
public class IbftBlockCreator extends AbstractBlockCreator {
private final KeyPair nodeKeys;
/**
* Instantiates a new Ibft block creator.
*
* @param coinbase the coinbase
* @param targetGasLimitSupplier the target gas limit supplier
* @param extraDataCalculator the extra data calculator
* @param pendingTransactions the pending transactions
* @param protocolContext the protocol context
* @param protocolSchedule the protocol schedule
* @param nodeKeys the node keys
* @param minTransactionGasPrice the min transaction gas price
* @param minBlockOccupancyRatio the min block occupancy ratio
* @param parentHeader the parent header
*/
public IbftBlockCreator(
final Address coinbase,
final Supplier<Optional<Long>> targetGasLimitSupplier,
final ExtraDataCalculator extraDataCalculator,
final PendingTransactions pendingTransactions,
final ProtocolContext protocolContext,
final ProtocolSchedule protocolSchedule,
final KeyPair nodeKeys,
final Wei minTransactionGasPrice,
final Double minBlockOccupancyRatio,
final BlockHeader parentHeader) {
super(
coinbase,
__ -> Util.publicKeyToAddress(nodeKeys.getPublicKey()),
targetGasLimitSupplier,
extraDataCalculator,
pendingTransactions,
protocolContext,
protocolSchedule,
minTransactionGasPrice,
minBlockOccupancyRatio,
parentHeader);
this.nodeKeys = nodeKeys;
}
/**
* Responsible for signing (hash of) the block (including MixHash and Nonce), and then injecting
* the seal into the extraData. This is called after a suitable set of transactions have been
* identified, and all resulting hashes have been inserted into the passed-in SealableBlockHeader.
*
* @param sealableBlockHeader A block header containing StateRoots, TransactionHashes etc.
* @return The blockhead which is to be added to the block being proposed.
*/
@Override
protected BlockHeader createFinalBlockHeader(final SealableBlockHeader sealableBlockHeader) {
final BlockHeaderFunctions blockHeaderFunctions =
ScheduleBasedBlockHeaderFunctions.create(protocolSchedule);
final BlockHeaderBuilder builder =
BlockHeaderBuilder.create()
.populateFrom(sealableBlockHeader)
.mixHash(IbftHelpers.EXPECTED_MIX_HASH)
.nonce(0)
.blockHeaderFunctions(blockHeaderFunctions);
final IbftExtraData sealedExtraData = constructSignedExtraData(builder.buildBlockHeader());
// Replace the extraData in the BlockHeaderBuilder, and return header.
return builder.extraData(sealedExtraData.encode()).buildBlockHeader();
}
/**
* Produces an IbftExtraData object with a populated proposerSeal. The signature in the block is
* generated from the Hash of the header (minus proposer and committer seals) and the nodeKeys.
*
* @param headerToSign An almost fully populated header (proposer and committer seals are empty)
* @return Extra data containing the same vanity data and validators as extraData, however
* proposerSeal will also be populated.
*/
private IbftExtraData constructSignedExtraData(final BlockHeader headerToSign) {
final IbftExtraData extraData = IbftExtraData.decode(headerToSign);
final Hash hashToSign =
IbftBlockHashing.calculateDataHashForProposerSeal(headerToSign, extraData);
return new IbftExtraData(
extraData.getVanityData(),
extraData.getSeals(),
SignatureAlgorithmFactory.getInstance().sign(hashToSign, nodeKeys),
extraData.getValidators());
}
}

@ -1,147 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy.headervalidationrules;
import org.hyperledger.besu.consensus.common.bft.BftHelpers;
import org.hyperledger.besu.consensus.ibft.IbftLegacyContext;
import org.hyperledger.besu.consensus.ibftlegacy.IbftBlockHashing;
import org.hyperledger.besu.consensus.ibftlegacy.IbftExtraData;
import org.hyperledger.besu.consensus.ibftlegacy.IbftHelpers;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.AttachedBlockHeaderValidationRule;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import java.util.Collection;
import java.util.List;
import java.util.NavigableSet;
import java.util.TreeSet;
import com.google.common.collect.Iterables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Ensures the byte content of the extraData field can be deserialised into an appropriate
* structure, and that the structure created contains data matching expectations from preceding
* blocks.
*/
public class IbftExtraDataValidationRule implements AttachedBlockHeaderValidationRule {
private static final Logger LOG = LoggerFactory.getLogger(IbftExtraDataValidationRule.class);
private final boolean validateCommitSeals;
private final long ceil2nBy3Block;
/**
* Instantiates a new Ibft extra data validation rule.
*
* @param validateCommitSeals the validate commit seals
* @param ceil2nBy3Block the ceil 2 n by 3 block
*/
public IbftExtraDataValidationRule(final boolean validateCommitSeals, final long ceil2nBy3Block) {
this.validateCommitSeals = validateCommitSeals;
this.ceil2nBy3Block = ceil2nBy3Block;
}
@Override
public boolean validate(
final BlockHeader header, final BlockHeader parent, final ProtocolContext context) {
try {
final Collection<Address> storedValidators =
context
.getConsensusContext(IbftLegacyContext.class)
.getValidatorProvider()
.getValidatorsAfterBlock(parent);
final IbftExtraData ibftExtraData = IbftExtraData.decode(header);
final Address proposer = IbftBlockHashing.recoverProposerAddress(header, ibftExtraData);
if (!storedValidators.contains(proposer)) {
LOG.info("Invalid block header: Proposer sealing block is not a member of the validators.");
return false;
}
if (validateCommitSeals) {
final List<Address> committers =
IbftBlockHashing.recoverCommitterAddresses(header, ibftExtraData);
final int minimumSealsRequired =
header.getNumber() < ceil2nBy3Block
? IbftHelpers.calculateRequiredValidatorQuorum(storedValidators.size())
: BftHelpers.calculateRequiredValidatorQuorum(storedValidators.size());
if (!validateCommitters(committers, storedValidators, minimumSealsRequired)) {
return false;
}
}
final NavigableSet<Address> sortedReportedValidators =
new TreeSet<>(ibftExtraData.getValidators());
if (!Iterables.elementsEqual(ibftExtraData.getValidators(), sortedReportedValidators)) {
LOG.info(
"Invalid block header: Validators are not sorted in ascending order. Expected {} but got {}.",
sortedReportedValidators,
ibftExtraData.getValidators());
return false;
}
if (!Iterables.elementsEqual(ibftExtraData.getValidators(), storedValidators)) {
LOG.info(
"Invalid block header: Incorrect validators. Expected {} but got {}.",
storedValidators,
ibftExtraData.getValidators());
return false;
}
} catch (final RLPException ex) {
LOG.info(
"Invalid block header: ExtraData field was unable to be deserialised into an IBFT Struct.",
ex);
return false;
} catch (final IllegalArgumentException ex) {
LOG.info("Invalid block header: Failed to verify extra data", ex);
return false;
} catch (final RuntimeException ex) {
LOG.info("Invalid block header: Failed to find validators at parent");
return false;
}
return true;
}
private boolean validateCommitters(
final Collection<Address> committers,
final Collection<Address> storedValidators,
final int minimumSealsRequired) {
if (committers.size() < minimumSealsRequired) {
LOG.info(
"Invalid block header: Insufficient committers to seal block. (Required {}, received {})",
minimumSealsRequired,
committers.size());
return false;
}
if (!storedValidators.containsAll(committers)) {
LOG.info(
"Invalid block header: Not all committers are in the locally maintained validator list.");
return false;
}
return true;
}
}

@ -1,45 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy.headervalidationrules;
import org.hyperledger.besu.consensus.ibftlegacy.IbftLegacyBlockInterface;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.DetachedBlockHeaderValidationRule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** The Vote validation rule. */
public class VoteValidationRule implements DetachedBlockHeaderValidationRule {
private static final Logger LOG = LoggerFactory.getLogger(VoteValidationRule.class);
/**
* Responsible for ensuring the nonce is either auth or drop.
*
* @param header the block header to validate
* @param parent the block header corresponding to the parent of the header being validated.
* @return true if the nonce in the header is a valid validator vote value.
*/
@Override
public boolean validate(final BlockHeader header, final BlockHeader parent) {
final long nonce = header.getNonce();
if (!IbftLegacyBlockInterface.isValidVoteValue(nonce)) {
LOG.info("Invalid block header: Nonce value ({}) is neither auth or drop.", nonce);
return false;
}
return true;
}
}

@ -1,130 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy.protocol;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import java.util.Arrays;
import java.util.List;
/**
* Represents the istanbul/64 protocol as used by Quorum (effectively an extension of eth/65, which
* adds a single message type (0x11) to encapsulate all communications required for IBFT block
* mining.
*/
public class Istanbul99Protocol implements SubProtocol {
/** The constant NAME. */
public static final String NAME = "istanbul";
private static final int VERSION = 99;
/** The Istanbul 99. */
static final Capability ISTANBUL99 = Capability.create(NAME, 99);
/** The Instanbul msg. */
static final int INSTANBUL_MSG = 0x11;
private static final Istanbul99Protocol INSTANCE = new Istanbul99Protocol();
private static final List<Integer> istanbul64Messages =
Arrays.asList(
EthPV62.STATUS,
EthPV62.NEW_BLOCK_HASHES,
EthPV62.TRANSACTIONS,
EthPV62.GET_BLOCK_HEADERS,
EthPV62.BLOCK_HEADERS,
EthPV62.GET_BLOCK_BODIES,
EthPV62.BLOCK_BODIES,
EthPV62.NEW_BLOCK,
EthPV65.NEW_POOLED_TRANSACTION_HASHES,
EthPV65.GET_POOLED_TRANSACTIONS,
EthPV65.POOLED_TRANSACTIONS,
EthPV63.GET_NODE_DATA,
EthPV63.NODE_DATA,
EthPV63.GET_RECEIPTS,
EthPV63.RECEIPTS,
INSTANBUL_MSG);
@Override
public String getName() {
return NAME;
}
@Override
public int messageSpace(final int protocolVersion) {
return INSTANBUL_MSG + 1;
}
@Override
public boolean isValidMessageCode(final int protocolVersion, final int code) {
if (protocolVersion == VERSION) {
return istanbul64Messages.contains(code);
}
return false;
}
@Override
public String messageName(final int protocolVersion, final int code) {
switch (code) {
case EthPV62.STATUS:
return "Status";
case EthPV62.NEW_BLOCK_HASHES:
return "NewBlockHashes";
case EthPV62.TRANSACTIONS:
return "Transactions";
case EthPV62.GET_BLOCK_HEADERS:
return "GetBlockHeaders";
case EthPV62.BLOCK_HEADERS:
return "BlockHeaders";
case EthPV62.GET_BLOCK_BODIES:
return "GetBlockBodies";
case EthPV62.BLOCK_BODIES:
return "BlockBodies";
case EthPV62.NEW_BLOCK:
return "NewBlock";
case EthPV65.NEW_POOLED_TRANSACTION_HASHES:
return "NewPooledTransactionHashes";
case EthPV65.GET_POOLED_TRANSACTIONS:
return "GetPooledTransactions";
case EthPV65.POOLED_TRANSACTIONS:
return "PooledTransactions";
case EthPV63.GET_NODE_DATA:
return "GetNodeData";
case EthPV63.NODE_DATA:
return "NodeData";
case EthPV63.GET_RECEIPTS:
return "GetReceipts";
case EthPV63.RECEIPTS:
return "Receipts";
case INSTANBUL_MSG:
return "InstanbulMsg";
default:
return INVALID_MESSAGE_NAME;
}
}
/**
* Get istanbul99 protocol singleton instance.
*
* @return the istanbul 99 protocol instance
*/
public static Istanbul99Protocol get() {
return INSTANCE;
}
}

@ -1,90 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy.protocol;
import static java.util.Collections.singletonList;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
/** This allows for interoperability with Quorum, but shouldn't be used otherwise. */
public class Istanbul99ProtocolManager extends EthProtocolManager {
/**
* Instantiates a new Istanbul99 protocol manager.
*
* @param blockchain the blockchain
* @param networkId the network id
* @param worldStateArchive the world state archive
* @param transactionPool the transaction pool
* @param ethereumWireProtocolConfiguration the ethereum wire protocol configuration
* @param ethPeers the eth peers
* @param ethMessages the eth messages
* @param ethContext the eth context
* @param peerValidators the peer validators
* @param synchronizerConfiguration the synchronizer configuration
* @param scheduler the scheduler
*/
public Istanbul99ProtocolManager(
final Blockchain blockchain,
final BigInteger networkId,
final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthMessages ethMessages,
final EthContext ethContext,
final List<PeerValidator> peerValidators,
final SynchronizerConfiguration synchronizerConfiguration,
final EthScheduler scheduler) {
super(
blockchain,
networkId,
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators,
Optional.empty(),
synchronizerConfiguration,
scheduler);
}
@Override
public List<Capability> getSupportedCapabilities() {
return singletonList(Istanbul99Protocol.ISTANBUL99);
}
@Override
public String getSupportedProtocol() {
return Istanbul99Protocol.get().getName();
}
}

@ -1,133 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.evm.log.LogsBloomFilter;
import java.util.Arrays;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public class BftBlockHashingTest {
private static final Address PROPOSER_IN_HEADER =
Address.fromHexString("0x24defc2d149861d3d245749b81fe0e6b28e04f31");
private static final List<Address> VALIDATORS_IN_HEADER =
Arrays.asList(
PROPOSER_IN_HEADER,
Address.fromHexString("0x2a813d7db3de19b07f92268b6d4125ed295cbe00"),
Address.fromHexString("0x3814f17bd4b7ce47ab8146684b3443c0a4b2fc2c"),
Address.fromHexString("0xc332d0db1704d18f89a590e7586811e36d37ce04"));
private static final List<Address> COMMITTERS_IN_HEADER =
Arrays.asList(
Address.fromHexString("0x3814f17bd4b7ce47ab8146684b3443c0a4b2fc2c"),
PROPOSER_IN_HEADER,
Address.fromHexString("0x2a813d7db3de19b07f92268b6d4125ed295cbe00"));
private static final Hash KNOWN_BLOCK_HASH =
Hash.fromHexString("0x0d60351c129af309fc8597c81358652d3d0f0e3141b5432888c4aae405ee0184");
private final BlockHeader header = createKnownHeaderFromCapturedData();
@Test
public void recoverProposerAddressFromSeal() {
final IbftExtraData ibftExtraData = IbftExtraData.decode(header);
final Address proposerAddress = IbftBlockHashing.recoverProposerAddress(header, ibftExtraData);
assertThat(proposerAddress).isEqualTo(PROPOSER_IN_HEADER);
}
@Test
public void readValidatorListFromExtraData() {
final IbftExtraData ibftExtraData = IbftExtraData.decode(header);
Assertions.assertThat(ibftExtraData.getValidators()).isEqualTo(VALIDATORS_IN_HEADER);
}
@Test
public void recoverCommitterAddresses() {
final IbftExtraData ibftExtraData = IbftExtraData.decode(header);
final List<Address> committers =
IbftBlockHashing.recoverCommitterAddresses(header, ibftExtraData);
assertThat(committers).isEqualTo(COMMITTERS_IN_HEADER);
}
@Test
public void calculateBlockHash() {
assertThat(header.getHash()).isEqualTo(KNOWN_BLOCK_HASH);
}
/*
Header information was extracted from a chain export (RLP) from a Quorum IBFT network.
The hash was determined by looking at the parentHash of the subsequent block (i.e. not calculated
by internal calculations, but rather by Quorum).
*/
private BlockHeader createKnownHeaderFromCapturedData() {
final BlockHeaderBuilder builder = new BlockHeaderBuilder();
final String extraDataHexString =
"0xd783010800846765746887676f312e392e32856c696e757800000"
+ "00000000000f90164f8549424defc2d149861d3d245749b81fe0e6b28e04f31942a813d7db3de19b07f92268b6d4"
+ "125ed295cbe00943814f17bd4b7ce47ab8146684b3443c0a4b2fc2c94c332d0db1704d18f89a590e7586811e36d3"
+ "7ce04b8417480a32e81936a40da3b8b730c28963a80011fdddb70470573675a11c7871873165e213b80b1ed5bf5a"
+ "59a31874baf1d6e83d55141f719ada73815c8712c4c6501f8c9b8417ba97752c9a3d14ae8c5f6f864c2808b816a0"
+ "d3ebef9a3b03c3cf9e31311baeb2e32609ccc99f13488e9e8ea192debf1c26f8c70c2332dfbb8456292fd9366110"
+ "0b841bbf2d1710a41bee7895dadbbbc92713ba9e74129bb665984f349950d7b5275303db99b12ea3483430079dd5"
+ "d90bcc3962f72217863725f6cd72ab5c10c8c540001b8415df74d9bf9687a3da10a4660cfd6fd6739df59db5535f"
+ "a3a7c58382ec587f4fe581089a9e3cd4b8c3b77eeabdd756f1f34ffb990cfd47e81bb205bd10be619d001";
builder.parentHash(
Hash.fromHexString("0xa7762d3307dbf2ae6a1ae1b09cf61c7603722b2379731b6b90409cdb8c8288a0"));
builder.ommersHash(
Hash.fromHexString("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"));
builder.coinbase(Address.fromHexString("0x0000000000000000000000000000000000000000"));
builder.stateRoot(
Hash.fromHexString("0xca07595b82f908822971b7e848398e3395e59ee52565c7ef3603df1a1fa7bc80"));
builder.transactionsRoot(
Hash.fromHexString("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"));
builder.receiptsRoot(
Hash.fromHexString("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"));
builder.logsBloom(
LogsBloomFilter.fromHexString(
"0x000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "0000"));
builder.difficulty(Difficulty.ONE);
builder.number(1);
builder.gasLimit(4704588);
builder.gasUsed(0);
builder.timestamp(1530674616);
builder.extraData(Bytes.fromHexString(extraDataHexString));
builder.mixHash(
Hash.fromHexString("0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365"));
builder.nonce(0);
builder.blockHeaderFunctions(new LegacyIbftBlockHeaderFunctions());
return builder.buildBlockHeader();
}
}

@ -1,157 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
public class BftExtraDataCodecTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
@Test
public void emptyListsConstituteValidContent() {
final SECPSignature proposerSeal =
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0);
final List<Address> validators = Lists.newArrayList();
final List<SECPSignature> committerSeals = Lists.newArrayList();
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
encoder.startList();
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytes(validator));
encoder.writeBytes(proposerSeal.encodedBytes());
encoder.writeList(committerSeals, (committer, rlp) -> rlp.writeBytes(committer.encodedBytes()));
encoder.endList();
// Create a byte buffer with no data.
final byte[] vanity_bytes = new byte[32];
final Bytes vanity_data = Bytes.wrap(vanity_bytes);
final Bytes bufferToInject = Bytes.wrap(vanity_data, encoder.encoded());
final IbftExtraData extraData = IbftExtraData.decodeRaw(bufferToInject);
assertThat(extraData.getVanityData()).isEqualTo(vanity_data);
assertThat(extraData.getProposerSeal()).isEqualTo(proposerSeal);
assertThat(extraData.getSeals()).isEqualTo(committerSeals);
assertThat(extraData.getValidators()).isEqualTo(validators);
}
@Test
public void fullyPopulatedDataProducesCorrectlyFormedExtraDataObject() {
final List<Address> validators = Arrays.asList(Address.ECREC, Address.SHA256);
final SECPSignature proposerSeal =
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0);
final List<SECPSignature> committerSeals =
Arrays.asList(
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.TEN, (byte) 0),
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.TEN, BigInteger.ONE, (byte) 0));
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
encoder.startList(); // This is required to create a "root node" for all RLP'd data
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytes(validator));
encoder.writeBytes(proposerSeal.encodedBytes());
encoder.writeList(committerSeals, (committer, rlp) -> rlp.writeBytes(committer.encodedBytes()));
encoder.endList();
// Create randomised vanity data.
final byte[] vanity_bytes = new byte[32];
new Random().nextBytes(vanity_bytes);
final Bytes vanity_data = Bytes.wrap(vanity_bytes);
final Bytes bufferToInject = Bytes.wrap(vanity_data, encoder.encoded());
final IbftExtraData extraData = IbftExtraData.decodeRaw(bufferToInject);
assertThat(extraData.getVanityData()).isEqualTo(vanity_data);
assertThat(extraData.getProposerSeal()).isEqualTo(proposerSeal);
assertThat(extraData.getSeals()).isEqualTo(committerSeals);
assertThat(extraData.getValidators()).isEqualTo(validators);
}
@Test
public void incorrectlyStructuredRlpThrowsException() {
final SECPSignature proposerSeal =
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0);
final List<Address> validators = Lists.newArrayList();
final List<SECPSignature> committerSeals = Lists.newArrayList();
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
encoder.startList();
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytes(validator));
encoder.writeBytes(proposerSeal.encodedBytes());
encoder.writeList(committerSeals, (committer, rlp) -> rlp.writeBytes(committer.encodedBytes()));
encoder.writeLong(1);
encoder.endList();
final Bytes bufferToInject = Bytes.wrap(Bytes.wrap(new byte[32]), encoder.encoded());
assertThatThrownBy(() -> IbftExtraData.decodeRaw(bufferToInject))
.isInstanceOf(RLPException.class);
}
@Test
public void incorrectlySizedVanityDataThrowsException() {
final List<Address> validators = Arrays.asList(Address.ECREC, Address.SHA256);
final SECPSignature proposerSeal =
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0);
final List<SECPSignature> committerSeals =
Arrays.asList(
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.TEN, (byte) 0),
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.TEN, BigInteger.ONE, (byte) 0));
final BytesValueRLPOutput encoder = new BytesValueRLPOutput();
encoder.startList();
encoder.writeList(validators, (validator, rlp) -> rlp.writeBytes(validator));
encoder.writeBytes(proposerSeal.encodedBytes());
encoder.writeList(committerSeals, (committer, rlp) -> rlp.writeBytes(committer.encodedBytes()));
encoder.endList();
final Bytes bufferToInject = Bytes.wrap(Bytes.wrap(new byte[31]), encoder.encoded());
assertThatThrownBy(() -> IbftExtraData.decodeRaw(bufferToInject))
.isInstanceOf(RLPException.class);
}
@Test
public void parseGenesisBlockWithZeroProposerSeal() {
final byte[] genesisBlockExtraData =
Hex.decode(
"0000000000000000000000000000000000000000000000000000000000000000f89af85494c332d0db1704d18f89a590e7586811e36d37ce049424defc2d149861d3d245749b81fe0e6b28e04f31943814f17bd4b7ce47ab8146684b3443c0a4b2fc2c942a813d7db3de19b07f92268b6d4125ed295cbe00b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0");
final Bytes bufferToInject = Bytes.wrap(genesisBlockExtraData);
final IbftExtraData extraData = IbftExtraData.decodeRaw(bufferToInject);
assertThat(extraData.getProposerSeal()).isNull();
}
}

@ -1,166 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.consensus.common.validator.ValidatorProvider;
import org.hyperledger.besu.consensus.ibft.IbftLegacyContext;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
import org.mockito.Mockito;
public class IbftBlockHeaderValidationRulesetFactoryTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private ProtocolContext setupContextWithValidators(final Collection<Address> validators) {
final IbftLegacyContext bftContext = mock(IbftLegacyContext.class);
final ValidatorProvider mockValidatorProvider = mock(ValidatorProvider.class);
when(bftContext.getValidatorProvider()).thenReturn(mockValidatorProvider);
when(mockValidatorProvider.getValidatorsAfterBlock(any())).thenReturn(validators);
when(bftContext.as(Mockito.any())).thenReturn(bftContext);
return new ProtocolContext(null, null, bftContext);
}
@Test
public void ibftValidateHeaderPasses() {
final KeyPair proposerKeyPair = SIGNATURE_ALGORITHM.get().generateKeyPair();
final Address proposerAddress =
Address.extract(Hash.hash(proposerKeyPair.getPublicKey().getEncodedBytes()));
final List<Address> validators = singletonList(proposerAddress);
final BlockHeader parentHeader = buildBlockHeader(1, proposerKeyPair, validators, null);
final BlockHeader blockHeader = buildBlockHeader(2, proposerKeyPair, validators, parentHeader);
final BlockHeaderValidator validator =
IbftBlockHeaderValidationRulesetFactory.ibftBlockHeaderValidator(5, 0).build();
assertThat(
validator.validateHeader(
blockHeader,
parentHeader,
setupContextWithValidators(validators),
HeaderValidationMode.FULL))
.isTrue();
}
@Test
public void ibftValidateHeaderFails() {
final KeyPair proposerKeyPair = SIGNATURE_ALGORITHM.get().generateKeyPair();
final Address proposerAddress =
Address.extract(Hash.hash(proposerKeyPair.getPublicKey().getEncodedBytes()));
final List<Address> validators = singletonList(proposerAddress);
final BlockHeader parentHeader = buildBlockHeader(1, proposerKeyPair, validators, null);
final BlockHeader blockHeader = buildBlockHeader(2, proposerKeyPair, validators, null);
final BlockHeaderValidator validator =
IbftBlockHeaderValidationRulesetFactory.ibftBlockHeaderValidator(5, 0).build();
assertThat(
validator.validateHeader(
blockHeader,
parentHeader,
setupContextWithValidators(validators),
HeaderValidationMode.FULL))
.isFalse();
}
private BlockHeader buildBlockHeader(
final long number,
final KeyPair proposerKeyPair,
final List<Address> validators,
final BlockHeader parent) {
final BlockHeaderTestFixture builder = new BlockHeaderTestFixture();
if (parent != null) {
builder.parentHash(parent.getHash());
}
builder.number(number);
builder.gasLimit(5000);
builder.timestamp(6000 * number);
builder.mixHash(
Hash.fromHexString("0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365"));
builder.ommersHash(Hash.EMPTY_LIST_HASH);
builder.nonce(IbftLegacyBlockInterface.DROP_NONCE);
builder.difficulty(Difficulty.ONE);
// Construct an extraData block
final IbftExtraData initialIbftExtraData =
new IbftExtraData(
Bytes.wrap(new byte[IbftExtraData.EXTRA_VANITY_LENGTH]),
emptyList(),
SIGNATURE_ALGORITHM.get().createSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0),
validators);
builder.extraData(initialIbftExtraData.encode());
final BlockHeader parentHeader = builder.buildHeader();
final Hash proposerSealHash =
IbftBlockHashing.calculateDataHashForProposerSeal(parentHeader, initialIbftExtraData);
final SECPSignature proposerSignature =
SIGNATURE_ALGORITHM.get().sign(proposerSealHash, proposerKeyPair);
final IbftExtraData proposedData =
new IbftExtraData(
Bytes.wrap(new byte[IbftExtraData.EXTRA_VANITY_LENGTH]),
singletonList(proposerSignature),
proposerSignature,
validators);
final Hash headerHashForCommitters =
IbftBlockHashing.calculateDataHashForCommittedSeal(parentHeader, proposedData);
final SECPSignature proposerAsCommitterSignature =
SIGNATURE_ALGORITHM.get().sign(headerHashForCommitters, proposerKeyPair);
final IbftExtraData sealedData =
new IbftExtraData(
Bytes.wrap(new byte[IbftExtraData.EXTRA_VANITY_LENGTH]),
singletonList(proposerAsCommitterSignature),
proposerSignature,
validators);
builder.extraData(sealedData.encode());
return builder.buildHeader();
}
}

@ -1,133 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.consensus.common.validator.VoteType.ADD;
import static org.hyperledger.besu.consensus.common.validator.VoteType.DROP;
import org.hyperledger.besu.consensus.common.validator.ValidatorVote;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.core.AddressHelpers;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.Util;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
public class IbftLegacyBlockInterfaceTest {
private static final KeyPair proposerKeys =
SignatureAlgorithmFactory.getInstance().generateKeyPair();
private static final Address proposerAddress =
Util.publicKeyToAddress(proposerKeys.getPublicKey());
private static final List<Address> validatorList = singletonList(proposerAddress);
private final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture();
private final IbftLegacyBlockInterface blockInterface = new IbftLegacyBlockInterface();
private final BlockHeaderBuilder builder =
BlockHeaderBuilder.fromHeader(headerBuilder.buildHeader())
.blockHeaderFunctions(new LegacyIbftBlockHeaderFunctions());
@Before
public void setup() {
// must set "number" to ensure extradata is correctly deserialised during hashing.
headerBuilder.coinbase(AddressHelpers.ofValue(0)).number(1);
}
@Test
public void headerWithZeroCoinbaseReturnsAnEmptyVote() {
assertThat(blockInterface.extractVoteFromHeader(headerBuilder.buildHeader())).isEmpty();
}
@Test
public void headerWithNonceOfZeroReportsDropVote() {
headerBuilder.nonce(0x0L).coinbase(AddressHelpers.ofValue(2));
final BlockHeader header =
TestHelpers.createIbftSignedBlockHeader(headerBuilder, proposerKeys, validatorList);
final Optional<ValidatorVote> extractedVote = blockInterface.extractVoteFromHeader(header);
assertThat(extractedVote)
.contains(new ValidatorVote(DROP, proposerAddress, header.getCoinbase()));
}
@Test
public void headerWithNonceOfMaxLongReportsAddVote() {
headerBuilder.nonce(0xFFFFFFFFFFFFFFFFL).coinbase(AddressHelpers.ofValue(2));
final BlockHeader header =
TestHelpers.createIbftSignedBlockHeader(headerBuilder, proposerKeys, validatorList);
final Optional<ValidatorVote> extractedVote = blockInterface.extractVoteFromHeader(header);
assertThat(extractedVote)
.contains(new ValidatorVote(ADD, proposerAddress, header.getCoinbase()));
}
@Test
public void blendingAddVoteToHeaderResultsInHeaderWithNonceOfMaxLong() {
final ValidatorVote vote =
new ValidatorVote(ADD, AddressHelpers.ofValue(1), AddressHelpers.ofValue(2));
final BlockHeaderBuilder builderWithVote =
IbftLegacyBlockInterface.insertVoteToHeaderBuilder(builder, Optional.of(vote));
final BlockHeader header = builderWithVote.buildBlockHeader();
assertThat(header.getCoinbase()).isEqualTo(vote.getRecipient());
assertThat(header.getNonce()).isEqualTo(0xFFFFFFFFFFFFFFFFL);
}
@Test
public void blendingDropVoteToHeaderResultsInHeaderWithNonceOfZero() {
final ValidatorVote vote =
new ValidatorVote(DROP, AddressHelpers.ofValue(1), AddressHelpers.ofValue(2));
final BlockHeaderBuilder builderWithVote =
IbftLegacyBlockInterface.insertVoteToHeaderBuilder(builder, Optional.of(vote));
final BlockHeader header = builderWithVote.buildBlockHeader();
assertThat(header.getCoinbase()).isEqualTo(vote.getRecipient());
assertThat(header.getNonce()).isEqualTo(0x0L);
}
@Test
public void nonVoteBlendedIntoHeaderResultsInACoinbaseOfZero() {
final BlockHeaderBuilder builderWithVote =
IbftLegacyBlockInterface.insertVoteToHeaderBuilder(builder, Optional.empty());
final BlockHeader header = builderWithVote.buildBlockHeader();
assertThat(header.getCoinbase()).isEqualTo(AddressHelpers.ofValue(0));
assertThat(header.getNonce()).isEqualTo(0x0L);
}
@Test
public void extractsValidatorsFromHeader() {
final BlockHeader header =
TestHelpers.createIbftSignedBlockHeader(headerBuilder, proposerKeys, validatorList);
final Collection<Address> extractedValidators = blockInterface.validatorsInBlock(header);
assertThat(extractedValidators).isEqualTo(validatorList);
}
}

@ -1,41 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import org.hyperledger.besu.consensus.common.validator.ValidatorProvider;
import org.hyperledger.besu.consensus.ibft.IbftLegacyContext;
import org.hyperledger.besu.datatypes.Address;
import java.util.Collection;
import org.mockito.Mockito;
public class IbftLegacyContextBuilder {
public static IbftLegacyContext setupContextWithValidators(final Collection<Address> validators) {
final IbftLegacyContext bftContext = mock(IbftLegacyContext.class, withSettings().lenient());
final ValidatorProvider mockValidatorProvider =
mock(ValidatorProvider.class, withSettings().lenient());
when(bftContext.getValidatorProvider()).thenReturn(mockValidatorProvider);
when(mockValidatorProvider.getValidatorsAfterBlock(any())).thenReturn(validators);
when(bftContext.as(Mockito.any())).thenReturn(bftContext);
return bftContext;
}
}

@ -1,59 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import java.util.Collections;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
public class TestHelpers {
public static BlockHeader createIbftSignedBlockHeader(
final BlockHeaderTestFixture blockHeaderBuilder,
final KeyPair signer,
final List<Address> validators) {
final IbftExtraData unsignedExtraData =
new IbftExtraData(Bytes.wrap(new byte[32]), Collections.emptyList(), null, validators);
blockHeaderBuilder.extraData(unsignedExtraData.encode());
final Hash signingHash =
IbftBlockHashing.calculateDataHashForProposerSeal(
blockHeaderBuilder.buildHeader(), unsignedExtraData);
final SECPSignature proposerSignature =
SignatureAlgorithmFactory.getInstance().sign(signingHash, signer);
final IbftExtraData signedExtraData =
new IbftExtraData(
unsignedExtraData.getVanityData(),
unsignedExtraData.getSeals(),
proposerSignature,
unsignedExtraData.getValidators());
blockHeaderBuilder.extraData(signedExtraData.encode());
return blockHeaderBuilder.buildHeader();
}
}

@ -1,131 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy.blockcreation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.consensus.ibftlegacy.IbftLegacyContextBuilder.setupContextWithValidators;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryWorldStateArchive;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.consensus.ibftlegacy.IbftBlockHeaderValidationRulesetFactory;
import org.hyperledger.besu.consensus.ibftlegacy.IbftExtraData;
import org.hyperledger.besu.consensus.ibftlegacy.IbftProtocolSchedule;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.GasPricePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.TestClock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
public class BftBlockCreatorTest {
private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
@Test
public void headerProducedPassesValidationRules() {
// Construct a parent block.
final BlockHeaderTestFixture blockHeaderBuilder = new BlockHeaderTestFixture();
blockHeaderBuilder.gasLimit(5000); // required to pass validation rule checks.
final BlockHeader parentHeader = blockHeaderBuilder.buildHeader();
final Optional<BlockHeader> optionalHeader = Optional.of(parentHeader);
// Construct a block chain and world state
final MutableBlockchain blockchain = mock(MutableBlockchain.class);
when(blockchain.getChainHeadHash()).thenReturn(parentHeader.getHash());
when(blockchain.getBlockHeader(any())).thenReturn(optionalHeader);
final BlockHeader blockHeader = mock(BlockHeader.class);
when(blockHeader.getBaseFee()).thenReturn(Optional.empty());
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
final KeyPair nodeKeys = SignatureAlgorithmFactory.getInstance().generateKeyPair();
// Add the local node as a validator (can't propose a block if node is not a validator).
final Address localAddr = Address.extract(Hash.hash(nodeKeys.getPublicKey().getEncodedBytes()));
final List<Address> initialValidatorList =
Arrays.asList(
Address.fromHexString(String.format("%020d", 1)),
Address.fromHexString(String.format("%020d", 2)),
Address.fromHexString(String.format("%020d", 3)),
Address.fromHexString(String.format("%020d", 4)),
localAddr);
final ProtocolSchedule protocolSchedule =
IbftProtocolSchedule.create(
GenesisConfigFile.fromConfig("{\"config\": {\"spuriousDragonBlock\":0}}")
.getConfigOptions(),
false,
EvmConfiguration.DEFAULT);
final ProtocolContext protContext =
new ProtocolContext(
blockchain,
createInMemoryWorldStateArchive(),
setupContextWithValidators(initialValidatorList));
final IbftBlockCreator blockCreator =
new IbftBlockCreator(
Address.fromHexString(String.format("%020d", 0)),
() -> Optional.of(10_000_000L),
parent ->
new IbftExtraData(
Bytes.wrap(new byte[32]), Lists.newArrayList(), null, initialValidatorList)
.encode(),
new GasPricePendingTransactionsSorter(
ImmutableTransactionPoolConfiguration.builder().txPoolMaxSize(1).build(),
TestClock.system(ZoneId.systemDefault()),
metricsSystem,
blockchain::getChainHeadHeader),
protContext,
protocolSchedule,
nodeKeys,
Wei.ZERO,
0.8,
parentHeader);
final Block block = blockCreator.createBlock(Instant.now().getEpochSecond()).getBlock();
final BlockHeaderValidator rules =
IbftBlockHeaderValidationRulesetFactory.ibftProposedBlockValidator(0).build();
final boolean validationResult =
rules.validateHeader(
block.getHeader(), parentHeader, protContext, HeaderValidationMode.FULL);
assertThat(validationResult).isTrue();
}
}

@ -1,330 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy.headervalidationrules;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.consensus.ibftlegacy.IbftLegacyContextBuilder.setupContextWithValidators;
import org.hyperledger.besu.consensus.ibftlegacy.IbftBlockHashing;
import org.hyperledger.besu.consensus.ibftlegacy.IbftExtraData;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.core.AddressHelpers;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes;
import org.assertj.core.api.Assertions;
import org.junit.Test;
public class BftExtraDataValidationRuleTest {
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance();
private BlockHeader createProposedBlockHeader(
final KeyPair proposerKeyPair, final List<Address> validators) {
final BlockHeaderTestFixture builder = new BlockHeaderTestFixture();
builder.number(1); // must NOT be block 0, as that should not contain seals at all
// Construct an extraData block and add to a header
final IbftExtraData initialIbftExtraData =
new IbftExtraData(
Bytes.wrap(new byte[IbftExtraData.EXTRA_VANITY_LENGTH]), emptyList(), null, validators);
builder.extraData(initialIbftExtraData.encode());
final BlockHeader header = builder.buildHeader();
// Hash the header (ignoring committer and proposer seals), and create signature
final Hash proposerSealHash =
IbftBlockHashing.calculateDataHashForProposerSeal(header, initialIbftExtraData);
final SECPSignature proposerSignature =
signatureAlgorithm.sign(proposerSealHash, proposerKeyPair);
// Construct a new extraData block, containing the constructed proposer signature
final IbftExtraData proposedData =
new IbftExtraData(
Bytes.wrap(new byte[IbftExtraData.EXTRA_VANITY_LENGTH]),
emptyList(),
proposerSignature,
validators);
// insert the signed ExtraData into the block
builder.extraData(proposedData.encode());
return builder.buildHeader();
}
private IbftExtraData createExtraDataWithCommitSeals(
final BlockHeader header, final Collection<KeyPair> committerKeyPairs) {
final IbftExtraData extraDataInHeader = IbftExtraData.decode(header);
final Hash headerHashForCommitters =
IbftBlockHashing.calculateDataHashForCommittedSeal(header, extraDataInHeader);
final List<SECPSignature> commitSeals =
committerKeyPairs.stream()
.map(keys -> signatureAlgorithm.sign(headerHashForCommitters, keys))
.collect(Collectors.toList());
return new IbftExtraData(
extraDataInHeader.getVanityData(),
commitSeals,
extraDataInHeader.getProposerSeal(),
extraDataInHeader.getValidators());
}
@Test
public void correctlyConstructedHeaderPassesValidation() {
final BlockHeaderTestFixture builder = new BlockHeaderTestFixture();
builder.number(1); // must NOT be block 0, as that should not contain seals at all
final KeyPair proposerKeyPair = signatureAlgorithm.generateKeyPair();
final Address proposerAddress =
Address.extract(Hash.hash(proposerKeyPair.getPublicKey().getEncodedBytes()));
final List<Address> validators = singletonList(proposerAddress);
final ProtocolContext context =
new ProtocolContext(null, null, setupContextWithValidators(validators));
final IbftExtraDataValidationRule extraDataValidationRule =
new IbftExtraDataValidationRule(true, 0);
BlockHeader header = createProposedBlockHeader(proposerKeyPair, validators);
// Insert an extraData block with committer seals.
final IbftExtraData commitedExtraData =
createExtraDataWithCommitSeals(header, singletonList(proposerKeyPair));
builder.extraData(commitedExtraData.encode());
header = builder.buildHeader();
assertThat(extraDataValidationRule.validate(header, null, context)).isTrue();
}
@Test
public void insufficientCommitSealsFailsValidation() {
final BlockHeaderTestFixture builder = new BlockHeaderTestFixture();
builder.number(1); // must NOT be block 0, as that should not contain seals at all
final KeyPair proposerKeyPair = signatureAlgorithm.generateKeyPair();
final Address proposerAddress =
Address.extract(Hash.hash(proposerKeyPair.getPublicKey().getEncodedBytes()));
final List<Address> validators = singletonList(proposerAddress);
final ProtocolContext context =
new ProtocolContext(null, null, setupContextWithValidators(validators));
final IbftExtraDataValidationRule extraDataValidationRule =
new IbftExtraDataValidationRule(true, 0);
final BlockHeader header = createProposedBlockHeader(proposerKeyPair, validators);
// Note that no committer seals are in the header's IBFT extra data.
final IbftExtraData headerExtraData = IbftExtraData.decode(header);
Assertions.assertThat(headerExtraData.getSeals().size()).isEqualTo(0);
assertThat(extraDataValidationRule.validate(header, null, context)).isFalse();
}
@Test
public void outOfOrderValidatorListFailsValidation() {
final BlockHeaderTestFixture builder = new BlockHeaderTestFixture();
builder.number(1); // must NOT be block 0, as that should not contain seals at all
final KeyPair proposerKeyPair = signatureAlgorithm.generateKeyPair();
final Address proposerAddress =
Address.extract(Hash.hash(proposerKeyPair.getPublicKey().getEncodedBytes()));
final List<Address> validators =
Lists.newArrayList(
AddressHelpers.calculateAddressWithRespectTo(proposerAddress, 1), proposerAddress);
final ProtocolContext context =
new ProtocolContext(null, null, setupContextWithValidators(validators));
final IbftExtraDataValidationRule extraDataValidationRule =
new IbftExtraDataValidationRule(true, 0);
BlockHeader header = createProposedBlockHeader(proposerKeyPair, validators);
// Insert an extraData block with committer seals.
final IbftExtraData commitedExtraData =
createExtraDataWithCommitSeals(header, singletonList(proposerKeyPair));
builder.extraData(commitedExtraData.encode());
header = builder.buildHeader();
assertThat(extraDataValidationRule.validate(header, null, context)).isFalse();
}
@Test
public void proposerNotInValidatorListFailsValidation() {
final BlockHeaderTestFixture builder = new BlockHeaderTestFixture();
builder.number(1); // must NOT be block 0, as that should not contain seals at all
final KeyPair proposerKeyPair = signatureAlgorithm.generateKeyPair();
final Address proposerAddress =
Address.extract(Hash.hash(proposerKeyPair.getPublicKey().getEncodedBytes()));
final List<Address> validators =
Lists.newArrayList(
AddressHelpers.calculateAddressWithRespectTo(proposerAddress, 1), proposerAddress);
final ProtocolContext context =
new ProtocolContext(null, null, setupContextWithValidators(validators));
final IbftExtraDataValidationRule extraDataValidationRule =
new IbftExtraDataValidationRule(true, 0);
BlockHeader header = createProposedBlockHeader(proposerKeyPair, validators);
// Insert an extraData block with committer seals.
final IbftExtraData commitedExtraData =
createExtraDataWithCommitSeals(header, singletonList(proposerKeyPair));
builder.extraData(commitedExtraData.encode());
header = builder.buildHeader();
assertThat(extraDataValidationRule.validate(header, null, context)).isFalse();
}
@Test
public void mismatchingReportedValidatorsVsLocallyStoredListFailsValidation() {
final BlockHeaderTestFixture builder = new BlockHeaderTestFixture();
builder.number(1); // must NOT be block 0, as that should not contain seals at all
final KeyPair proposerKeyPair = signatureAlgorithm.generateKeyPair();
final Address proposerAddress =
Address.extract(Hash.hash(proposerKeyPair.getPublicKey().getEncodedBytes()));
final List<Address> validators = Lists.newArrayList(proposerAddress);
final ProtocolContext context =
new ProtocolContext(null, null, setupContextWithValidators(validators));
final IbftExtraDataValidationRule extraDataValidationRule =
new IbftExtraDataValidationRule(true, 0);
// Add another validator to the list reported in the IbftExtraData (note, as the
final List<Address> extraDataValidators =
Lists.newArrayList(
proposerAddress, AddressHelpers.calculateAddressWithRespectTo(proposerAddress, 1));
BlockHeader header = createProposedBlockHeader(proposerKeyPair, extraDataValidators);
// Insert an extraData block with committer seals.
final IbftExtraData commitedExtraData =
createExtraDataWithCommitSeals(header, singletonList(proposerKeyPair));
builder.extraData(commitedExtraData.encode());
header = builder.buildHeader();
assertThat(extraDataValidationRule.validate(header, null, context)).isFalse();
}
@Test
public void committerNotInValidatorListFailsValidation() {
final BlockHeaderTestFixture builder = new BlockHeaderTestFixture();
builder.number(1); // must NOT be block 0, as that should not contain seals at all
final KeyPair proposerKeyPair = signatureAlgorithm.generateKeyPair();
final Address proposerAddress =
Address.extract(Hash.hash(proposerKeyPair.getPublicKey().getEncodedBytes()));
final List<Address> validators = singletonList(proposerAddress);
BlockHeader header = createProposedBlockHeader(proposerKeyPair, validators);
// Insert an extraData block with committer seals.
final KeyPair nonValidatorKeyPair = signatureAlgorithm.generateKeyPair();
final IbftExtraData commitedExtraData =
createExtraDataWithCommitSeals(header, singletonList(nonValidatorKeyPair));
builder.extraData(commitedExtraData.encode());
header = builder.buildHeader();
final ProtocolContext context =
new ProtocolContext(null, null, setupContextWithValidators(validators));
final IbftExtraDataValidationRule extraDataValidationRule =
new IbftExtraDataValidationRule(true, 0);
assertThat(extraDataValidationRule.validate(header, null, context)).isFalse();
}
@Test
public void ratioOfCommittersToValidatorsAffectValidation() {
assertThat(subExecution(4, 4, false)).isEqualTo(true);
assertThat(subExecution(4, 3, false)).isEqualTo(true);
assertThat(subExecution(4, 2, false)).isEqualTo(false);
assertThat(subExecution(5, 3, false)).isEqualTo(true);
assertThat(subExecution(5, 2, false)).isEqualTo(false);
assertThat(subExecution(6, 4, false)).isEqualTo(true);
assertThat(subExecution(6, 3, false)).isEqualTo(true);
assertThat(subExecution(6, 2, false)).isEqualTo(false);
assertThat(subExecution(7, 5, false)).isEqualTo(true);
assertThat(subExecution(7, 4, false)).isEqualTo(false);
assertThat(subExecution(9, 5, false)).isEqualTo(true);
assertThat(subExecution(9, 4, false)).isEqualTo(false);
assertThat(subExecution(10, 7, false)).isEqualTo(true);
assertThat(subExecution(10, 6, false)).isEqualTo(false);
assertThat(subExecution(12, 7, false)).isEqualTo(true);
assertThat(subExecution(12, 6, false)).isEqualTo(false);
// The concern in the above is that when using 6 validators, only 1/2 the validators are
// required to seal a block. All other combinations appear to be safe they always have >50%
// validators sealing the block.
}
private boolean subExecution(
final int validatorCount, final int committerCount, final boolean useTwoThirds) {
final BlockHeaderTestFixture builder = new BlockHeaderTestFixture();
builder.number(1); // must NOT be block 0, as that should not contain seals at all
final KeyPair proposerKeyPair = signatureAlgorithm.generateKeyPair();
final Address proposerAddress =
Address.extract(Hash.hash(proposerKeyPair.getPublicKey().getEncodedBytes()));
final List<Address> validators = Lists.newArrayList();
final List<KeyPair> committerKeys = Lists.newArrayList();
validators.add(proposerAddress);
committerKeys.add(proposerKeyPair);
for (int i = 0; i < validatorCount - 1; i++) { // need -1 to account for proposer
final KeyPair committerKeyPair = signatureAlgorithm.generateKeyPair();
committerKeys.add(committerKeyPair);
validators.add(Address.extract(Hash.hash(committerKeyPair.getPublicKey().getEncodedBytes())));
}
Collections.sort(validators);
BlockHeader header = createProposedBlockHeader(proposerKeyPair, validators);
final IbftExtraData commitedExtraData =
createExtraDataWithCommitSeals(header, committerKeys.subList(0, committerCount));
builder.extraData(commitedExtraData.encode());
header = builder.buildHeader();
final ProtocolContext context =
new ProtocolContext(null, null, setupContextWithValidators(validators));
final IbftExtraDataValidationRule extraDataValidationRule =
new IbftExtraDataValidationRule(true, useTwoThirds ? 0 : 2);
return extraDataValidationRule.validate(header, null, context);
}
}

@ -1,159 +0,0 @@
/*
* Copyright 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.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.consensus.ibftlegacy.protocol;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.EthProtocolVersion;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.manager.MockPeerConnection;
import org.hyperledger.besu.ethereum.eth.manager.MockPeerConnection.PeerSendHandler;
import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.StatusMessage;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.worldstate.DataStorageFormat;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.testutil.TestClock;
import java.math.BigInteger;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.google.common.collect.Lists;
import org.junit.BeforeClass;
import org.junit.Test;
public class Istanbul99ProtocolManagerTest {
private static Blockchain blockchain;
private static TransactionPool transactionPool;
private static ProtocolSchedule protocolSchedule;
private static ProtocolContext protocolContext;
@BeforeClass
public static void setup() {
final BlockchainSetupUtil blockchainSetupUtil =
BlockchainSetupUtil.forTesting(DataStorageFormat.FOREST);
blockchainSetupUtil.importAllBlocks();
blockchain = blockchainSetupUtil.getBlockchain();
transactionPool = blockchainSetupUtil.getTransactionPool();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext();
assertThat(blockchainSetupUtil.getMaxBlockNumber()).isGreaterThanOrEqualTo(20L);
}
private MockPeerConnection setupPeer(
final EthProtocolManager ethManager, final PeerSendHandler onSend) {
final MockPeerConnection peer = setupPeerWithoutStatusExchange(ethManager, onSend);
final StatusMessage statusMessage =
StatusMessage.create(
EthProtocolVersion.V63,
BigInteger.ONE,
blockchain.getChainHead().getTotalDifficulty(),
blockchain.getChainHeadHash(),
blockchain.getBlockHeader(BlockHeader.GENESIS_BLOCK_NUMBER).get().getHash());
ethManager.processMessage(
Istanbul99Protocol.ISTANBUL99, new DefaultMessage(peer, statusMessage));
return peer;
}
private MockPeerConnection setupPeerWithoutStatusExchange(
final EthProtocolManager ethManager, final PeerSendHandler onSend) {
final Set<Capability> caps =
new HashSet<>(Collections.singletonList(Istanbul99Protocol.ISTANBUL99));
final MockPeerConnection peer = new MockPeerConnection(caps, onSend);
ethManager.handleNewConnection(peer);
return peer;
}
@Test
public void respondToEth65GetHeadersUsingIstanbul99()
throws ExecutionException, InterruptedException, TimeoutException {
final CompletableFuture<Void> done = new CompletableFuture<>();
final EthScheduler ethScheduler = new DeterministicEthScheduler(() -> false);
EthPeers peers =
new EthPeers(
Istanbul99Protocol.NAME,
() -> protocolSchedule.getByBlockHeader(blockchain.getChainHeadHeader()),
TestClock.fixed(),
new NoOpMetricsSystem(),
25,
EthProtocolConfiguration.DEFAULT_MAX_MESSAGE_SIZE);
EthMessages messages = new EthMessages();
final BigInteger networkId = BigInteger.ONE;
try (final EthProtocolManager ethManager =
new Istanbul99ProtocolManager(
blockchain,
networkId,
protocolContext.getWorldStateArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig(),
peers,
messages,
new EthContext(peers, messages, ethScheduler),
Collections.emptyList(),
mock(SynchronizerConfiguration.class),
ethScheduler)) {
final long startBlock = blockchain.getChainHeadBlockNumber() + 1;
final int blockCount = 5;
final MessageData messageData =
GetBlockHeadersMessage.create(startBlock, blockCount, 0, false);
final PeerSendHandler onSend =
(cap, message, conn) -> {
if (message.getCode() == EthPV62.STATUS) {
// Ignore status message
return;
}
assertThat(message.getCode()).isEqualTo(EthPV62.BLOCK_HEADERS);
final BlockHeadersMessage headersMsg = BlockHeadersMessage.readFrom(message);
final List<BlockHeader> headers =
Lists.newArrayList(headersMsg.getHeaders(protocolSchedule));
assertThat(headers.size()).isEqualTo(0);
done.complete(null);
};
final PeerConnection peer = setupPeer(ethManager, onSend);
ethManager.processMessage(
Istanbul99Protocol.ISTANBUL99, new DefaultMessage(peer, messageData));
done.get(10, TimeUnit.SECONDS);
}
}
}

@ -99,7 +99,7 @@ public class GenesisFileModule {
private static GenesisFileModule createGenesisModule(final String genesisConfig) { private static GenesisFileModule createGenesisModule(final String genesisConfig) {
final JsonObject genesis = new JsonObject(genesisConfig); final JsonObject genesis = new JsonObject(genesisConfig);
final JsonObject config = genesis.getJsonObject("config"); final JsonObject config = genesis.getJsonObject("config");
if (config.containsKey("ibft") || config.containsKey("clique") || config.containsKey("qbft")) { if (config.containsKey("clique") || config.containsKey("qbft")) {
throw new RuntimeException("Only Ethash and Merge configs accepted as genesis files"); throw new RuntimeException("Only Ethash and Merge configs accepted as genesis files");
} }
return new MainnetGenesisFileModule(genesisConfig); return new MainnetGenesisFileModule(genesisConfig);

@ -41,7 +41,6 @@ include 'config'
include 'consensus:clique' include 'consensus:clique'
include 'consensus:common' include 'consensus:common'
include 'consensus:ibft' include 'consensus:ibft'
include 'consensus:ibftlegacy'
include 'consensus:merge' include 'consensus:merge'
include 'consensus:qbft' include 'consensus:qbft'
include 'datatypes' include 'datatypes'

Loading…
Cancel
Save