Skip to content

Commit

Permalink
Refresh the registered addresses and gas currency whitelist more freq…
Browse files Browse the repository at this point in the history
…uently (ethereum#300)

* Tests passing

* tests working

* Tests passing

* Cleanup

* Cleanup

* Cleanup

* Address comments
  • Loading branch information
asaj authored and mergify[bot] committed Jun 26, 2019
1 parent 52adf69 commit 654e999
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 69 deletions.
79 changes: 43 additions & 36 deletions core/currency.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
Expand Down Expand Up @@ -198,10 +200,8 @@ func (co *CurrencyOperator) Cmp(val1 *big.Int, currency1 *common.Address, val2 *
// This function will retrieve the exchange rates from the SortedOracles contract and cache them.
// SortedOracles must have a function with the following signature:
// "function medianRate(address)"
func (co *CurrencyOperator) retrieveExchangeRates() {
gasCurrencyAddresses := co.gcWl.retrieveWhitelist()
log.Trace("CurrencyOperator.retrieveExchangeRates called", "gasCurrencyAddresses", gasCurrencyAddresses)

func (co *CurrencyOperator) refreshExchangeRates() {
gasCurrencyAddresses := co.gcWl.Whitelist()
sortedOraclesAddress, err := co.regAdd.GetRegisteredAddress(params.SortedOraclesRegistryId)

if err == ErrSmartContractNotDeployed {
Expand All @@ -228,15 +228,11 @@ func (co *CurrencyOperator) retrieveExchangeRates() {
}

var returnArray [2]*big.Int

log.Trace("CurrencyOperator.retrieveExchangeRates - Calling medianRate", "sortedOraclesAddress", sortedOraclesAddress.Hex(),
"gas currency", gasCurrencyAddress.Hex())

if leftoverGas, err := co.iEvmH.MakeStaticCall(*sortedOraclesAddress, medianRateFuncABI, "medianRate", []interface{}{gasCurrencyAddress}, &returnArray, 20000, nil, nil); err != nil {
log.Error("CurrencyOperator.retrieveExchangeRates - SortedOracles.medianRate invocation error", "leftoverGas", leftoverGas, "err", err)
log.Error("medianRate invocation error", "gasCurrencyAddress", gasCurrencyAddress.Hex(), "leftoverGas", leftoverGas, "err", err)
continue
} else {
log.Trace("CurrencyOperator.retrieveExchangeRates - SortedOracles.medianRate invocation success", "returnArray", returnArray, "leftoverGas", leftoverGas)
log.Trace("medianRate invocation success", "gasCurrencyAddress", gasCurrencyAddress, "returnArray", returnArray, "leftoverGas", leftoverGas)

if _, ok := co.exchangeRates[gasCurrencyAddress]; !ok {
co.exchangeRates[gasCurrencyAddress] = &exchangeRate{}
Expand All @@ -252,11 +248,11 @@ func (co *CurrencyOperator) retrieveExchangeRates() {

// TODO (jarmg 5/30/18): Change this to cache based on block number
func (co *CurrencyOperator) mainLoop() {
co.retrieveExchangeRates()
co.refreshExchangeRates()
ticker := time.NewTicker(10 * time.Second)

for range ticker.C {
co.retrieveExchangeRates()
co.refreshExchangeRates()
}
}

Expand Down Expand Up @@ -312,52 +308,52 @@ type GasCurrencyWhitelist struct {
iEvmH *InternalEVMHandler
}

func (gcWl *GasCurrencyWhitelist) retrieveWhitelist() []common.Address {
log.Trace("GasCurrencyWhitelist.retrieveWhitelist called")

func (gcWl *GasCurrencyWhitelist) retrieveWhitelist(state *state.StateDB, header *types.Header) ([]common.Address, error) {
returnList := []common.Address{}

gasCurrencyWhiteListAddress, err := gcWl.regAdd.GetRegisteredAddress(params.GasCurrencyWhitelistRegistryId)
if err == ErrSmartContractNotDeployed {
log.Warn("Registry address lookup failed", "err", err)
return returnList
} else if err != nil {
log.Error(err.Error())
if err != nil {
if err == ErrSmartContractNotDeployed {
log.Warn("Registry address lookup failed", "err", err)
} else {
log.Error("Registry address lookup failed", "err", err)
}
return returnList, err
}

log.Trace("GasCurrencyWhiteList.retrieveWhiteList() - Calling retrieveWhiteList", "address", gasCurrencyWhiteListAddress.Hex())

if leftoverGas, err := gcWl.iEvmH.MakeStaticCall(*gasCurrencyWhiteListAddress, getWhitelistFuncABI, "getWhitelist", []interface{}{}, &returnList, 20000, nil, nil); err != nil {
log.Error("GasCurrencyWhitelist.retrieveWhitelist - GasCurrencyWhitelist.getWhitelist invocation error", "leftoverGas", leftoverGas, "err", err)
return []common.Address{}
}
_, err = gcWl.iEvmH.MakeStaticCall(*gasCurrencyWhiteListAddress, getWhitelistFuncABI, "getWhitelist", []interface{}{}, &returnList, 20000, header, state)
return returnList, err
}

outputWhiteList := make([]string, len(returnList))
for _, address := range returnList {
outputWhiteList = append(outputWhiteList, address.Hex())
}
func (gcWl *GasCurrencyWhitelist) RefreshWhitelistAtStateAndHeader(state *state.StateDB, header *types.Header) {
gcWl.refreshWhitelist(state, header)
}

log.Trace("GasCurrencyWhitelist.retrieveWhitelist - GasCurrencyWhitelist.getWhitelist invocation success", "whitelisted currencies", outputWhiteList)
return returnList
func (gcWl *GasCurrencyWhitelist) RefreshWhitelistAtCurrentHeader() {
gcWl.refreshWhitelist(nil, nil)
}

func (gcWl *GasCurrencyWhitelist) RefreshWhitelist() {
addresses := gcWl.retrieveWhitelist()
func (gcWl *GasCurrencyWhitelist) refreshWhitelist(state *state.StateDB, header *types.Header) {
whitelist, err := gcWl.retrieveWhitelist(state, header)
if err != nil {
log.Warn("Failed to get gas currency whitelist", "err", err)
return
}

gcWl.whitelistedAddressesMu.Lock()

for k := range gcWl.whitelistedAddresses {
delete(gcWl.whitelistedAddresses, k)
}

for _, address := range addresses {
for _, address := range whitelist {
gcWl.whitelistedAddresses[address] = true
}

gcWl.whitelistedAddressesMu.Unlock()
}

func (gcWl *GasCurrencyWhitelist) IsWhitelisted(gasCurrencyAddress common.Address) bool {
gcWl.RefreshWhitelistAtCurrentHeader()
gcWl.whitelistedAddressesMu.RLock()

_, ok := gcWl.whitelistedAddresses[gasCurrencyAddress]
Expand All @@ -367,6 +363,17 @@ func (gcWl *GasCurrencyWhitelist) IsWhitelisted(gasCurrencyAddress common.Addres
return ok
}

func (gcWl *GasCurrencyWhitelist) Whitelist() []common.Address {
gcWl.RefreshWhitelistAtCurrentHeader()
whitelist := make([]common.Address, 0, len(gcWl.whitelistedAddresses))
gcWl.whitelistedAddressesMu.RLock()
for k := range gcWl.whitelistedAddresses {
whitelist = append(whitelist, k)
}
gcWl.whitelistedAddressesMu.RUnlock()
return whitelist
}

func NewGasCurrencyWhitelist(regAdd *RegisteredAddresses, iEvmH *InternalEVMHandler) *GasCurrencyWhitelist {
gcWl := &GasCurrencyWhitelist{
whitelistedAddresses: make(map[common.Address]bool),
Expand Down
29 changes: 17 additions & 12 deletions core/registeredAddresses.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
)
Expand Down Expand Up @@ -75,19 +77,18 @@ type RegisteredAddresses struct {
iEvmH *InternalEVMHandler
}

func (ra *RegisteredAddresses) retrieveRegisteredAddresses() map[string]common.Address {
func (ra *RegisteredAddresses) retrieveRegisteredAddresses(state *state.StateDB, header *types.Header) map[string]common.Address {
log.Trace("RegisteredAddresses.retrieveRegisteredAddresses called")

returnMap := make(map[string]common.Address)

for _, contractRegistryId := range registeredContractIds {
var contractAddress common.Address
log.Trace("RegisteredAddresses.retrieveRegisteredAddresses - Calling Registry.getAddressFor", "contractRegistryId", contractRegistryId)
if leftoverGas, err := ra.iEvmH.MakeStaticCall(registrySmartContractAddress, getAddressForFuncABI, "getAddressFor", []interface{}{contractRegistryId}, &contractAddress, 20000, nil, nil); err != nil {
log.Error("RegisteredAddresses.retrieveRegisteredAddresses - Registry.getAddressFor invocation error", "leftoverGas", leftoverGas, "err", err)
if leftoverGas, err := ra.iEvmH.MakeStaticCall(registrySmartContractAddress, getAddressForFuncABI, "getAddressFor", []interface{}{contractRegistryId}, &contractAddress, 20000, header, state); err != nil {
log.Error("Registry.getAddressFor invocation error", "registryId", contractRegistryId, "leftoverGas", leftoverGas, "err", err)
continue
} else {
log.Trace("RegisteredAddresses.retrieveRegisteredAddresses - Registry.getAddressFor invocation success", "contractAddress", contractAddress.Hex(), "leftoverGas", leftoverGas)
log.Debug("Registry.getAddressFor invocation success", "registryId", contractRegistryId, "contractAddress", contractAddress.Hex(), "leftoverGas", leftoverGas)

if contractAddress != common.ZeroAddress {
returnMap[contractRegistryId] = contractAddress
Expand All @@ -98,20 +99,24 @@ func (ra *RegisteredAddresses) retrieveRegisteredAddresses() map[string]common.A
return returnMap
}

func (ra *RegisteredAddresses) RefreshAddresses() {
registeredAddresses := ra.retrieveRegisteredAddresses()
func (ra *RegisteredAddresses) RefreshAddressesAtStateAndHeader(state *state.StateDB, header *types.Header) {
ra.refreshAddresses(state, header)
}

func (ra *RegisteredAddresses) RefreshAddressesAtCurrentHeader() {
ra.refreshAddresses(nil, nil)
}

func (ra *RegisteredAddresses) refreshAddresses(state *state.StateDB, header *types.Header) {
registeredAddresses := ra.retrieveRegisteredAddresses(state, header)

ra.registeredAddressesMu.Lock()
ra.registeredAddresses = registeredAddresses
ra.registeredAddressesMu.Unlock()
}

func (ra *RegisteredAddresses) GetRegisteredAddress(registryId string) (*common.Address, error) {
log.Trace("RegisteredAddresses.GetRegisteredAddress called for", "registryId", registryId)

if len(ra.registeredAddresses) == 0 { // This refresh is for a light client that failed to refresh (did not have a network connection) during node construction
ra.RefreshAddresses()
}
ra.RefreshAddressesAtCurrentHeader()

ra.registeredAddressesMu.RLock()
defer ra.registeredAddressesMu.RUnlock()
Expand Down
19 changes: 9 additions & 10 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
misc.ApplyDAOHardFork(statedb)
}

// Refresh the registered addresses cache right before processing the block's transactions
if p.gcWl != nil {
p.regAdd.RefreshAddresses()
}

// Refresh the gas currency whitelist cache right before processing the block's transactions
if p.gcWl != nil {
p.gcWl.RefreshWhitelist()
}

if p.random != nil && p.random.Running() {
err := p.random.RevealAndCommit(block.Randomness().Revealed, block.Randomness().Committed, header.Coinbase, header, statedb)
if err != nil {
Expand Down Expand Up @@ -125,6 +115,15 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
return nil, 0, err
}

// Refresh the registered addresses and gas currency whitelist right before processing the transaction
if regAdd != nil {
regAdd.RefreshAddressesAtStateAndHeader(statedb, header)
}

if gcWl != nil {
gcWl.RefreshWhitelistAtStateAndHeader(statedb, header)
}

// Create a new context to be used in the EVM environment
context := NewEVMContext(msg, header, bc, author, regAdd)
// Create a new environment which holds all relevant information
Expand Down
3 changes: 1 addition & 2 deletions core/tx_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -645,8 +645,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
return ErrNonceTooLow
}
if tx.GasCurrency() == nil && pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
log.Debug("validateTx insufficient funds", "balance", pool.currentState.GetBalance(from).String(),
"txn cost", tx.Cost().String())
log.Debug("validateTx insufficient funds", "balance", pool.currentState.GetBalance(from).String(), "from", from.Hex(), "txn cost", tx.Cost().String())
return ErrInsufficientFunds
} else if tx.GasCurrency() != nil {
gasCurrencyBalance, _, err := GetBalanceOf(from, *tx.GasCurrency(), pool.iEvmH, nil, params.MaxGasToReadErc20Balance)
Expand Down
4 changes: 2 additions & 2 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
gas, err = evm.TobinTransfer(evm.StateDB, caller.Address(), to.Address(), gas, value)
if err != nil {
log.Debug("Failed to transfer with tobin tax", "err", err)
log.Error("Failed to transfer with tobin tax", "err", err)
return nil, gas, err
}
// Initialise a new contract and set the code that is to be used by the EVM.
Expand Down Expand Up @@ -423,7 +423,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
gas, err := evm.TobinTransfer(evm.StateDB, caller.Address(), address, gas, value)
if err != nil {
log.Error("TobinTransfer failed", "error", err)
log.Error("Failed to transfer with tobin tax", "err", err)
return nil, address, gas, err
}

Expand Down
4 changes: 4 additions & 0 deletions internal/ethapi/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,10 @@ func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr
// this makes sure resources are cleaned up.
defer cancel()

// Needed so that the values returned by estimate gas, view functions, are correct.
s.b.RegisteredAddresses().RefreshAddressesAtStateAndHeader(state, header)
s.b.GasCurrencyWhitelist().RefreshWhitelistAtStateAndHeader(state, header)

// Get a new instance of the EVM.
evm, vmError, err := s.b.GetEVM(ctx, msg, state, header)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions internal/ethapi/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type Backend interface {
CurrentBlock() *types.Block

GasCurrencyWhitelist() *core.GasCurrencyWhitelist
RegisteredAddresses() *core.RegisteredAddresses
GasFeeRecipient() common.Address
}

Expand Down
4 changes: 2 additions & 2 deletions les/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (b *LesApiBackend) GetTd(hash common.Hash) *big.Int {
func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
state.SetBalance(msg.From(), math.MaxBig256)

context := core.NewEVMContext(msg, header, b.eth.blockchain, nil, nil)
context := core.NewEVMContext(msg, header, b.eth.blockchain, nil, b.eth.regAdd)
return vm.NewEVM(context, state, b.eth.chainConfig, vm.Config{}), state.Error, nil
}

Expand Down Expand Up @@ -210,5 +210,5 @@ func (b *LesApiBackend) GasFeeRecipient() common.Address {
}

func (b *LesApiBackend) RegisteredAddresses() *core.RegisteredAddresses {
return nil
return b.eth.regAdd
}
2 changes: 1 addition & 1 deletion les/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
// Object used to retrieve and cache registered addresses from the Registry smart contract.
leth.regAdd = core.NewRegisteredAddresses(leth.iEvmH)
leth.iEvmH.SetRegisteredAddresses(leth.regAdd)
leth.regAdd.RefreshAddresses()
leth.regAdd.RefreshAddressesAtCurrentHeader()
leth.gcWl = core.NewGasCurrencyWhitelist(leth.regAdd, leth.iEvmH)

// Note: AddChildIndexer starts the update process for the child
Expand Down
8 changes: 4 additions & 4 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,12 +528,12 @@ func (w *worker) mainLoop() {

// Refresh the registered address cache before processing transaction batch
if regAdd := w.eth.RegisteredAddresses(); regAdd != nil {
regAdd.RefreshAddresses()
regAdd.RefreshAddressesAtCurrentHeader()
}

// Refresh the gas currency whitelist cache before processing transaction batch
if wl := w.eth.GasCurrencyWhitelist(); wl != nil {
wl.RefreshWhitelist()
wl.RefreshWhitelistAtCurrentHeader()
}

txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, w.txCmp)
Expand Down Expand Up @@ -991,12 +991,12 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)

// Refresh the registered address cache before processing transaction batch
if regAdd := w.eth.RegisteredAddresses(); regAdd != nil {
regAdd.RefreshAddresses()
regAdd.RefreshAddressesAtCurrentHeader()
}

// Refresh the gas currency whitelist cache before processing the pending transactions
if wl := w.eth.GasCurrencyWhitelist(); wl != nil {
wl.RefreshWhitelist()
wl.RefreshWhitelistAtCurrentHeader()
}

// Play our part in generating the random beacon.
Expand Down
1 change: 1 addition & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,6 @@ const (
ExpectedGasForDebitFromTransactions uint64 = 35 * 1000
MaxGasForCreditToTransactions uint64 = 30 * 1000
MaxGasToReadErc20Balance uint64 = 10 * 1000
MaxGasToReadTobinTax uint64 = 50 * 1000
AdditionalGasForNonGoldCurrencies uint64 = 2*MaxGasForCreditToTransactions + ExpectedGasForDebitFromTransactions + MaxGasToReadErc20Balance
)

0 comments on commit 654e999

Please sign in to comment.