use of org.hyperledger.besu.ethereum.vm.BlockHashLookup in project besu by hyperledger.
the class BlockReplay method afterTransactionInBlock.
public <T> Optional<T> afterTransactionInBlock(final Hash blockHash, final Hash transactionHash, final TransactionAction<T> action) {
return beforeTransactionInBlock(blockHash, transactionHash, (transaction, blockHeader, blockchain, worldState, transactionProcessor) -> {
final ProtocolSpec spec = protocolSchedule.getByBlockNumber(blockHeader.getNumber());
transactionProcessor.processTransaction(blockchain, worldState.updater(), blockHeader, transaction, spec.getMiningBeneficiaryCalculator().calculateBeneficiary(blockHeader), new BlockHashLookup(blockHeader, blockchain), false, TransactionValidationParams.blockReplay());
return action.performAction(transaction, blockHeader, blockchain, worldState, transactionProcessor);
});
}
use of org.hyperledger.besu.ethereum.vm.BlockHashLookup in project besu by hyperledger.
the class BlockHashOperationBenchmark method executeOperationWithEmptyHashCache.
@Benchmark
public Bytes executeOperationWithEmptyHashCache() {
final MessageFrame cleanFrame = operationBenchmarkHelper.createMessageFrameBuilder().blockHashLookup(new BlockHashLookup((ProcessableBlockHeader) frame.getBlockValues(), operationBenchmarkHelper.getBlockchain())).build();
cleanFrame.pushStackItem(UInt256.valueOf(blockNumber));
operation.execute(cleanFrame, null);
return cleanFrame.popStackItem();
}
use of org.hyperledger.besu.ethereum.vm.BlockHashLookup in project besu by hyperledger.
the class BlockTransactionSelector method evaluateTransaction.
/*
* Passed into the PendingTransactions, and is called on each transaction until sufficient
* transactions are found which fill a block worth of gas.
*
* This function will continue to be called until the block under construction is suitably
* full (in terms of gasLimit) and the provided transaction's gasLimit does not fit within
* the space remaining in the block.
*
*/
private TransactionSelectionResult evaluateTransaction(final Transaction transaction) {
if (isCancelled.get()) {
throw new CancellationException("Cancelled during transaction selection.");
}
if (transactionTooLargeForBlock(transaction)) {
LOG.trace("{} too large to select for block creation", transaction);
if (blockOccupancyAboveThreshold()) {
return TransactionSelectionResult.COMPLETE_OPERATION;
} else {
return TransactionSelectionResult.CONTINUE;
}
}
// If the gas price specified by the transaction is less than this node is willing to accept,
// do not include it in the block.
final Wei actualMinTransactionGasPriceInBlock = feeMarket.getTransactionPriceCalculator().price(transaction, processableBlockHeader.getBaseFee());
if (minTransactionGasPrice.compareTo(actualMinTransactionGasPriceInBlock) > 0) {
LOG.warn("Gas fee of {} lower than configured minimum {}, deleting", transaction, minTransactionGasPrice);
return TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE;
}
final WorldUpdater worldStateUpdater = worldState.updater();
final BlockHashLookup blockHashLookup = new BlockHashLookup(processableBlockHeader, blockchain);
final boolean isGoQuorumPrivateTransaction = transaction.isGoQuorumPrivateTransaction(transactionProcessor.getTransactionValidator().getGoQuorumCompatibilityMode());
TransactionProcessingResult effectiveResult;
if (isGoQuorumPrivateTransaction) {
final ValidationResult<TransactionInvalidReason> validationResult = validateTransaction(processableBlockHeader, transaction, worldStateUpdater);
if (!validationResult.isValid()) {
LOG.warn("Invalid transaction: {}. Block {} Transaction {}", validationResult.getErrorMessage(), processableBlockHeader.getParentHash().toHexString(), transaction.getHash().toHexString());
return transactionSelectionResultForInvalidResult(validationResult);
} else {
// valid GoQuorum private tx, we need to hand craft the receipt and increment the nonce
effectiveResult = publicResultForWhenWeHaveAPrivateTransaction(transaction);
worldStateUpdater.getOrCreate(transaction.getSender()).getMutable().incrementNonce();
}
} else {
effectiveResult = transactionProcessor.processTransaction(blockchain, worldStateUpdater, processableBlockHeader, transaction, miningBeneficiary, blockHashLookup, false, TransactionValidationParams.mining());
}
if (!effectiveResult.isInvalid()) {
worldStateUpdater.commit();
LOG.trace("Selected {} for block creation", transaction);
updateTransactionResultTracking(transaction, effectiveResult);
} else {
return transactionSelectionResultForInvalidResult(effectiveResult.getValidationResult());
}
return TransactionSelectionResult.CONTINUE;
}
use of org.hyperledger.besu.ethereum.vm.BlockHashLookup in project besu by hyperledger.
the class CreateOperationTest method testMemoryFrame.
@NotNull
private MessageFrame testMemoryFrame(final UInt256 memoryOffset, final UInt256 memoryLength, final UInt256 value, final int depth, final ArrayDeque<MessageFrame> messageFrameStack) {
final MessageFrame messageFrame = MessageFrame.builder().type(MessageFrame.Type.CONTRACT_CREATION).contract(Address.ZERO).inputData(Bytes.EMPTY).sender(Address.fromHexString(SENDER)).value(Wei.ZERO).apparentValue(Wei.ZERO).code(Code.createLegacyCode(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE))).depth(depth).completer(__ -> {
}).address(Address.fromHexString(SENDER)).blockHashLookup(mock(BlockHashLookup.class)).blockValues(mock(ProcessableBlockHeader.class)).gasPrice(Wei.ZERO).messageFrameStack(messageFrameStack).miningBeneficiary(Address.ZERO).originator(Address.ZERO).initialGas(100000L).worldUpdater(worldUpdater).build();
messageFrame.pushStackItem(memoryLength);
messageFrame.pushStackItem(memoryOffset);
messageFrame.pushStackItem(value);
messageFrame.expandMemory(0, 500);
messageFrame.writeMemory(memoryOffset.trimLeadingZeros().toInt(), SIMPLE_CREATE.size(), SIMPLE_CREATE);
return messageFrame;
}
use of org.hyperledger.besu.ethereum.vm.BlockHashLookup in project besu by hyperledger.
the class MainnetTransactionProcessor method processTransaction.
public TransactionProcessingResult processTransaction(final Blockchain blockchain, final WorldUpdater worldState, final ProcessableBlockHeader blockHeader, final Transaction transaction, final Address miningBeneficiary, final OperationTracer operationTracer, final BlockHashLookup blockHashLookup, final Boolean isPersistingPrivateState, final TransactionValidationParams transactionValidationParams, final PrivateMetadataUpdater privateMetadataUpdater) {
try {
LOG.trace("Starting execution of {}", transaction);
ValidationResult<TransactionInvalidReason> validationResult = transactionValidator.validate(transaction, blockHeader.getBaseFee(), transactionValidationParams);
// be signed correctly to extract the sender).
if (!validationResult.isValid()) {
LOG.debug("Invalid transaction: {}", validationResult.getErrorMessage());
return TransactionProcessingResult.invalid(validationResult);
}
final Address senderAddress = transaction.getSender();
final EvmAccount sender = worldState.getOrCreateSenderAccount(senderAddress);
validationResult = transactionValidator.validateForSender(transaction, sender, transactionValidationParams);
if (!validationResult.isValid()) {
LOG.debug("Invalid transaction: {}", validationResult.getErrorMessage());
return TransactionProcessingResult.invalid(validationResult);
}
final MutableAccount senderMutableAccount = sender.getMutable();
final long previousNonce = senderMutableAccount.incrementNonce();
final Wei transactionGasPrice = feeMarket.getTransactionPriceCalculator().price(transaction, blockHeader.getBaseFee());
LOG.trace("Incremented sender {} nonce ({} -> {})", senderAddress, previousNonce, sender.getNonce());
final Wei upfrontGasCost = transaction.getUpfrontGasCost(transactionGasPrice);
final Wei previousBalance = senderMutableAccount.decrementBalance(upfrontGasCost);
LOG.trace("Deducted sender {} upfront gas cost {} ({} -> {})", senderAddress, upfrontGasCost, previousBalance, sender.getBalance());
final List<AccessListEntry> accessListEntries = transaction.getAccessList().orElse(List.of());
// we need to keep a separate hash set of addresses in case they specify no storage.
// No-storage is a common pattern, especially for Externally Owned Accounts
final Set<Address> addressList = new HashSet<>();
final Multimap<Address, Bytes32> storageList = HashMultimap.create();
int accessListStorageCount = 0;
for (final var entry : accessListEntries) {
final Address address = entry.getAddress();
addressList.add(address);
final List<Bytes32> storageKeys = entry.getStorageKeys();
storageList.putAll(address, storageKeys);
accessListStorageCount += storageKeys.size();
}
final long intrinsicGas = gasCalculator.transactionIntrinsicGasCost(transaction.getPayload(), transaction.isContractCreation());
final long accessListGas = gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount);
final long gasAvailable = transaction.getGasLimit() - intrinsicGas - accessListGas;
LOG.trace("Gas available for execution {} = {} - {} (limit - intrinsic)", gasAvailable, transaction.getGasLimit(), intrinsicGas);
final WorldUpdater worldUpdater = worldState.updater();
final Deque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final ImmutableMap.Builder<String, Object> contextVariablesBuilder = ImmutableMap.<String, Object>builder().put(KEY_IS_PERSISTING_PRIVATE_STATE, isPersistingPrivateState).put(KEY_TRANSACTION, transaction).put(KEY_TRANSACTION_HASH, transaction.getHash());
if (privateMetadataUpdater != null) {
contextVariablesBuilder.put(KEY_PRIVATE_METADATA_UPDATER, privateMetadataUpdater);
}
final MessageFrame.Builder commonMessageFrameBuilder = MessageFrame.builder().messageFrameStack(messageFrameStack).maxStackSize(maxStackSize).worldUpdater(worldUpdater.updater()).initialGas(gasAvailable).originator(senderAddress).gasPrice(transactionGasPrice).sender(senderAddress).value(transaction.getValue()).apparentValue(transaction.getValue()).blockValues(blockHeader).depth(0).completer(__ -> {
}).miningBeneficiary(miningBeneficiary).blockHashLookup(blockHashLookup).contextVariables(contextVariablesBuilder.build()).accessListWarmAddresses(addressList).accessListWarmStorage(storageList);
final MessageFrame initialFrame;
if (transaction.isContractCreation()) {
final Address contractAddress = Address.contractAddress(senderAddress, senderMutableAccount.getNonce() - 1L);
final Bytes initCodeBytes = transaction.getPayload();
initialFrame = commonMessageFrameBuilder.type(MessageFrame.Type.CONTRACT_CREATION).address(contractAddress).contract(contractAddress).inputData(Bytes.EMPTY).code(contractCreationProcessor.getCodeFromEVM(Hash.hash(initCodeBytes), initCodeBytes)).build();
} else {
// isContractCall tests isPresent
@SuppressWarnings("OptionalGetWithoutIsPresent") final Address to = transaction.getTo().get();
final Optional<Account> maybeContract = Optional.ofNullable(worldState.get(to));
initialFrame = commonMessageFrameBuilder.type(MessageFrame.Type.MESSAGE_CALL).address(to).contract(to).inputData(transaction.getPayload()).code(maybeContract.map(c -> messageCallProcessor.getCodeFromEVM(c.getCodeHash(), c.getCode())).orElse(Code.EMPTY_CODE)).build();
}
messageFrameStack.addFirst(initialFrame);
while (!messageFrameStack.isEmpty()) {
process(messageFrameStack.peekFirst(), operationTracer);
}
if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
worldUpdater.commit();
}
if (LOG.isTraceEnabled()) {
LOG.trace("Gas used by transaction: {}, by message call/contract creation: {}", transaction.getGasLimit() - initialFrame.getRemainingGas(), gasAvailable - initialFrame.getRemainingGas());
}
// Refund the sender by what we should and pay the miner fee (note that we're doing them one
// after the other so that if it is the same account somehow, we end up with the right result)
final long selfDestructRefund = gasCalculator.getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
final long refundGas = initialFrame.getGasRefund() + selfDestructRefund;
final long refunded = refunded(transaction, initialFrame.getRemainingGas(), refundGas);
final Wei refundedWei = transactionGasPrice.multiply(refunded);
senderMutableAccount.incrementBalance(refundedWei);
final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas();
if (!worldState.getClass().equals(GoQuorumMutablePrivateWorldStateUpdater.class)) {
// if this is not a private GoQuorum transaction we have to update the coinbase
final var coinbase = worldState.getOrCreate(miningBeneficiary).getMutable();
final long coinbaseFee = transaction.getGasLimit() - refunded;
if (blockHeader.getBaseFee().isPresent()) {
final Wei baseFee = blockHeader.getBaseFee().get();
if (transactionGasPrice.compareTo(baseFee) < 0) {
return TransactionProcessingResult.failed(gasUsedByTransaction, refunded, ValidationResult.invalid(TransactionInvalidReason.TRANSACTION_PRICE_TOO_LOW, "transaction price must be greater than base fee"), Optional.empty());
}
}
final CoinbaseFeePriceCalculator coinbaseCalculator = blockHeader.getBaseFee().isPresent() ? coinbaseFeePriceCalculator : CoinbaseFeePriceCalculator.frontier();
final Wei coinbaseWeiDelta = coinbaseCalculator.price(coinbaseFee, transactionGasPrice, blockHeader.getBaseFee());
coinbase.incrementBalance(coinbaseWeiDelta);
}
initialFrame.getSelfDestructs().forEach(worldState::deleteAccount);
if (clearEmptyAccounts) {
clearEmptyAccounts(worldState);
}
if (initialFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
return TransactionProcessingResult.successful(initialFrame.getLogs(), gasUsedByTransaction, refunded, initialFrame.getOutputData(), validationResult);
} else {
return TransactionProcessingResult.failed(gasUsedByTransaction, refunded, validationResult, initialFrame.getRevertReason());
}
} catch (final RuntimeException re) {
LOG.error("Critical Exception Processing Transaction", re);
return TransactionProcessingResult.invalid(ValidationResult.invalid(TransactionInvalidReason.INTERNAL_ERROR, "Internal Error in Besu - " + re));
}
}
Aggregations