TransactionValidatorProvider.java
/*
* 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.qbft.validator;
import org.hyperledger.besu.config.QbftConfigOptions;
import org.hyperledger.besu.consensus.common.ForksSchedule;
import org.hyperledger.besu.consensus.common.validator.ValidatorProvider;
import org.hyperledger.besu.consensus.common.validator.VoteProvider;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
/** The Transaction validator provider. */
public class TransactionValidatorProvider implements ValidatorProvider {
private final Blockchain blockchain;
private final ValidatorContractController validatorContractController;
private final ForksSchedule<QbftConfigOptions> forksSchedule;
private final Cache<Long, Collection<Address>> afterBlockValidatorCache =
CacheBuilder.newBuilder().maximumSize(100).build();
private final Cache<Long, Collection<Address>> forBlockValidatorCache =
CacheBuilder.newBuilder().maximumSize(100).build();
/**
* Instantiates a new Transaction validator provider.
*
* @param blockchain the blockchain
* @param validatorContractController the validator contract controller
* @param forksSchedule the forks schedule
*/
public TransactionValidatorProvider(
final Blockchain blockchain,
final ValidatorContractController validatorContractController,
final ForksSchedule<QbftConfigOptions> forksSchedule) {
this.blockchain = blockchain;
this.validatorContractController = validatorContractController;
this.forksSchedule = forksSchedule;
}
@Override
public Collection<Address> getValidatorsAtHead() {
return getValidatorsAfterBlock(blockchain.getChainHeadHeader());
}
@Override
public Collection<Address> getValidatorsAfterBlock(final BlockHeader parentHeader) {
// For the validator contract we determine the validators from the previous block but the
// address from block about to be created
final long nextBlock = parentHeader.getNumber() + 1;
final Address contractAddress = resolveContractAddress(nextBlock);
return getValidatorsFromContract(afterBlockValidatorCache, parentHeader, contractAddress);
}
@Override
public Collection<Address> getValidatorsForBlock(final BlockHeader header) {
final Address contractAddress = resolveContractAddress(header.getNumber());
return getValidatorsFromContract(forBlockValidatorCache, header, contractAddress);
}
private Collection<Address> getValidatorsFromContract(
final Cache<Long, Collection<Address>> validatorCache,
final BlockHeader header,
final Address contractAddress) {
final long blockNumber = header.getNumber();
try {
return validatorCache.get(
blockNumber,
() ->
validatorContractController.getValidators(blockNumber, contractAddress).stream()
.sorted()
.collect(Collectors.toList()));
} catch (final ExecutionException e) {
throw new RuntimeException("Unable to determine a validators for the requested block.");
}
}
@Override
public Optional<VoteProvider> getVoteProviderAtHead() {
return Optional.empty();
}
private Address resolveContractAddress(final long blockNumber) {
return forksSchedule
.getFork(blockNumber)
.getValue()
.getValidatorContractAddress()
.map(Address::fromHexString)
.orElseThrow(
() ->
new RuntimeException(
"Error resolving smart contract address unable to make validator contract call"));
}
}