@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.WorldUpdater ;
import org.hyperledger.besu.ethereum.core.WorldUpdater ;
import org.hyperledger.besu.ethereum.core.fees.CoinbaseFeePriceCalculator ;
import org.hyperledger.besu.ethereum.core.fees.CoinbaseFeePriceCalculator ;
import org.hyperledger.besu.ethereum.core.fees.TransactionPriceCalculator ;
import org.hyperledger.besu.ethereum.core.fees.TransactionPriceCalculator ;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason ;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup ;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup ;
import org.hyperledger.besu.ethereum.vm.Code ;
import org.hyperledger.besu.ethereum.vm.Code ;
import org.hyperledger.besu.ethereum.vm.GasCalculator ;
import org.hyperledger.besu.ethereum.vm.GasCalculator ;
@ -208,193 +209,204 @@ public class MainnetTransactionProcessor implements TransactionProcessor {
final BlockHashLookup blockHashLookup ,
final BlockHashLookup blockHashLookup ,
final Boolean isPersistingPrivateState ,
final Boolean isPersistingPrivateState ,
final TransactionValidationParams transactionValidationParams ) {
final TransactionValidationParams transactionValidationParams ) {
LOG . trace ( "Starting execution of {}" , transaction ) ;
try {
LOG . trace ( "Starting execution of {}" , transaction ) ;
ValidationResult < TransactionValidator . TransactionInvalidReason > validationResult =
transactionValidator . validate ( transaction , blockHeader . getBaseFee ( ) ) ;
ValidationResult < TransactionValidator . TransactionInvalidReason > validationResult =
// Make sure the transaction is intrinsically valid before trying to
transactionValidator . validate ( transaction , blockHeader . getBaseFee ( ) ) ;
// compare against a sender account (because the transaction may not
// Make sure the transaction is intrinsically valid before trying to
// be signed correctly to extract the sender).
// compare against a sender account (because the transaction may not
if ( ! validationResult . isValid ( ) ) {
// be signed correctly to extract the sender).
LOG . warn ( "Invalid transaction: {}" , validationResult . getErrorMessage ( ) ) ;
if ( ! validationResult . isValid ( ) ) {
return Result . invalid ( validationResult ) ;
LOG . warn ( "Invalid transaction: {}" , validationResult . getErrorMessage ( ) ) ;
}
return Result . invalid ( validationResult ) ;
}
final Address senderAddress = transaction . getSender ( ) ;
final Address senderAddress = transaction . getSender ( ) ;
final DefaultEvmAccount sender = worldState . getOrCreate ( senderAddress ) ;
final DefaultEvmAccount sender = worldState . getOrCreate ( senderAddress ) ;
validationResult =
validationResult =
transactionValidator . validateForSender ( transaction , sender , transactionValidationParams ) ;
transactionValidator . validateForSender ( transaction , sender , transactionValidationParams ) ;
if ( ! validationResult . isValid ( ) ) {
if ( ! validationResult . isValid ( ) ) {
LOG . debug ( "Invalid transaction: {}" , validationResult . getErrorMessage ( ) ) ;
LOG . debug ( "Invalid transaction: {}" , validationResult . getErrorMessage ( ) ) ;
return Result . invalid ( validationResult ) ;
return Result . invalid ( validationResult ) ;
}
}
final MutableAccount senderMutableAccount = sender . getMutable ( ) ;
final MutableAccount senderMutableAccount = sender . getMutable ( ) ;
final long previousNonce = senderMutableAccount . incrementNonce ( ) ;
final long previousNonce = senderMutableAccount . incrementNonce ( ) ;
final Wei transactionGasPrice =
final Wei transactionGasPrice =
transactionPriceCalculator . price ( transaction , blockHeader . getBaseFee ( ) ) ;
transactionPriceCalculator . price ( transaction , blockHeader . getBaseFee ( ) ) ;
LOG . trace (
LOG . trace (
"Incremented sender {} nonce ({} -> {})" , senderAddress , previousNonce , sender . getNonce ( ) ) ;
"Incremented sender {} nonce ({} -> {})" ,
senderAddress ,
final Wei upfrontGasCost = transaction . getUpfrontGasCost ( transactionGasPrice ) ;
previousNonce ,
final Wei previousBalance = senderMutableAccount . decrementBalance ( upfrontGasCost ) ;
sender . getNonce ( ) ) ;
LOG . trace (
"Deducted sender {} upfront gas cost {} ({} -> {})" ,
senderAddress ,
upfrontGasCost ,
previousBalance ,
sender . getBalance ( ) ) ;
final Gas intrinsicGas = gasCalculator . transactionIntrinsicGasCost ( transaction ) ;
final Gas gasAvailable = Gas . of ( transaction . getGasLimit ( ) ) . minus ( intrinsicGas ) ;
LOG . trace (
"Gas available for execution {} = {} - {} (limit - intrinsic)" ,
gasAvailable ,
transaction . getGasLimit ( ) ,
intrinsicGas ) ;
final WorldUpdater worldUpdater = worldState . updater ( ) ;
final MessageFrame initialFrame ;
final Deque < MessageFrame > messageFrameStack = new ArrayDeque < > ( ) ;
final ReturnStack returnStack = new ReturnStack ( ) ;
if ( transaction . isContractCreation ( ) ) {
final Address contractAddress =
Address . contractAddress ( senderAddress , sender . getNonce ( ) - 1L ) ;
initialFrame =
MessageFrame . builder ( )
. type ( MessageFrame . Type . CONTRACT_CREATION )
. messageFrameStack ( messageFrameStack )
. returnStack ( returnStack )
. blockchain ( blockchain )
. worldState ( worldUpdater . updater ( ) )
. initialGas ( gasAvailable )
. address ( contractAddress )
. originator ( senderAddress )
. contract ( contractAddress )
. contractAccountVersion ( createContractAccountVersion )
. gasPrice ( transactionGasPrice )
. inputData ( Bytes . EMPTY )
. sender ( senderAddress )
. value ( transaction . getValue ( ) )
. apparentValue ( transaction . getValue ( ) )
. code ( new Code ( transaction . getPayload ( ) ) )
. blockHeader ( blockHeader )
. depth ( 0 )
. completer ( c - > { } )
. miningBeneficiary ( miningBeneficiary )
. blockHashLookup ( blockHashLookup )
. isPersistingPrivateState ( isPersistingPrivateState )
. maxStackSize ( maxStackSize )
. transactionHash ( transaction . getHash ( ) )
. build ( ) ;
} else {
final Address to = transaction . getTo ( ) . get ( ) ;
final Account contract = worldState . get ( to ) ;
initialFrame =
MessageFrame . builder ( )
. type ( MessageFrame . Type . MESSAGE_CALL )
. messageFrameStack ( messageFrameStack )
. returnStack ( returnStack )
. blockchain ( blockchain )
. worldState ( worldUpdater . updater ( ) )
. initialGas ( gasAvailable )
. address ( to )
. originator ( senderAddress )
. contract ( to )
. contractAccountVersion (
contract ! = null ? contract . getVersion ( ) : Account . DEFAULT_VERSION )
. gasPrice ( transactionGasPrice )
. inputData ( transaction . getPayload ( ) )
. sender ( senderAddress )
. value ( transaction . getValue ( ) )
. apparentValue ( transaction . getValue ( ) )
. code ( new Code ( contract ! = null ? contract . getCode ( ) : Bytes . EMPTY ) )
. blockHeader ( blockHeader )
. depth ( 0 )
. completer ( c - > { } )
. miningBeneficiary ( miningBeneficiary )
. blockHashLookup ( blockHashLookup )
. maxStackSize ( maxStackSize )
. isPersistingPrivateState ( isPersistingPrivateState )
. transactionHash ( transaction . getHash ( ) )
. build ( ) ;
}
messageFrameStack . addFirst ( initialFrame ) ;
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 Gas intrinsicGas = gasCalculator . transactionIntrinsicGasCost ( transaction ) ;
final Gas gasAvailable = Gas . of ( transaction . getGasLimit ( ) ) . minus ( intrinsicGas ) ;
LOG . trace (
"Gas available for execution {} = {} - {} (limit - intrinsic)" ,
gasAvailable ,
transaction . getGasLimit ( ) ,
intrinsicGas ) ;
final WorldUpdater worldUpdater = worldState . updater ( ) ;
final MessageFrame initialFrame ;
final Deque < MessageFrame > messageFrameStack = new ArrayDeque < > ( ) ;
final ReturnStack returnStack = new ReturnStack ( ) ;
if ( transaction . isContractCreation ( ) ) {
final Address contractAddress =
Address . contractAddress ( senderAddress , sender . getNonce ( ) - 1L ) ;
initialFrame =
MessageFrame . builder ( )
. type ( MessageFrame . Type . CONTRACT_CREATION )
. messageFrameStack ( messageFrameStack )
. returnStack ( returnStack )
. blockchain ( blockchain )
. worldState ( worldUpdater . updater ( ) )
. initialGas ( gasAvailable )
. address ( contractAddress )
. originator ( senderAddress )
. contract ( contractAddress )
. contractAccountVersion ( createContractAccountVersion )
. gasPrice ( transactionGasPrice )
. inputData ( Bytes . EMPTY )
. sender ( senderAddress )
. value ( transaction . getValue ( ) )
. apparentValue ( transaction . getValue ( ) )
. code ( new Code ( transaction . getPayload ( ) ) )
. blockHeader ( blockHeader )
. depth ( 0 )
. completer ( c - > { } )
. miningBeneficiary ( miningBeneficiary )
. blockHashLookup ( blockHashLookup )
. isPersistingPrivateState ( isPersistingPrivateState )
. maxStackSize ( maxStackSize )
. transactionHash ( transaction . getHash ( ) )
. build ( ) ;
} else {
final Address to = transaction . getTo ( ) . get ( ) ;
final Account contract = worldState . get ( to ) ;
initialFrame =
MessageFrame . builder ( )
. type ( MessageFrame . Type . MESSAGE_CALL )
. messageFrameStack ( messageFrameStack )
. returnStack ( returnStack )
. blockchain ( blockchain )
. worldState ( worldUpdater . updater ( ) )
. initialGas ( gasAvailable )
. address ( to )
. originator ( senderAddress )
. contract ( to )
. contractAccountVersion (
contract ! = null ? contract . getVersion ( ) : Account . DEFAULT_VERSION )
. gasPrice ( transactionGasPrice )
. inputData ( transaction . getPayload ( ) )
. sender ( senderAddress )
. value ( transaction . getValue ( ) )
. apparentValue ( transaction . getValue ( ) )
. code ( new Code ( contract ! = null ? contract . getCode ( ) : Bytes . EMPTY ) )
. blockHeader ( blockHeader )
. depth ( 0 )
. completer ( c - > { } )
. miningBeneficiary ( miningBeneficiary )
. blockHashLookup ( blockHashLookup )
. maxStackSize ( maxStackSize )
. isPersistingPrivateState ( isPersistingPrivateState )
. transactionHash ( transaction . getHash ( ) )
. build ( ) ;
}
while ( ! messageFrameStack . isEmpty ( ) ) {
messageFrameStack . addFirst ( initialFrame ) ;
process ( messageFrameStack . peekFirst ( ) , operationTracer ) ;
}
if ( initialFrame . getState ( ) = = MessageFrame . State . COMPLETED_SUCCESS ) {
while ( ! messageFrameStack . isEmpty ( ) ) {
worldUpdater . commit ( ) ;
process ( messageFrameStack . peekFirst ( ) , operationTracer ) ;
}
}
if ( LOG . isTraceEnabled ( ) ) {
if ( initialFrame . getState ( ) = = MessageFrame . State . COMPLETED_SUCCESS ) {
LOG . trace (
worldUpdater . commit ( ) ;
"Gas used by transaction: {}, by message call/contract creation: {}" ,
}
( ) - > Gas . of ( transaction . getGasLimit ( ) ) . minus ( initialFrame . getRemainingGas ( ) ) ,
( ) - > gasAvailable . minus ( initialFrame . getRemainingGas ( ) ) ) ;
}
// Refund the sender by what we should and pay the miner fee (note that we're doing them one
if ( LOG . isTraceEnabled ( ) ) {
// after the other so that if it is the same account somehow, we end up with the right result)
LOG . trace (
final Gas selfDestructRefund =
"Gas used by transaction: {}, by message call/contract creation: {}" ,
gasCalculator . getSelfDestructRefundAmount ( ) . times ( initialFrame . getSelfDestructs ( ) . size ( ) ) ;
( ) - > Gas . of ( transaction . getGasLimit ( ) ) . minus ( initialFrame . getRemainingGas ( ) ) ,
final Gas refundGas = initialFrame . getGasRefund ( ) . plus ( selfDestructRefund ) ;
( ) - > gasAvailable . minus ( initialFrame . getRemainingGas ( ) ) ) ;
final Gas refunded = refunded ( transaction , initialFrame . getRemainingGas ( ) , refundGas ) ;
final Wei refundedWei = refunded . priceFor ( transactionGasPrice ) ;
senderMutableAccount . incrementBalance ( refundedWei ) ;
final Gas gasUsedByTransaction =
Gas . of ( transaction . getGasLimit ( ) ) . minus ( initialFrame . getRemainingGas ( ) ) ;
final MutableAccount coinbase = worldState . getOrCreate ( miningBeneficiary ) . getMutable ( ) ;
final Gas coinbaseFee = Gas . of ( transaction . getGasLimit ( ) ) . minus ( refunded ) ;
if ( blockHeader . getBaseFee ( ) . isPresent ( ) & & transaction . isEIP1559Transaction ( ) ) {
final Wei baseFee = Wei . of ( blockHeader . getBaseFee ( ) . get ( ) ) ;
if ( transactionGasPrice . compareTo ( baseFee ) < 0 ) {
return Result . failed (
gasUsedByTransaction . toLong ( ) ,
refunded . toLong ( ) ,
ValidationResult . invalid (
TransactionValidator . TransactionInvalidReason . TRANSACTION_PRICE_TOO_LOW ,
"transaction price must be greater than base fee" ) ,
Optional . empty ( ) ) ;
}
}
}
final CoinbaseFeePriceCalculator coinbaseCreditService =
transaction . isFrontierTransaction ( )
? CoinbaseFeePriceCalculator . frontier ( )
: coinbaseFeePriceCalculator ;
final Wei coinbaseWeiDelta =
coinbaseCreditService . price ( coinbaseFee , transactionGasPrice , blockHeader . getBaseFee ( ) ) ;
coinbase . incrementBalance ( coinbaseWeiDelta ) ;
// 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 Gas selfDestructRefund =
gasCalculator . getSelfDestructRefundAmount ( ) . times ( initialFrame . getSelfDestructs ( ) . size ( ) ) ;
final Gas refundGas = initialFrame . getGasRefund ( ) . plus ( selfDestructRefund ) ;
final Gas refunded = refunded ( transaction , initialFrame . getRemainingGas ( ) , refundGas ) ;
final Wei refundedWei = refunded . priceFor ( transactionGasPrice ) ;
senderMutableAccount . incrementBalance ( refundedWei ) ;
final Gas gasUsedByTransaction =
Gas . of ( transaction . getGasLimit ( ) ) . minus ( initialFrame . getRemainingGas ( ) ) ;
final MutableAccount coinbase = worldState . getOrCreate ( miningBeneficiary ) . getMutable ( ) ;
final Gas coinbaseFee = Gas . of ( transaction . getGasLimit ( ) ) . minus ( refunded ) ;
if ( blockHeader . getBaseFee ( ) . isPresent ( ) & & transaction . isEIP1559Transaction ( ) ) {
final Wei baseFee = Wei . of ( blockHeader . getBaseFee ( ) . get ( ) ) ;
if ( transactionGasPrice . compareTo ( baseFee ) < 0 ) {
return Result . failed (
gasUsedByTransaction . toLong ( ) ,
refunded . toLong ( ) ,
ValidationResult . invalid (
TransactionValidator . TransactionInvalidReason . TRANSACTION_PRICE_TOO_LOW ,
"transaction price must be greater than base fee" ) ,
Optional . empty ( ) ) ;
}
}
final CoinbaseFeePriceCalculator coinbaseCreditService =
transaction . isFrontierTransaction ( )
? CoinbaseFeePriceCalculator . frontier ( )
: coinbaseFeePriceCalculator ;
final Wei coinbaseWeiDelta =
coinbaseCreditService . price ( coinbaseFee , transactionGasPrice , blockHeader . getBaseFee ( ) ) ;
initialFrame . getSelfDestructs ( ) . forEach ( worldState : : deleteAccount ) ;
coinbase . incrementBalance ( coinbaseWeiDelta ) ;
if ( clearEmptyAccounts ) {
initialFrame . getSelfDestructs ( ) . forEach ( worldState : : deleteAccount ) ;
clearEmptyAccounts ( worldState ) ;
}
if ( initialFrame . getState ( ) = = MessageFrame . State . COMPLETED_SUCCESS ) {
if ( clearEmptyAccounts ) {
return Result . successful (
clearEmptyAccounts ( worldState ) ;
initialFrame . getLogs ( ) ,
}
gasUsedByTransaction . toLong ( ) ,
refunded . toLong ( ) ,
if ( initialFrame . getState ( ) = = MessageFrame . State . COMPLETED_SUCCESS ) {
initialFrame . getOutputData ( ) ,
return Result . successful (
validationResult ) ;
initialFrame . getLogs ( ) ,
} else {
gasUsedByTransaction . toLong ( ) ,
return Result . failed (
refunded . toLong ( ) ,
gasUsedByTransaction . toLong ( ) ,
initialFrame . getOutputData ( ) ,
refunded . toLong ( ) ,
validationResult ) ;
validationResult ,
} else {
initialFrame . getRevertReason ( ) ) ;
return Result . failed (
gasUsedByTransaction . toLong ( ) ,
refunded . toLong ( ) ,
validationResult ,
initialFrame . getRevertReason ( ) ) ;
}
} catch ( final RuntimeException re ) {
LOG . error ( "Critical Exception Processing Transaction" , re ) ;
return Result . invalid (
ValidationResult . invalid (
TransactionInvalidReason . INTERNAL_ERROR ,
"Internal Error in Besu - " + re . toString ( ) ) ) ;
}
}
}
}