diff --git a/core/currency.go b/core/currency.go index 976009a88cbe..39ac776dc2cd 100644 --- a/core/currency.go +++ b/core/currency.go @@ -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" @@ -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 { @@ -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{} @@ -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() } } @@ -312,37 +308,36 @@ 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() @@ -350,7 +345,7 @@ func (gcWl *GasCurrencyWhitelist) RefreshWhitelist() { delete(gcWl.whitelistedAddresses, k) } - for _, address := range addresses { + for _, address := range whitelist { gcWl.whitelistedAddresses[address] = true } @@ -358,6 +353,7 @@ func (gcWl *GasCurrencyWhitelist) RefreshWhitelist() { } func (gcWl *GasCurrencyWhitelist) IsWhitelisted(gasCurrencyAddress common.Address) bool { + gcWl.RefreshWhitelistAtCurrentHeader() gcWl.whitelistedAddressesMu.RLock() _, ok := gcWl.whitelistedAddresses[gasCurrencyAddress] @@ -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), diff --git a/core/registeredAddresses.go b/core/registeredAddresses.go index 1b8a804c6ff0..858e561d7f54 100644 --- a/core/registeredAddresses.go +++ b/core/registeredAddresses.go @@ -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" ) @@ -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 @@ -98,8 +99,16 @@ 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 @@ -107,11 +116,7 @@ func (ra *RegisteredAddresses) RefreshAddresses() { } 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() diff --git a/core/state_processor.go b/core/state_processor.go index 991cf4d8e0de..3dbfd22a3eac 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -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 { @@ -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 diff --git a/core/tx_pool.go b/core/tx_pool.go index d40cfc82c7a5..66a8313e8815 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -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) diff --git a/core/vm/evm.go b/core/vm/evm.go index 535b59dc53a8..7a609de33280 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -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. @@ -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 } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c7ff2451eae3..7f0ef90b457f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -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 { diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 7481af307646..3076a8bce55a 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -71,6 +71,7 @@ type Backend interface { CurrentBlock() *types.Block GasCurrencyWhitelist() *core.GasCurrencyWhitelist + RegisteredAddresses() *core.RegisteredAddresses GasFeeRecipient() common.Address } diff --git a/les/api_backend.go b/les/api_backend.go index 4b7763ab0c6f..994e38ff6657 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -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 } @@ -210,5 +210,5 @@ func (b *LesApiBackend) GasFeeRecipient() common.Address { } func (b *LesApiBackend) RegisteredAddresses() *core.RegisteredAddresses { - return nil + return b.eth.regAdd } diff --git a/les/backend.go b/les/backend.go index d32becf87eee..858eadc9d38f 100644 --- a/les/backend.go +++ b/les/backend.go @@ -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 diff --git a/miner/worker.go b/miner/worker.go index a076acbf4a80..9bca10cfd0f4 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -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) @@ -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. diff --git a/params/protocol_params.go b/params/protocol_params.go index c671283b7a1b..a2e7da5201e9 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -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 )