mirror of https://github.com/hyperledger/besu
[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
parent
14407ade0b
commit
795b7c4dfb
2
acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningIbftStallAcceptanceTest.java → acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningIbft2StallAcceptanceTest.java
2
acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningIbftStallAcceptanceTest.java → acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningIbft2StallAcceptanceTest.java
@ -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); |
||||
} |
||||
} |
@ -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" |
||||
} |
@ -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(); |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue