Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft of op-geth's tx pool adaptation to celo's #25

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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: 2 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,5 @@ func (d *Decimal) UnmarshalJSON(input []byte) error {
return err
}
}

type ExchangeRates = map[Address]*big.Rat
8 changes: 4 additions & 4 deletions core/celo_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import (
// CeloBackend provide a partial ContractBackend implementation, so that we can
// access core contracts during block processing.
type CeloBackend struct {
chainConfig *params.ChainConfig
state *state.StateDB
ChainConfig *params.ChainConfig
State *state.StateDB
}

// ContractCaller implementation

func (b *CeloBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
return b.state.GetCode(contract), nil
return b.State.GetCode(contract), nil
}

func (b *CeloBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
Expand All @@ -44,7 +44,7 @@ func (b *CeloBackend) CallContract(ctx context.Context, call ethereum.CallMsg, b
txCtx := vm.TxContext{}
vmConfig := vm.Config{}

evm := vm.NewEVM(blockCtx, txCtx, b.state, b.chainConfig, vmConfig)
evm := vm.NewEVM(blockCtx, txCtx, b.State, b.ChainConfig, vmConfig)
ret, _, err := evm.StaticCall(vm.AccountRef(evm.Origin), *call.To, call.Data, call.Gas)

return ret, err
Expand Down
4 changes: 2 additions & 2 deletions core/celo_evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
)

// Returns the exchange rates for all gas currencies from CELO
func getExchangeRates(caller *CeloBackend) (map[common.Address]*big.Rat, error) {
func GetExchangeRates(caller bind.ContractCaller) (common.ExchangeRates, error) {
exchangeRates := map[common.Address]*big.Rat{}
whitelist, err := abigen.NewFeeCurrencyWhitelistCaller(contracts.FeeCurrencyWhitelistAddress, caller)
if err != nil {
Expand Down Expand Up @@ -57,7 +57,7 @@ func setCeloFieldsInBlockContext(blockContext *vm.BlockContext, header *types.He

// Add fee currency exchange rates
var err error
blockContext.ExchangeRates, err = getExchangeRates(caller)
blockContext.ExchangeRates, err = GetExchangeRates(caller)
if err != nil {
log.Error("Error fetching exchange rates!", "err", err)
}
Expand Down
3 changes: 1 addition & 2 deletions core/txpool/blobpool/blobpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -1035,8 +1035,7 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
MaxSize: txMaxSize,
MinTip: p.gasTip.ToBig(),
}
var fcv txpool.FeeCurrencyValidator = nil // TODO: create with proper value
if err := txpool.CeloValidateTransaction(tx, p.head, p.signer, baseOpts, p.state, fcv); err != nil {
if err := txpool.CeloValidateTransaction(tx, p.head, p.signer, baseOpts, p.state, p.feeCurrencyValidator); err != nil {
return err
}
// Ensure the transaction adheres to the stateful pool filters (nonce, balance)
Expand Down
5 changes: 4 additions & 1 deletion core/txpool/celo_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ type FeeCurrencyValidator interface {
// Balance returns the feeCurrency balance of the address specified, in the given state.
// If feeCurrency is nil, the native currency balance has to be returned.
Balance(st *state.StateDB, address common.Address, feeCurrency *common.Address) *big.Int

// ToCurrencyValue
ToCurrencyValue(st *state.StateDB, fromNativeValue *big.Int, toFeeCurrency *common.Address) *big.Int
}

func NewFeeCurrencyValidator() FeeCurrencyValidator {
Expand Down Expand Up @@ -82,7 +85,7 @@ func CeloValidateTransaction(tx *types.Transaction, head *types.Header,
return err
}
if IsFeeCurrencyTx(tx) {
if !fcv.IsWhitelisted(st, tx.FeeCurrency()) {
if !fcv.IsWhitelisted(st, tx.FeeCurrency()) { // TODO: change to celoContext
return NonWhitelistedFeeCurrencyError
}
}
Expand Down
123 changes: 123 additions & 0 deletions core/txpool/legacypool/celo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package legacypool

import (
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/contracts/celo/abigen"
"github.com/ethereum/go-ethereum/core/state"
)

var (
unitRate = big.NewRat(1, 1)
)

// IsWhitelisted checks if a given fee currency is whitelisted
func IsWhitelisted(exchangeRates common.ExchangeRates, feeCurrency *common.Address) bool {
if feeCurrency == nil {
return true
}
_, ok := exchangeRates[*feeCurrency]
return ok
}

func TranslateValue(exchangeRates common.ExchangeRates, val *big.Int, fromFeeCurrency, toFeeCurrency *common.Address) (*big.Int, error) {
// TODO: implement me
return val, nil
}

func CurrencyBaseFee(exchangeRates common.ExchangeRates, feeCurrency *common.Address) *big.Int {
// TODO: implement me
return nil
}

func CurrencyBaseFeeAt(st *state.StateDB, feeCurrency *common.Address) *big.Int {
var exchangeRates common.ExchangeRates
return CurrencyBaseFee(exchangeRates, feeCurrency)
}

// Compares values in different currencies
// nil currency => native currency
func CompareValue(exchangeRates common.ExchangeRates, val1 *big.Int, feeCurrency1 *common.Address, val2 *big.Int, feeCurrency2 *common.Address) (int, error) {
// Short circuit if the fee currency is the same.
if areEqualAddresses(feeCurrency1, feeCurrency2) {
return val1.Cmp(val2), nil
}

var exchangeRate1, exchangeRate2 *big.Rat
var ok bool
if feeCurrency1 == nil {
exchangeRate1 = unitRate
} else {
exchangeRate1, ok = exchangeRates[*feeCurrency1]
if !ok {
return 0, fmt.Errorf("fee currency not registered: %s", feeCurrency1.Hex())
}
}

if feeCurrency2 == nil {
exchangeRate2 = unitRate
} else {
exchangeRate2, ok = exchangeRates[*feeCurrency2]
if !ok {
return 0, fmt.Errorf("fee currency not registered: %s", feeCurrency1.Hex())
}
}

// Below code block is basically evaluating this comparison:
// val1 * exchangeRate1.denominator / exchangeRate1.numerator < val2 * exchangeRate2.denominator / exchangeRate2.numerator
// It will transform that comparison to this, to remove having to deal with fractional values.
// val1 * exchangeRate1.denominator * exchangeRate2.numerator < val2 * exchangeRate2.denominator * exchangeRate1.numerator
leftSide := new(big.Int).Mul(
val1,
new(big.Int).Mul(
exchangeRate1.Denom(),
exchangeRate2.Num(),
),
)
rightSide := new(big.Int).Mul(
val2,
new(big.Int).Mul(
exchangeRate2.Denom(),
exchangeRate1.Num(),
),
)

return leftSide.Cmp(rightSide), nil
}

func CompareValueAt(st *state.StateDB, val1 *big.Int, curr1 *common.Address, val2 *big.Int, curr2 *common.Address) int {
// TODO: Get exchangeRates from statedb
var exchangeRates common.ExchangeRates
ret, err := CompareValue(exchangeRates, val1, curr1, val2, curr2)
// Err should not be possible if the pruning of non whitelisted currencies
// was made properly (and exchange rates are available)
if err != nil {
// TODO: LOG
// Compare with no currencies (Panic could be an option too)
r2, _ := CompareValue(exchangeRates, val1, nil, val2, nil)
return r2
}
return ret
}

func areEqualAddresses(addr1, addr2 *common.Address) bool {
return (addr1 == nil && addr2 == nil) || (addr1 != nil && addr2 != nil && *addr1 == *addr2)
}

func GetBalanceOf(backend *bind.ContractCaller, account common.Address, feeCurrency common.Address) (*big.Int, error) {
token, err := abigen.NewFeeCurrencyCaller(feeCurrency, *backend)
if err != nil {
return nil, errors.New("failed to access fee currency token")
}

balance, err := token.BalanceOf(&bind.CallOpts{}, account)
if err != nil {
return nil, errors.New("failed to access token balance")
}

return balance, nil
}
24 changes: 24 additions & 0 deletions core/txpool/legacypool/celo_legacypool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package legacypool

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

// filter Filters transactions from the given list, according to remaining balance (per currency, minus l1Cost)
// and gasLimit. Returns drops and invalid txs.
func (pool *LegacyPool) filter(list *celo_list, addr common.Address, l1Cost *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
st := pool.currentState
fcv := pool.feeCurrencyValidator
// CELO: drop all transactions that no longer have a whitelisted currency
dropsWhitelist := list.FilterWhitelisted(st, pool.all, fcv)

drops, invalids := list.FilterBalance(st, addr, l1Cost, gasLimit,
fcv)
totalDrops := make(types.Transactions, 0, len(dropsWhitelist)+len(drops))
totalDrops = append(totalDrops, dropsWhitelist...)
totalDrops = append(totalDrops, drops...)
return totalDrops, invalids
}
Loading