Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM

vmRunner := b.blockchain.NewEVMRunner(block.Header(), statedb)

return core.NewStateTransition(vmenv, msg, gaspool, vmRunner).TransitionDb()
return core.NewStateTransition(vmenv, msg, gaspool, vmRunner, core.NewBlockContext(vmRunner)).TransitionDb()
}

// SendTransaction updates the pending block to include the given transaction.
Expand Down
5 changes: 3 additions & 2 deletions cmd/geth/retesteth.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ func (api *RetestethAPI) mineBlock() error {
&header.GasUsed,
*api.blockchain.GetVMConfig(),
vmRunner,
core.NewBlockContext(vmRunner),
)
if err != nil {
statedb.RevertToSnapshot(snap)
Expand Down Expand Up @@ -659,7 +660,7 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
vmRunner := api.blockchain.NewEVMRunner(block.Header(), statedb)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner); err != nil {
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner, core.NewBlockContext(vmRunner)); err != nil {
return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
Expand Down Expand Up @@ -770,7 +771,7 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
// Not yet the searched for transaction, execute on top of the current state
vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
vmRunner := api.blockchain.NewEVMRunner(block.Header(), statedb)
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner); err != nil {
if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()), vmRunner, core.NewBlockContext(vmRunner)); err != nil {
return StorageRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
}
// Ensure any modifications are committed to the state
Expand Down
45 changes: 43 additions & 2 deletions core/block_context.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package core

import (
"math/big"

"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/contracts/blockchain_parameters"
"github.com/celo-org/celo-blockchain/contracts/currency"
gpm "github.com/celo-org/celo-blockchain/contracts/gasprice_minimum"
"github.com/celo-org/celo-blockchain/core/vm"
)

Expand All @@ -12,14 +15,20 @@ import (
type BlockContext struct {
whitelistedCurrencies map[common.Address]struct{}
gasForAlternativeCurrency uint64

// gasPriceMinimums stores values for whitelist currency under their currency addresses
// Note that native currency(CELO) is under common.ZeroAddress
gasPriceMinimums map[common.Address]*big.Int
}

// NewBlockContext creates a block context for a given block (represented by the
// header & state).
// state MUST be pointing to header's stateRoot
func NewBlockContext(vmRunner vm.EVMRunner) BlockContext {
func NewBlockContext(vmRunner vm.EVMRunner) *BlockContext {
// gasForAlternativeCurrency
gasForAlternativeCurrency := blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(vmRunner)

// whitelistedCurrencies
whitelistedCurrenciesArr, err := currency.CurrencyWhitelist(vmRunner)
if err != nil {
whitelistedCurrenciesArr = []common.Address{}
Expand All @@ -30,9 +39,20 @@ func NewBlockContext(vmRunner vm.EVMRunner) BlockContext {
whitelistedCurrencies[currency] = struct{}{}
}

return BlockContext{
// gasPriceMinimums
gasPriceMinimums := make(map[common.Address]*big.Int)

nativeTokenGpm, _ := gpm.GetGasPriceMinimum(vmRunner, nil)
gasPriceMinimums[common.ZeroAddress] = nativeTokenGpm

for currency := range whitelistedCurrencies {
gasPriceMinimums[currency], _ = gpm.GetGasPriceMinimum(vmRunner, &currency)
}

return &BlockContext{
whitelistedCurrencies: whitelistedCurrencies,
gasForAlternativeCurrency: gasForAlternativeCurrency,
gasPriceMinimums: gasPriceMinimums,
}
}

Expand All @@ -51,3 +71,24 @@ func (bc *BlockContext) IsWhitelisted(feeCurrency *common.Address) bool {
_, ok := bc.whitelistedCurrencies[*feeCurrency]
return ok
}

// GetGasPriceMinimum gets the gasPriceMinimum given the address
// - if the input address is nil or common.ZeroAddress, return value for native currency(CELO)
// - otherwise it returns value based on address
func (bc *BlockContext) GetGasPriceMinimum(address *common.Address) (gasPriceMinimum *big.Int) {
if address == nil || *address == common.ZeroAddress {
return bc.gasPriceMinimums[common.ZeroAddress]
}
return bc.gasPriceMinimums[*address]
}

// WithZeroGasPriceMinimum returns a new BlockContext with GasPriceMinimums set to zero
// One use case is core.ApplyMessageWithoutGasPriceMinimum
func (bc *BlockContext) WithZeroGasPriceMinimum() *BlockContext{
cpy := *bc
for address := range cpy.gasPriceMinimums {
bc.gasPriceMinimums[address] = common.Big0
}

return &cpy
}
2 changes: 1 addition & 1 deletion core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (b *BlockGen) AddTxWithChain(bc ChainContext, tx *types.Transaction) {
b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs))

celoMock := testutil.NewCeloMock()
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, celoMock.Runner)
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vm.Config{}, celoMock.Runner, NewBlockContext(celoMock.Runner))
if err != nil {
panic(err)
}
Expand Down
3 changes: 2 additions & 1 deletion core/state_prefetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func precacheTransaction(config *params.ChainConfig, bc *BlockChain, author *com
ctx := NewEVMContext(msg, header, bc, author)
vm := vm.NewEVM(ctx, statedb, config, cfg)

_, err = ApplyMessage(vm, msg, gaspool, bc.NewEVMRunner(header, statedb))
evmRunner := bc.NewEVMRunner(header, statedb)
_, err = ApplyMessage(vm, msg, gaspool, evmRunner, NewBlockContext(evmRunner))
return err
}
20 changes: 17 additions & 3 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package core

import (
"fmt"

"github.com/celo-org/celo-blockchain/common"
"github.com/celo-org/celo-blockchain/consensus"
"github.com/celo-org/celo-blockchain/consensus/misc"
Expand Down Expand Up @@ -81,10 +83,22 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// always true (EIP158)
statedb.IntermediateRoot(true)
}

// After E hard fork, create BlockContext based on parent block
var blockContext *BlockContext
if p.bc.chainConfig.IsEHardfork(block.Number()) {
parentHeader := p.bc.GetHeaderByNumber(block.NumberU64() - 1)
parentState, err := p.bc.StateAt(parentHeader.Root)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to get the parent state: %w", err)
}
blockContext = NewBlockContext(p.bc.NewEVMRunner(parentHeader, parentState))
}

// Iterate over and process the individual transactions
for i, tx := range block.Transactions() {
statedb.Prepare(tx.Hash(), block.Hash(), i)
receipt, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg, vmRunner)
receipt, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg, vmRunner, blockContext)
if err != nil {
return nil, nil, 0, err
}
Expand All @@ -104,7 +118,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
// and uses the input parameters for its environment. It returns the receipt
// for the transaction, gas used and an error if the transaction failed,
// indicating the block was invalid.
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, txFeeRecipient *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, vmRunner vm.EVMRunner) (*types.Receipt, error) {
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, txFeeRecipient *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, vmRunner vm.EVMRunner, blockContext *BlockContext) (*types.Receipt, error) {
if config.IsDonut(header.Number) && !tx.Protected() {
return nil, ErrUnprotectedTransaction
}
Expand All @@ -120,7 +134,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, txFeeRecipien
vmenv := vm.NewEVM(ctx, statedb, config, cfg)

// Apply the transaction to the current state (included in the env)
result, err := ApplyMessage(vmenv, msg, gp, vmRunner)
result, err := ApplyMessage(vmenv, msg, gp, vmRunner, blockContext)
if err != nil {
return nil, err
}
Expand Down
50 changes: 38 additions & 12 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type StateTransition struct {
evm *vm.EVM
vmRunner vm.EVMRunner
gasPriceMinimum *big.Int
blockContext *BlockContext
}

// Message represents a message sent to a contract.
Expand Down Expand Up @@ -190,8 +191,13 @@ func IntrinsicGas(data []byte, contractCreation bool, feeCurrency *common.Addres
}

// NewStateTransition initialises and returns a new state transition object.
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner) *StateTransition {
gasPriceMinimum, _ := gpm.GetGasPriceMinimum(vmRunner, msg.FeeCurrency())
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner, blockContext *BlockContext) *StateTransition {
// - Prior to E hard fork, it does a contract call to get gasPriceMinimum
// - After E hard fork, it uses BlockContext
var gasPriceMinimum *big.Int
if !evm.ChainConfig().IsEHardfork(evm.BlockNumber) {
gasPriceMinimum, _ = gpm.GetGasPriceMinimum(vmRunner, msg.FeeCurrency())
}

return &StateTransition{
gp: gp,
Expand All @@ -203,6 +209,7 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRu
data: msg.Data(),
state: evm.StateDB,
gasPriceMinimum: gasPriceMinimum,
blockContext: blockContext,
}
}

Expand All @@ -213,18 +220,21 @@ func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRu
// the gas used (which includes gas refunds) and an error if it failed. An error always
// indicates a core error meaning that the message would always fail for that particular
// state and would never be accepted within a block.
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner) (*ExecutionResult, error) {
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner, blockContext *BlockContext) (*ExecutionResult, error) {
log.Trace("Applying state transition message", "from", msg.From(), "nonce", msg.Nonce(), "to", msg.To(), "gas price", msg.GasPrice(), "fee currency", msg.FeeCurrency(), "gateway fee recipient", msg.GatewayFeeRecipient(), "gateway fee", msg.GatewayFee(), "gas", msg.Gas(), "value", msg.Value(), "data", msg.Data())
return NewStateTransition(evm, msg, gp, vmRunner).TransitionDb()
return NewStateTransition(evm, msg, gp, vmRunner, blockContext).TransitionDb()
}

// ApplyMessageWithoutGasPriceMinimum applies the given message with the gas price minimum
// set to zero. It's only for use in eth_call and eth_estimateGas, so that they can be used
// with gas price set to zero if the sender doesn't have funds to pay for gas.
// Returns the gas used (which does not include gas refunds) and an error if it failed.
func ApplyMessageWithoutGasPriceMinimum(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner) (*ExecutionResult, error) {
func ApplyMessageWithoutGasPriceMinimum(evm *vm.EVM, msg Message, gp *GasPool, vmRunner vm.EVMRunner, blockContext *BlockContext) (*ExecutionResult, error) {
log.Trace("Applying state transition message without gas price minimum", "from", msg.From(), "nonce", msg.Nonce(), "to", msg.To(), "fee currency", msg.FeeCurrency(), "gateway fee recipient", msg.GatewayFeeRecipient(), "gateway fee", msg.GatewayFee(), "gas limit", msg.Gas(), "value", msg.Value(), "data", msg.Data())
st := NewStateTransition(evm, msg, gp, vmRunner)
if evm.ChainConfig().IsEHardfork(evm.BlockNumber) {
blockContext = blockContext.WithZeroGasPriceMinimum()
}
st := NewStateTransition(evm, msg, gp, vmRunner, blockContext)
st.gasPriceMinimum = common.Big0
return st.TransitionDb()
}
Expand Down Expand Up @@ -361,8 +371,14 @@ func (st *StateTransition) preCheck() error {
}

// Make sure this transaction's gas price is valid.
if st.gasPrice.Cmp(st.gasPriceMinimum) < 0 {
log.Debug("Tx gas price is less than minimum", "minimum", st.gasPriceMinimum, "price", st.gasPrice)
var gpm *big.Int
if !st.evm.ChainConfig().IsEHardfork(st.evm.BlockNumber) {
gpm = st.gasPriceMinimum
} else {
gpm = st.blockContext.GetGasPriceMinimum(st.msg.FeeCurrency())
}
if st.gasPrice.Cmp(gpm) < 0 {
log.Debug("Tx gas price is less than minimum", "minimum", gpm, "price", st.gasPrice)
return ErrGasPriceDoesNotExceedMinimum
}

Expand Down Expand Up @@ -417,7 +433,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
gasForAlternativeCurrency := uint64(0)
// If the fee currency is nil, do not retrieve the intrinsic gas adjustment from the chain state, as it will not be used.
if msg.FeeCurrency() != nil {
gasForAlternativeCurrency = blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(st.vmRunner)
if !st.evm.ChainConfig().IsEHardfork(st.evm.BlockNumber) {
gasForAlternativeCurrency = blockchain_parameters.GetIntrinsicGasForAlternativeFeeCurrencyOrDefault(st.vmRunner)
} else {
gasForAlternativeCurrency = st.blockContext.GetIntrinsicGasForAlternativeFeeCurrency()
}
}
gas, err := IntrinsicGas(st.data, contractCreation, msg.FeeCurrency(), gasForAlternativeCurrency, istanbul)
if err != nil {
Expand Down Expand Up @@ -481,9 +501,15 @@ func (st *StateTransition) distributeTxFees() error {
from := st.msg.From()

// Divide the transaction into a base (the minimum transaction fee) and tip (any extra).
baseTxFee := new(big.Int).Mul(gasUsed, st.gasPriceMinimum)
tipTxFee := new(big.Int).Sub(totalTxFee, baseTxFee)
feeCurrency := st.msg.FeeCurrency()
var gpm *big.Int
if !st.evm.ChainConfig().IsEHardfork(st.evm.BlockNumber) {
gpm = st.gasPriceMinimum
} else {
gpm = st.blockContext.GetGasPriceMinimum(feeCurrency)
}
baseTxFee := new(big.Int).Mul(gasUsed, gpm)
tipTxFee := new(big.Int).Sub(totalTxFee, baseTxFee)

gatewayFeeRecipient := st.msg.GatewayFeeRecipient()
if gatewayFeeRecipient == nil {
Expand All @@ -502,7 +528,7 @@ func (st *StateTransition) distributeTxFees() error {
baseTxFee = new(big.Int)
}

log.Trace("distributeTxFees", "from", from, "refund", refund, "feeCurrency", st.msg.FeeCurrency(),
log.Trace("distributeTxFees", "from", from, "refund", refund, "feeCurrency", feeCurrency,
"gatewayFeeRecipient", *gatewayFeeRecipient, "gatewayFee", st.msg.GatewayFee(),
"coinbaseFeeRecipient", st.evm.Coinbase, "coinbaseFee", tipTxFee,
"comunityFundRecipient", governanceAddress, "communityFundFee", baseTxFee)
Expand Down
2 changes: 1 addition & 1 deletion core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
}

type txPoolContext struct {
BlockContext
*BlockContext
*currency.CurrencyManager
}

Expand Down
Loading