Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/celo10' into piersy/smoketest
Browse files Browse the repository at this point in the history
  • Loading branch information
piersy committed Nov 25, 2024
2 parents effa0fe + 4e467f1 commit acb90f1
Show file tree
Hide file tree
Showing 26 changed files with 216 additions and 94 deletions.
6 changes: 3 additions & 3 deletions core/celo_evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import (
"github.com/ethereum/go-ethereum/params"
)

func setCeloFieldsInBlockContext(blockContext *vm.BlockContext, header *types.Header, config *params.ChainConfig, statedb vm.StateDB) {
func GetFeeCurrencyContext(header *types.Header, config *params.ChainConfig, statedb vm.StateDB) *common.FeeCurrencyContext {
if !config.IsCel2(header.Time) {
return
return &common.FeeCurrencyContext{}
}

caller := &contracts.CeloBackend{ChainConfig: config, State: statedb}
Expand All @@ -20,7 +20,7 @@ func setCeloFieldsInBlockContext(blockContext *vm.BlockContext, header *types.He
if err != nil {
log.Error("Error fetching exchange rates!", "err", err)
}
blockContext.FeeCurrencyContext = feeCurrencyContext
return &feeCurrencyContext
}

func GetExchangeRates(header *types.Header, config *params.ChainConfig, statedb vm.StateDB) common.ExchangeRates {
Expand Down
22 changes: 15 additions & 7 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ import (
// BlockGen creates blocks for testing.
// See GenerateChain for a detailed explanation.
type BlockGen struct {
i int
cm *chainMaker
parent *types.Block
header *types.Header
statedb *state.StateDB
i int
cm *chainMaker
parent *types.Block
header *types.Header
statedb *state.StateDB
feeCurrencyContext *common.FeeCurrencyContext

gasPool *GasPool
txs []*types.Transaction
Expand All @@ -54,6 +55,10 @@ type BlockGen struct {
engine consensus.Engine
}

func (b *BlockGen) updateFeeCurrencyContext() {
b.feeCurrencyContext = GetFeeCurrencyContext(b.header, b.cm.config, b.statedb)
}

// SetCoinbase sets the coinbase of the generated block.
// It can be called at most once.
func (b *BlockGen) SetCoinbase(addr common.Address) {
Expand Down Expand Up @@ -99,7 +104,7 @@ func (b *BlockGen) Difficulty() *big.Int {
func (b *BlockGen) SetParentBeaconRoot(root common.Hash) {
b.header.ParentBeaconRoot = &root
var (
blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase, b.cm.config, b.statedb)
blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase, b.cm.config, b.statedb, b.feeCurrencyContext)
vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{})
)
ProcessBeaconBlockRoot(root, vmenv, b.statedb)
Expand All @@ -117,7 +122,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
b.SetCoinbase(common.Address{})
}
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(b.cm.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig)
receipt, err := ApplyTransaction(b.cm.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig, b.feeCurrencyContext)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -329,6 +334,8 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
b.header.Difficulty = big.NewInt(0)
}
}
b.updateFeeCurrencyContext()

// Mutate the state and block according to any hard-fork specs
if daoBlock := config.DAOForkBlock; daoBlock != nil {
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
Expand Down Expand Up @@ -430,6 +437,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
b.header = cm.makeHeader(parent, statedb, b.engine)
b.updateFeeCurrencyContext()

// TODO uncomment when proof generation is merged
// Save pre state for proof generation
Expand Down
6 changes: 4 additions & 2 deletions core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type ChainContext interface {
}

// NewEVMBlockContext creates a new context for use in the EVM.
func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address, config *params.ChainConfig, statedb vm.StateDB) vm.BlockContext {
func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common.Address, config *params.ChainConfig, statedb vm.StateDB, feeCurrencyContext *common.FeeCurrencyContext) vm.BlockContext {
var (
beneficiary common.Address
baseFee *big.Int
Expand Down Expand Up @@ -78,7 +78,9 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
L1CostFunc: types.NewL1CostFunc(config, statedb),
}

setCeloFieldsInBlockContext(&blockContext, header, config, statedb)
if config.IsCel2(header.Time) {
blockContext.FeeCurrencyContext = *feeCurrencyContext
}

return blockContext
}
Expand Down
11 changes: 6 additions & 5 deletions core/state_prefetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,12 @@ func newStatePrefetcher(config *params.ChainConfig, chain *HeaderChain) *statePr
// only goal is to pre-cache transaction signatures and state trie nodes.
func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, cfg vm.Config, interrupt *atomic.Bool) {
var (
header = block.Header()
gaspool = new(GasPool).AddGas(block.GasLimit())
blockContext = NewEVMBlockContext(header, p.chain, nil, p.config, statedb)
evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
signer = types.MakeSigner(p.config, header.Number, header.Time)
header = block.Header()
gaspool = new(GasPool).AddGas(block.GasLimit())
feeCurrencyContext = GetFeeCurrencyContext(header, p.config, statedb)
blockContext = NewEVMBlockContext(header, p.chain, nil, p.config, statedb, feeCurrencyContext)
evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
signer = types.MakeSigner(p.config, header.Number, header.Time)
)
// Iterate over and process the individual transactions
byzantium := p.config.IsByzantium(block.Number())
Expand Down
11 changes: 6 additions & 5 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
context vm.BlockContext
signer = types.MakeSigner(p.config, header.Number, header.Time)
)
context = NewEVMBlockContext(header, p.chain, nil, p.config, statedb)
feeCurrencyContext := GetFeeCurrencyContext(header, p.config, statedb)
context = NewEVMBlockContext(header, p.chain, nil, p.config, statedb, feeCurrencyContext)
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
Expand Down Expand Up @@ -197,13 +198,13 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
// 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, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author, config, statedb)
msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee, blockContext.FeeCurrencyContext.ExchangeRates)
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, feeCurrencyContext *common.FeeCurrencyContext) (*types.Receipt, error) {
msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee, feeCurrencyContext.ExchangeRates)
if err != nil {
return nil, err
}
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author, config, statedb, feeCurrencyContext)
txContext := NewEVMTxContext(msg)
vmenv := vm.NewEVM(blockContext, txContext, statedb, config, cfg)
return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
Expand Down
43 changes: 28 additions & 15 deletions core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,8 @@ func (st *StateTransition) buyGas() error {
balanceCheck.SetUint64(st.msg.GasLimit)
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap)
}
balanceCheck.Add(balanceCheck, st.msg.Value)
// Moved to canPayFee
// balanceCheck.Add(balanceCheck, st.msg.Value)
if l1Cost != nil {
balanceCheck.Add(balanceCheck, l1Cost)
}
Expand All @@ -333,12 +334,7 @@ func (st *StateTransition) buyGas() error {
mgval.Add(mgval, blobFee)
}
}
balanceCheckU256, overflow := uint256.FromBig(balanceCheck)
if overflow {
return fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex())
}

if err := st.canPayFee(balanceCheckU256); err != nil {
if err := st.canPayFee(balanceCheck); err != nil {
return err
}
if err := st.gp.SubGas(st.msg.GasLimit); err != nil {
Expand All @@ -356,14 +352,33 @@ func (st *StateTransition) buyGas() error {
}

// canPayFee checks whether accountOwner's balance can cover transaction fee.
func (st *StateTransition) canPayFee(checkAmount *uint256.Int) error {
func (st *StateTransition) canPayFee(checkAmountForGas *big.Int) error {
var checkAmountInCelo, checkAmountInAlternativeCurrency *big.Int
if st.msg.FeeCurrency == nil {
checkAmountInCelo = new(big.Int).Add(checkAmountForGas, st.msg.Value)
checkAmountInAlternativeCurrency = common.Big0
} else {
checkAmountInCelo = st.msg.Value
checkAmountInAlternativeCurrency = checkAmountForGas
}

if checkAmountInCelo.Cmp(common.Big0) > 0 {
balanceInCeloU256, overflow := uint256.FromBig(checkAmountInCelo)
if overflow {
return fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex())
}

balance := st.state.GetBalance(st.msg.From)

if balance.Cmp(checkAmount) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), balance, checkAmount)
if balance.Cmp(balanceInCeloU256) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), balance, checkAmountInCelo)
}
}
if checkAmountInAlternativeCurrency.Cmp(common.Big0) > 0 {
_, overflow := uint256.FromBig(checkAmountInAlternativeCurrency)
if overflow {
return fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex())
}
} else {
backend := &contracts.CeloBackend{
ChainConfig: st.evm.ChainConfig(),
State: st.state,
Expand All @@ -373,10 +388,8 @@ func (st *StateTransition) canPayFee(checkAmount *uint256.Int) error {
return err
}

// Token amount can't be bigger than 256 bit
balanceU256, _ := uint256.FromBig(balance)
if balanceU256.Cmp(checkAmount) < 0 {
return fmt.Errorf("%w: address %v have %v want %v, fee currency: %v", ErrInsufficientFunds, st.msg.From.Hex(), balance, checkAmount, st.msg.FeeCurrency.Hex())
if balance.Cmp(checkAmountInAlternativeCurrency) < 0 {
return fmt.Errorf("%w: address %v have %v want %v, fee currency: %v", ErrInsufficientFunds, st.msg.From.Hex(), balance, checkAmountInAlternativeCurrency, st.msg.FeeCurrency.Hex())
}
}
return nil
Expand Down
4 changes: 3 additions & 1 deletion e2e_test/debug-fee-currency/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ function cleanup_fee_currency() {
# $3: replaceTransaction (bool):
# replace the transaction with a transaction of higher priority-fee when
# there is no receipt after the `waitBlocks` time passed
# $4: value (num):
# value to send in the transaction
function cip_64_tx() {
$SCRIPT_DIR/js-tests/send_tx.mjs $1 $2 $3
$SCRIPT_DIR/js-tests/send_tx.mjs "$(cast chain-id)" $ACC_PRIVKEY $1 $2 $3 $4
}

# use this function to assert the cip_64_tx return value, by using a pipe like
Expand Down
10 changes: 7 additions & 3 deletions e2e_test/js-tests/send_tx.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
} from "viem";
import { publicClient, walletClient, account } from "./viem_setup.mjs"

const [feeCurrency, waitBlocks, replaceTxAfterWait] = process.argv.slice(2);
const [chainId, privateKey, feeCurrency, waitBlocks, replaceTxAfterWait, celoValue] = process.argv.slice(2);

function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
Expand Down Expand Up @@ -49,13 +49,17 @@ async function replaceTransaction(tx) {
}

async function main() {
let value = 2n
if (celoValue !== "") {
value = BigInt(celoValue)
}
const request = await walletClient.prepareTransactionRequest({
account,
to: "0x00000000000000000000000000000000DeaDBeef",
value: 2n,
value: value,
gas: 90000,
feeCurrency,
maxFeePerGas: 2000000000n,
maxFeePerGas: 25000000000n,
maxPriorityFeePerGas: 100n, // should be >= 1wei even after conversion to native tokens
});

Expand Down
4 changes: 2 additions & 2 deletions e2e_test/js-tests/test_viem_tx.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ describe("viem send tx", () => {
assert.equal(receipt.status, "success", "receipt status 'failure'");
}).timeout(10_000);

it("send overlapping nonce tx in different currencies", async () => {
it.skip("send overlapping nonce tx in different currencies", async () => {
const priceBump = 1.1;

const rate = await getRate(process.env.FEE_CURRENCY);
Expand Down Expand Up @@ -263,7 +263,7 @@ describe("viem send tx", () => {
}
}).timeout(10_000);

it("send fee currency tx with just high enough gas price", async () => {
it.skip("send fee currency tx with just high enough gas price", async () => {
// The idea of this test is to check that the fee currency is taken into
// account by the server. We do this by using a fee currency that has a
// value greater than celo, so that the base fee in fee currency becomes a
Expand Down
2 changes: 1 addition & 1 deletion e2e_test/run_all_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ for f in test_*"$TEST_GLOB"*; do
if [[ -n $NETWORK ]]; then
case $f in
# Skip tests that require a local network.
test_fee_currency_fails_on_credit.sh|test_fee_currency_fails_on_debit.sh|test_fee_currency_fails_intrinsic.sh)
test_fee_currency_fails_on_credit.sh|test_fee_currency_fails_on_debit.sh|test_fee_currency_fails_intrinsic.sh|test_value_and_fee_currency_balance_check.sh)
echo "skipping file $f"
continue
;;
Expand Down
4 changes: 2 additions & 2 deletions e2e_test/test_fee_currency_fails_intrinsic.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ trap 'kill %%' EXIT # kill bg tail job on exit
# trigger the first failed call to the CreditFees(), causing the
# currency to get temporarily blocklisted.
# initial tx should not succeed, should have required a replacement transaction.
cip_64_tx $fee_currency 1 true | assert_cip_64_tx false
cip_64_tx $fee_currency 1 true 2 | assert_cip_64_tx false

sleep 2

# since the fee currency is temporarily blocked,
# this should NOT make the transaction execute anymore,
# but invalidate the transaction earlier.
# initial tx should not succeed, should have required a replacement transaction.
cip_64_tx $fee_currency 1 true | assert_cip_64_tx false
cip_64_tx $fee_currency 1 true 2 | assert_cip_64_tx false

cleanup_fee_currency $fee_currency
)
Expand Down
4 changes: 2 additions & 2 deletions e2e_test/test_fee_currency_fails_on_credit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ trap 'kill %%' EXIT # kill bg tail job on exit
# trigger the first failed call to the CreditFees(), causing the
# currency to get temporarily blocklisted.
# initial tx should not succeed, should have required a replacement transaction.
cip_64_tx $fee_currency 1 true | assert_cip_64_tx false
cip_64_tx $fee_currency 1 true 2 | assert_cip_64_tx false

sleep 2

# since the fee currency is temporarily blocked,
# this should NOT make the transaction execute anymore,
# but invalidate the transaction earlier.
# initial tx should not succeed, should have required a replacement transaction.
cip_64_tx $fee_currency 1 true | assert_cip_64_tx false
cip_64_tx $fee_currency 1 true 2 | assert_cip_64_tx false

cleanup_fee_currency $fee_currency
)
Expand Down
2 changes: 1 addition & 1 deletion e2e_test/test_fee_currency_fails_on_debit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ source debug-fee-currency/lib.sh
#
fee_currency=$(deploy_fee_currency true false false)
# this fails during the RPC call, since the DebitFees() is part of the pre-validation
cip_64_tx $fee_currency 1 false | assert_cip_64_tx false "fee-currency internal error"
cip_64_tx $fee_currency 1 false 2 | assert_cip_64_tx false "fee-currency internal error"

cleanup_fee_currency $fee_currency
23 changes: 23 additions & 0 deletions e2e_test/test_value_and_fee_currency_balance_check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
#shellcheck disable=SC2086
set -eo pipefail
set -x

source shared.sh
source debug-fee-currency/lib.sh

TEST_ACCOUNT_ADDR=0xEa787f769d66B5C131319f262F07254790985BdC
TEST_ACCOUNT_PRIVKEY=0xd36ad839c0bc4bfd8c718a3219591a791871dafad2391149153e6abb43a777fd

fee_currency=$(deploy_fee_currency false false false)

# Send 2.5e15 fee currency to test account
cast send --private-key $ACC_PRIVKEY $fee_currency 'transfer(address to, uint256 value) returns (bool)' $TEST_ACCOUNT_ADDR 2500000000000000
# Send 1e18 celo to test account
cast send --private-key $ACC_PRIVKEY $TOKEN_ADDR 'transfer(address to, uint256 value) returns (bool)' $TEST_ACCOUNT_ADDR 1000000000000000000

# balanceFeeCurrency=2.5e15, txCost=2.25e15, balanceCelo=1e18, valueCelo=1e15
# this should succed because the value and the txCost should not be added (with the bug the total cost could be 3.25e15 will fail because the balanceFeeCurrency is 2.5e15)
$SCRIPT_DIR/js-tests/send_tx.mjs "$(cast chain-id)" $TEST_ACCOUNT_PRIVKEY $fee_currency 1 false 1000000000000000 | assert_cip_64_tx true ""

cleanup_fee_currency $fee_currency
3 changes: 2 additions & 1 deletion eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,8 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *st
if blockCtx != nil {
context = *blockCtx
} else {
context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil, b.eth.blockchain.Config(), state)
feeCurrencyContext := core.GetFeeCurrencyContext(header, b.eth.blockchain.Config(), state)
context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil, b.eth.blockchain.Config(), state, feeCurrencyContext)
}
return vm.NewEVM(context, txContext, state, b.ChainConfig(), *vmConfig)
}
Expand Down
5 changes: 3 additions & 2 deletions eth/gasestimator/gasestimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,9 @@ func execute(ctx context.Context, call *core.Message, opts *Options, gasLimit ui
func run(ctx context.Context, call *core.Message, opts *Options) (*core.ExecutionResult, error) {
// Assemble the call and the call context
var (
msgContext = core.NewEVMTxContext(call)
evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil, opts.Config, opts.State)
feeCurrencyContext = core.GetFeeCurrencyContext(opts.Header, opts.Config, opts.State)
msgContext = core.NewEVMTxContext(call)
evmContext = core.NewEVMBlockContext(opts.Header, opts.Chain, nil, opts.Config, opts.State, feeCurrencyContext)

dirtyState = opts.State.Copy()
evm = vm.NewEVM(evmContext, msgContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true})
Expand Down
Loading

0 comments on commit acb90f1

Please sign in to comment.