use of org.hyperledger.besu.evm.account.EvmAccount in project besu by hyperledger.
the class AbstractBlockCreator method rewardBeneficiary.
/* Copied from BlockProcessor (with modifications). */
boolean rewardBeneficiary(final MutableWorldState worldState, final ProcessableBlockHeader header, final List<BlockHeader> ommers, final Address miningBeneficiary, final Wei blockReward, final boolean skipZeroBlockRewards) {
// TODO(tmm): Added to make this work, should come from blockProcessor.
final int MAX_GENERATION = 6;
if (skipZeroBlockRewards && blockReward.isZero()) {
return true;
}
final Wei coinbaseReward = protocolSpec.getBlockProcessor().getCoinbaseReward(blockReward, header.getNumber(), ommers.size());
final WorldUpdater updater = worldState.updater();
final EvmAccount beneficiary = updater.getOrCreate(miningBeneficiary);
beneficiary.getMutable().incrementBalance(coinbaseReward);
for (final BlockHeader ommerHeader : ommers) {
if (ommerHeader.getNumber() - header.getNumber() > MAX_GENERATION) {
LOG.trace("Block processing error: ommer block number {} more than {} generations current block number {}", ommerHeader.getNumber(), MAX_GENERATION, header.getNumber());
return false;
}
final EvmAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase());
final Wei ommerReward = protocolSpec.getBlockProcessor().getOmmerReward(blockReward, header.getNumber(), ommerHeader.getNumber());
ommerCoinbase.getMutable().incrementBalance(ommerReward);
}
updater.commit();
return true;
}
use of org.hyperledger.besu.evm.account.EvmAccount 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));
}
}
use of org.hyperledger.besu.evm.account.EvmAccount in project besu by hyperledger.
the class MessageCallProcessor method transferValue.
/**
* Transfers the message call value from the sender to the recipient.
*
* <p>Assumes that the transaction has been validated so that the sender has the required fund as
* of the world state of this executor.
*/
private void transferValue(final MessageFrame frame) {
final EvmAccount senderAccount = frame.getWorldUpdater().getSenderAccount(frame);
// The yellow paper explicitly states that if the recipient account doesn't exist at this
// point, it is created. Even if the value is zero we are still creating an account with 0x!
final EvmAccount recipientAccount = frame.getWorldUpdater().getOrCreate(frame.getRecipientAddress());
if (Objects.equals(frame.getValue(), Wei.ZERO)) {
// This is only here for situations where you are calling a public address from a private
// address. Without this guard clause we would attempt to get a mutable public address
// which isn't possible from a private address and an error would be thrown.
// If you are attempting to transfer value from a private address
// to public address an error will be thrown.
LOG.trace("Message call from {} to {} has zero value: no fund transferred", frame.getSenderAddress(), frame.getRecipientAddress());
return;
}
if (frame.getRecipientAddress().equals(frame.getSenderAddress())) {
LOG.trace("Message call of {} to itself: no fund transferred", frame.getSenderAddress());
} else {
final Wei prevSenderBalance = senderAccount.getMutable().decrementBalance(frame.getValue());
final Wei prevRecipientBalance = recipientAccount.getMutable().incrementBalance(frame.getValue());
LOG.trace("Transferred value {} for message call from {} ({} -> {}) to {} ({} -> {})", frame.getValue(), frame.getSenderAddress(), prevSenderBalance, senderAccount.getBalance(), frame.getRecipientAddress(), prevRecipientBalance, recipientAccount.getBalance());
}
}
use of org.hyperledger.besu.evm.account.EvmAccount in project besu by hyperledger.
the class GoQuorumBlockProcessor method validateTransaction.
private ValidationResult<TransactionInvalidReason> validateTransaction(final BlockHeader blockHeader, final Transaction transaction, final WorldUpdater publicWorldStateUpdater) {
final MainnetTransactionValidator transactionValidator = transactionProcessor.getTransactionValidator();
final TransactionValidationParams transactionValidationParams = TransactionValidationParams.processingBlock();
ValidationResult<TransactionInvalidReason> validationResult = transactionValidator.validate(transaction, blockHeader.getBaseFee(), transactionValidationParams);
if (!validationResult.isValid()) {
LOG.warn("Invalid transaction: {}. Block {} Transaction {}", validationResult.getErrorMessage(), blockHeader.getHash().toHexString(), transaction.getHash().toHexString());
return validationResult;
}
final Address senderAddress = transaction.getSender();
final EvmAccount sender = publicWorldStateUpdater.getOrCreate(senderAddress);
validationResult = transactionValidator.validateForSender(transaction, sender, transactionValidationParams);
if (!validationResult.isValid()) {
LOG.warn("Invalid transaction: {}. Block {} Transaction {}", validationResult.getErrorMessage(), blockHeader.getHash().toHexString(), transaction.getHash().toHexString());
return validationResult;
}
return ValidationResult.valid();
}
use of org.hyperledger.besu.evm.account.EvmAccount in project besu by hyperledger.
the class BlockTransactionSelector method validateTransaction.
private ValidationResult<TransactionInvalidReason> validateTransaction(final ProcessableBlockHeader blockHeader, final Transaction transaction, final WorldUpdater publicWorldStateUpdater) {
final TransactionValidationParams transactionValidationParams = TransactionValidationParams.processingBlock();
final MainnetTransactionValidator transactionValidator = transactionProcessor.getTransactionValidator();
ValidationResult<TransactionInvalidReason> validationResult = transactionValidator.validate(transaction, blockHeader.getBaseFee(), transactionValidationParams);
if (!validationResult.isValid()) {
return validationResult;
}
final Address senderAddress = transaction.getSender();
final EvmAccount sender = publicWorldStateUpdater.getOrCreate(senderAddress);
validationResult = transactionValidator.validateForSender(transaction, sender, transactionValidationParams);
return validationResult;
}
Aggregations