From 16ef9f09d0926359418a8952aec431205f01bf20 Mon Sep 17 00:00:00 2001 From: jarmg Date: Wed, 5 Jun 2019 11:32:23 -0700 Subject: [PATCH] Revert "Revert "Enable smart contract calling from LES (#207)" (#239)" (#242) This reverts commit dfc9eef85ee3711eaa6d31d1aee89537dc66ab3c. --- consensus/istanbul/backend/engine.go | 9 +++ core/currency.go | 116 ++++++++++++++++++--------- core/evm.go | 36 +++++---- core/registeredAddresses.go | 7 +- core/tx_list.go | 12 +-- core/tx_pool.go | 10 +-- core/tx_pool_test.go | 68 ++++++++-------- eth/api_backend.go | 2 +- eth/backend.go | 14 ++-- eth/gasprice/access_gasprice.go | 64 +++++++++++++++ eth/gasprice/gasprice.go | 81 ++++++++++++------- les/api_backend.go | 3 +- les/backend.go | 21 +++-- les/handler_test.go | 4 +- light/lightchain.go | 7 +- miner/miner.go | 4 +- miner/worker.go | 8 +- miner/worker_test.go | 8 +- params/protocol_params.go | 1 + 19 files changed, 315 insertions(+), 160 deletions(-) create mode 100644 eth/gasprice/access_gasprice.go diff --git a/consensus/istanbul/backend/engine.go b/consensus/istanbul/backend/engine.go index 344a2b83b087..5e198ab072b8 100644 --- a/consensus/istanbul/backend/engine.go +++ b/consensus/istanbul/backend/engine.go @@ -428,6 +428,10 @@ func (sb *Backend) IsLastBlockOfEpoch(header *types.Header) bool { // consensus rules that happen at finalization (e.g. block rewards). func (sb *Backend) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) { + + // Calculate a new gas price suggestion and push it to the GasPriceOracle SmartContract + sb.updateGasPriceSuggestion(state) + // No block rewards in Istanbul, so the state remains as is and uncles are dropped header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.UncleHash = nilUncleHash @@ -436,6 +440,11 @@ func (sb *Backend) Finalize(chain consensus.ChainReader, header *types.Header, s return types.NewBlock(header, txs, nil, receipts), nil } +// TODO (jarmg 5/23/18): Implement this +func (sb *Backend) updateGasPriceSuggestion(state *state.StateDB) *state.StateDB { + return (state) +} + // Seal generates a new block for the given input block with the local miner's // seal place on top. func (sb *Backend) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { diff --git a/core/currency.go b/core/currency.go index 1aa985a44e47..04e33958d35d 100644 --- a/core/currency.go +++ b/core/currency.go @@ -109,33 +109,66 @@ type exchangeRate struct { Denominator *big.Int } -type PriceComparator struct { - gcWl *GasCurrencyWhitelist // Object to retrieve the set of currencies that will have their exchange rate monitored - exchangeRates map[common.Address]*exchangeRate // indexedCurrency:CeloGold exchange rate - regAdd *RegisteredAddresses - iEvmH *InternalEVMHandler +type CurrencyOperator struct { + gcWl *GasCurrencyWhitelist // Object to retrieve the set of currencies that will have their exchange rate monitored + exchangeRates map[common.Address]*exchangeRate // indexedCurrency:CeloGold exchange rate + regAdd *RegisteredAddresses + iEvmH *InternalEVMHandler + currencyOperatorMu sync.RWMutex } -// Returns the price of gold in the provided currency. -func (pc *PriceComparator) getExchangeRate(currency *common.Address) (*big.Int, *big.Int, error) { +func (co *CurrencyOperator) getExchangeRate(currency *common.Address) (*exchangeRate, error) { if currency == nil { - return cgExchangeRateNum, cgExchangeRateDen, nil + return &exchangeRate{cgExchangeRateNum, cgExchangeRateDen}, nil } else { - if exchangeRate, ok := pc.exchangeRates[*currency]; !ok { - return nil, nil, errExchangeRateCacheMiss + co.currencyOperatorMu.RLock() + defer co.currencyOperatorMu.RUnlock() + if exchangeRate, ok := co.exchangeRates[*currency]; !ok { + return nil, errExchangeRateCacheMiss } else { - return exchangeRate.Numerator, exchangeRate.Denominator, nil + return exchangeRate, nil } } } -func (pc *PriceComparator) Cmp(val1 *big.Int, currency1 *common.Address, val2 *big.Int, currency2 *common.Address) int { +func (co *CurrencyOperator) ConvertToGold(val *big.Int, currencyFrom *common.Address) (*big.Int, error) { + celoGoldAddress := co.regAdd.GetRegisteredAddress(params.GoldTokenRegistryId) + if currencyFrom == nil || currencyFrom == celoGoldAddress { + return val, nil + } + return co.Convert(val, currencyFrom, celoGoldAddress) +} + +// NOTE (jarmg 4/24/18): values are rounded down which can cause +// an estimate to be off by 1 (at most) +func (co *CurrencyOperator) Convert(val *big.Int, currencyFrom *common.Address, currencyTo *common.Address) (*big.Int, error) { + exchangeRateFrom, err1 := co.getExchangeRate(currencyFrom) + exchangeRateTo, err2 := co.getExchangeRate(currencyTo) + + if err1 != nil || err2 != nil { + log.Error("CurrencyOperator.Convert - Error in retreiving currency exchange rates") + if err1 != nil { + return nil, err1 + } + if err2 != nil { + return nil, err2 + } + } + + // Given value of val and rates n1/d1 and n2/d2 the function below does + // (val * n1 * d2) / (d1 * n2) + numerator := new(big.Int).Mul(val, new(big.Int).Mul(exchangeRateFrom.Numerator, exchangeRateTo.Denominator)) + denominator := new(big.Int).Mul(exchangeRateFrom.Denominator, exchangeRateTo.Numerator) + return new(big.Int).Div(numerator, denominator), nil +} + +func (co *CurrencyOperator) Cmp(val1 *big.Int, currency1 *common.Address, val2 *big.Int, currency2 *common.Address) int { if currency1 == currency2 { return val1.Cmp(val2) } - exchangeRate1Num, exchangeRate1Den, err1 := pc.getExchangeRate(currency1) - exchangeRate2Num, exchangeRate2Den, err2 := pc.getExchangeRate(currency2) + exchangeRate1, err1 := co.getExchangeRate(currency1) + exchangeRate2, err2 := co.getExchangeRate(currency2) if err1 != nil || err2 != nil { currency1Output := "nil" @@ -151,35 +184,37 @@ func (pc *PriceComparator) Cmp(val1 *big.Int, currency1 *common.Address, val2 *b } // Below code block is basically evaluating this comparison: - // val1 / exchangeRate1Num/exchangeRate1Den < val2 / exchangeRate2Num/exchangeRate2Den + // val1 * exchangeRate1.Numerator/exchangeRate1.Denominator < val2 * exchangeRate2.Numerator/exchangeRate2.Denominator // It will transform that comparison to this, to remove having to deal with fractional values. - // val1 * exchangeRate2Num * exchangeRate1Den < val2 * exchangeRate1Num * exchangeRate2Den - leftSide := new(big.Int).Mul(val1, new(big.Int).Mul(exchangeRate2Num, exchangeRate1Den)) - rightSide := new(big.Int).Mul(val2, new(big.Int).Mul(exchangeRate1Num, exchangeRate2Den)) + // val1 * exchangeRate1.Numerator * exchangeRate2.Denominator < val2 * exchangeRate2.Numerator * exchangeRate1.Denominator + leftSide := new(big.Int).Mul(val1, new(big.Int).Mul(exchangeRate1.Numerator, exchangeRate2.Denominator)) + rightSide := new(big.Int).Mul(val2, new(big.Int).Mul(exchangeRate2.Numerator, exchangeRate1.Denominator)) return leftSide.Cmp(rightSide) } // 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 (pc *PriceComparator) retrieveExchangeRates() { - gasCurrencyAddresses := pc.gcWl.retrieveWhitelist() - log.Trace("PriceComparator.retrieveExchangeRates called", "gasCurrencyAddresses", gasCurrencyAddresses) +func (co *CurrencyOperator) retrieveExchangeRates() { + gasCurrencyAddresses := co.gcWl.retrieveWhitelist() + log.Trace("CurrencyOperator.retrieveExchangeRates called", "gasCurrencyAddresses", gasCurrencyAddresses) - sortedOraclesAddress := pc.regAdd.GetRegisteredAddress(params.SortedOraclesRegistryId) + sortedOraclesAddress := co.regAdd.GetRegisteredAddress(params.SortedOraclesRegistryId) if sortedOraclesAddress == nil { log.Error("Can't get the sortedOracles smart contract address from the registry") return } - celoGoldAddress := pc.regAdd.GetRegisteredAddress(params.GoldTokenRegistryId) + celoGoldAddress := co.regAdd.GetRegisteredAddress(params.GoldTokenRegistryId) if celoGoldAddress == nil { log.Error("Can't get the celo gold smart contract address from the registry") return } + co.currencyOperatorMu.Lock() + for _, gasCurrencyAddress := range gasCurrencyAddresses { if gasCurrencyAddress == *celoGoldAddress { continue @@ -187,49 +222,52 @@ func (pc *PriceComparator) retrieveExchangeRates() { var returnArray [2]*big.Int - log.Trace("PriceComparator.retrieveExchangeRates - Calling medianRate", "sortedOraclesAddress", sortedOraclesAddress.Hex(), + log.Trace("CurrencyOperator.retrieveExchangeRates - Calling medianRate", "sortedOraclesAddress", sortedOraclesAddress.Hex(), "gas currency", gasCurrencyAddress.Hex()) - if leftoverGas, err := pc.iEvmH.MakeCall(*sortedOraclesAddress, medianRateFuncABI, "medianRate", []interface{}{gasCurrencyAddress}, &returnArray, 20000, nil, nil); err != nil { - log.Error("PriceComparator.retrieveExchangeRates - SortedOracles.medianRate invocation error", "leftoverGas", leftoverGas, "err", err) + if leftoverGas, err := co.iEvmH.MakeCall(*sortedOraclesAddress, medianRateFuncABI, "medianRate", []interface{}{gasCurrencyAddress}, &returnArray, 20000, nil, nil); err != nil { + log.Error("CurrencyOperator.retrieveExchangeRates - SortedOracles.medianRate invocation error", "leftoverGas", leftoverGas, "err", err) continue } else { - log.Trace("PriceComparator.retrieveExchangeRates - SortedOracles.medianRate invocation success", "returnArray", returnArray, "leftoverGas", leftoverGas) + log.Trace("CurrencyOperator.retrieveExchangeRates - SortedOracles.medianRate invocation success", "returnArray", returnArray, "leftoverGas", leftoverGas) - if _, ok := pc.exchangeRates[gasCurrencyAddress]; !ok { - pc.exchangeRates[gasCurrencyAddress] = &exchangeRate{} + if _, ok := co.exchangeRates[gasCurrencyAddress]; !ok { + co.exchangeRates[gasCurrencyAddress] = &exchangeRate{} } - pc.exchangeRates[gasCurrencyAddress].Numerator = returnArray[0] - pc.exchangeRates[gasCurrencyAddress].Denominator = returnArray[1] + co.exchangeRates[gasCurrencyAddress].Numerator = returnArray[0] + co.exchangeRates[gasCurrencyAddress].Denominator = returnArray[1] } } + + co.currencyOperatorMu.Unlock() } -func (pc *PriceComparator) mainLoop() { - pc.retrieveExchangeRates() +// TODO (jarmg 5/30/18): Change this to cache based on block number +func (co *CurrencyOperator) mainLoop() { + co.retrieveExchangeRates() ticker := time.NewTicker(10 * time.Second) for range ticker.C { - pc.retrieveExchangeRates() + co.retrieveExchangeRates() } } -func NewPriceComparator(gcWl *GasCurrencyWhitelist, regAdd *RegisteredAddresses, iEvmH *InternalEVMHandler) *PriceComparator { +func NewCurrencyOperator(gcWl *GasCurrencyWhitelist, regAdd *RegisteredAddresses, iEvmH *InternalEVMHandler) *CurrencyOperator { exchangeRates := make(map[common.Address]*exchangeRate) - pc := &PriceComparator{ + co := &CurrencyOperator{ gcWl: gcWl, exchangeRates: exchangeRates, regAdd: regAdd, iEvmH: iEvmH, } - if pc.gcWl != nil { - go pc.mainLoop() + if co.gcWl != nil { + go co.mainLoop() } - return pc + return co } // This function will retrieve the balance of an ERC20 token. diff --git a/core/evm.go b/core/evm.go index 46371318c0ac..0fb18e819934 100644 --- a/core/evm.go +++ b/core/evm.go @@ -29,14 +29,24 @@ import ( "github.com/ethereum/go-ethereum/params" ) -// ChainContext supports retrieving headers and consensus parameters from the -// current blockchain to be used during transaction processing. +// ChainContext supports retrieving chain data and consensus parameters +// from the block chain to be used during transaction processing. type ChainContext interface { - // Engine retrieves the chain's consensus engine. + // Engine retrieves the blockchain's consensus engine. Engine() consensus.Engine // GetHeader returns the hash corresponding to their hash. GetHeader(common.Hash, uint64) *types.Header + + // GetVMConfig returns the node's vm configuration + GetVMConfig() *vm.Config + + CurrentHeader() *types.Header + + State() (*state.StateDB, error) + + // Config returns the blockchain's chain configuration + Config() *params.ChainConfig } // NewEVMContext creates a new context for use in the EVM. @@ -138,9 +148,8 @@ func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) // An EVM handler to make calls to smart contracts from within geth type InternalEVMHandler struct { - blockchain *BlockChain // Used to construct the EVM object needed to make the call the medianator contract - chainConfig *params.ChainConfig // The config object of the eth object - regAdd *RegisteredAddresses + chain ChainContext + regAdd *RegisteredAddresses } func (iEvmH *InternalEVMHandler) MakeCall(scAddress common.Address, abi abi.ABI, funcName string, args []interface{}, returnObj interface{}, gas uint64, header *types.Header, state *state.StateDB) (uint64, error) { @@ -148,14 +157,15 @@ func (iEvmH *InternalEVMHandler) MakeCall(scAddress common.Address, abi abi.ABI, // there are times (e.g. retrieving the set of validators when an epoch ends) that we need // to call the evm using the currently mined block. In that case, the header and state params // will be non nil. + log.Trace("InternalEVMHandler.MakeCall called") if header == nil { - header = iEvmH.blockchain.CurrentBlock().Header() + header = iEvmH.chain.CurrentHeader() } if state == nil { var err error - state, err = iEvmH.blockchain.StateAt(header.Root) + state, err = iEvmH.chain.State() if err != nil { log.Error("Error in retrieving the state from the blockchain") return 0, err @@ -165,8 +175,8 @@ func (iEvmH *InternalEVMHandler) MakeCall(scAddress common.Address, abi abi.ABI, // The EVM Context requires a msg, but the actual field values don't really matter for this case. // Putting in zero values. msg := types.NewMessage(common.HexToAddress("0x0"), nil, 0, common.Big0, 0, common.Big0, nil, nil, []byte{}, false) - context := NewEVMContext(msg, header, iEvmH.blockchain, nil, iEvmH.regAdd) - evm := vm.NewEVM(context, state, iEvmH.chainConfig, *iEvmH.blockchain.GetVMConfig()) + context := NewEVMContext(msg, header, iEvmH.chain, nil, iEvmH.regAdd) + evm := vm.NewEVM(context, state, iEvmH.chain.Config(), *iEvmH.chain.GetVMConfig()) zeroCaller := vm.AccountRef(common.HexToAddress("0x0")) return evm.ABIStaticCall(zeroCaller, scAddress, abi, funcName, args, returnObj, gas) @@ -176,11 +186,9 @@ func (iEvmH *InternalEVMHandler) SetRegisteredAddresses(regAdd *RegisteredAddres iEvmH.regAdd = regAdd } -func NewInternalEVMHandler(chainConfig *params.ChainConfig, blockchain *BlockChain) *InternalEVMHandler { +func NewInternalEVMHandler(chain ChainContext) *InternalEVMHandler { iEvmH := InternalEVMHandler{ - blockchain: blockchain, - chainConfig: chainConfig, + chain: chain, } - return &iEvmH } diff --git a/core/registeredAddresses.go b/core/registeredAddresses.go index 36741463dd21..f16a6be71c90 100644 --- a/core/registeredAddresses.go +++ b/core/registeredAddresses.go @@ -50,7 +50,7 @@ const ( var ( registrySmartContractAddress = common.HexToAddress("0x000000000000000000000000000000000000ce10") - registeredContractIds = []string{params.GoldTokenRegistryId, params.AddressBasedEncryptionRegistryId, params.ReserveRegistryId, params.SortedOraclesRegistryId, params.GasCurrencyWhitelistRegistryId, params.ValidatorsRegistryId} + registeredContractIds = []string{params.GoldTokenRegistryId, params.AddressBasedEncryptionRegistryId, params.ReserveRegistryId, params.SortedOraclesRegistryId, params.GasCurrencyWhitelistRegistryId, params.ValidatorsRegistryId, params.GasPriceOracleRegistryId} getAddressForFuncABI, _ = abi.JSON(strings.NewReader(getAddressForABI)) zeroAddress = common.Address{} ) @@ -93,10 +93,15 @@ func (ra *RegisteredAddresses) RefreshAddresses() { } func (ra *RegisteredAddresses) GetRegisteredAddress(registryId string) *common.Address { + 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.registeredAddressesMu.RLock() defer ra.registeredAddressesMu.RUnlock() if address, ok := ra.registeredAddresses[registryId]; !ok { + log.Error("RegisteredAddresses.GetRegisteredAddress - Error in address retrieval for ", "registry", registryId) return nil } else { return &address diff --git a/core/tx_list.go b/core/tx_list.go index d962ec40eb74..328f5c9462ee 100644 --- a/core/tx_list.go +++ b/core/tx_list.go @@ -411,16 +411,16 @@ type txPricedList struct { nonNilCurrencyHeaps map[common.Address]*priceHeap // Heap of prices of all the stored non-nil currency transactions nilCurrencyHeap *priceHeap // Heap of prices of all the stored nil currency transactions stales int // Number of stale price points to (re-heap trigger) - pc *PriceComparator // Comparator object used to compare prices that are using different currencies + co *CurrencyOperator // Comparator object used to compare prices that are using different currencies } // newTxPricedList creates a new price-sorted transaction heap. -func newTxPricedList(all *txLookup, pc *PriceComparator) *txPricedList { +func newTxPricedList(all *txLookup, co *CurrencyOperator) *txPricedList { return &txPricedList{ all: all, nonNilCurrencyHeaps: make(map[common.Address]*priceHeap), nilCurrencyHeap: new(priceHeap), - pc: pc, + co: co, } } @@ -489,7 +489,7 @@ func (l *txPricedList) Cap(cgThreshold *big.Int, local *accountSet) types.Transa continue } - if l.pc.Cmp(tx.GasPrice(), tx.GasCurrency(), cgThreshold, nil) >= 0 { + if l.co.Cmp(tx.GasPrice(), tx.GasCurrency(), cgThreshold, nil) >= 0 { save = append(save, tx) break } @@ -531,7 +531,7 @@ func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) boo } cheapest := l.getMinPricedTx() - return l.pc.Cmp(cheapest.GasPrice(), cheapest.GasCurrency(), tx.GasPrice(), tx.GasCurrency()) >= 0 + return l.co.Cmp(cheapest.GasPrice(), cheapest.GasCurrency(), tx.GasPrice(), tx.GasCurrency()) >= 0 } // Discard finds a number of most underpriced transactions, removes them from the @@ -574,7 +574,7 @@ func (l *txPricedList) getHeapWithMinHead() (*priceHeap, *types.Transaction) { cheapestTxn = []*types.Transaction(*cheapestHeap)[0] } else { txn := []*types.Transaction(*priceHeap)[0] - if l.pc.Cmp(cheapestTxn.GasPrice(), cheapestTxn.GasCurrency(), txn.GasPrice(), txn.GasCurrency()) < 0 { + if l.co.Cmp(cheapestTxn.GasPrice(), cheapestTxn.GasCurrency(), txn.GasPrice(), txn.GasCurrency()) < 0 { cheapestHeap = priceHeap } } diff --git a/core/tx_pool.go b/core/tx_pool.go index 54cf32f6deb0..d8c59f795ddf 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -246,14 +246,14 @@ type TxPool struct { homestead bool - pc *PriceComparator + co *CurrencyOperator gcWl *GasCurrencyWhitelist iEvmH *InternalEVMHandler } // NewTxPool creates a new transaction pool to gather, sort and filter inbound // transactions from the network. -func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain, pc *PriceComparator, gcWl *GasCurrencyWhitelist, iEvmH *InternalEVMHandler) *TxPool { +func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain blockChain, co *CurrencyOperator, gcWl *GasCurrencyWhitelist, iEvmH *InternalEVMHandler) *TxPool { // Sanitize the input to ensure no vulnerable gas prices are set config = (&config).sanitize() @@ -269,7 +269,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block all: newTxLookup(), chainHeadCh: make(chan ChainHeadEvent, chainHeadChanSize), gasPrice: new(big.Int).SetUint64(config.PriceLimit), - pc: pc, + co: co, gcWl: gcWl, iEvmH: iEvmH, } @@ -278,7 +278,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block log.Info("Setting new local account", "address", addr) pool.locals.add(addr) } - pool.priced = newTxPricedList(pool.all, pool.pc) + pool.priced = newTxPricedList(pool.all, pool.co) pool.reset(nil, chain.CurrentBlock().Header()) @@ -637,7 +637,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { // Drop non-local transactions under our own minimal accepted gas price local = local || pool.locals.contains(from) // account may be local even if the transaction arrived from the network - if !local && pool.pc.Cmp(pool.gasPrice, nil, tx.GasPrice(), tx.GasCurrency()) > 0 { + if !local && pool.co.Cmp(pool.gasPrice, nil, tx.GasPrice(), tx.GasCurrency()) > 0 { return ErrUnderpriced } // Ensure the transaction adheres to nonce ordering diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go index 2ff2a78cd801..be050da1dcf5 100644 --- a/core/tx_pool_test.go +++ b/core/tx_pool_test.go @@ -97,8 +97,8 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} key, _ := crypto.GenerateKey() - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, co, nil, nil) return pool, key } @@ -208,8 +208,8 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) { tx0 := transaction(0, 100000, key) tx1 := transaction(1, 100000, key) - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() nonce := pool.State().GetNonce(address) @@ -574,8 +574,8 @@ func TestTransactionPostponing(t *testing.T) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Create two test accounts to produce different gap profiles with @@ -794,8 +794,8 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) { config.NoLocals = nolocals config.GlobalQueue = config.AccountQueue*3 - 1 // reduce the queue limits to shorten test time (-1 to make it non divisible) - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Create a number of test accounts and fund them (last one will be the local) @@ -883,8 +883,8 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) { config.Lifetime = time.Second config.NoLocals = nolocals - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Create two test accounts to ensure remotes expire but locals do not @@ -1037,8 +1037,8 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) { config := testTxPoolConfig config.GlobalSlots = config.AccountSlots * 10 - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Create a number of test accounts and fund them @@ -1086,8 +1086,8 @@ func TestTransactionCapClearsFromAll(t *testing.T) { config.AccountQueue = 2 config.GlobalSlots = 8 - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Create a number of test accounts and fund them @@ -1119,8 +1119,8 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) { config := testTxPoolConfig config.GlobalSlots = 1 - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Create a number of test accounts and fund them @@ -1165,8 +1165,8 @@ func TestTransactionPoolRepricing(t *testing.T) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Keep track of transaction events to ensure all executables get announced @@ -1287,8 +1287,8 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Create a number of test accounts and fund them @@ -1354,8 +1354,8 @@ func TestTransactionPoolUnderpricing(t *testing.T) { config.GlobalSlots = 2 config.GlobalQueue = 2 - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Keep track of transaction events to ensure all executables get announced @@ -1461,8 +1461,8 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) { config.GlobalSlots = 128 config.GlobalQueue = 0 - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Keep track of transaction events to ensure all executables get announced @@ -1524,8 +1524,8 @@ func TestTransactionReplacement(t *testing.T) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Keep track of transaction events to ensure all executables get announced @@ -1624,8 +1624,8 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { config.Journal = journal config.Rejournal = time.Second - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) // Create two test accounts to ensure remotes expire but locals do not local, _ := crypto.GenerateKey() @@ -1662,8 +1662,8 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1) blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)} - pc = NewPriceComparator(nil, nil, nil) - pool = NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co = NewCurrencyOperator(nil, nil, nil) + pool = NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) pending, queued = pool.Stats() if queued != 0 { @@ -1689,8 +1689,8 @@ func testTransactionJournaling(t *testing.T, nolocals bool) { statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1) blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)} - pc = NewPriceComparator(nil, nil, nil) - pool = NewTxPool(config, params.TestChainConfig, blockchain, pc, nil, nil) + co = NewCurrencyOperator(nil, nil, nil) + pool = NewTxPool(config, params.TestChainConfig, blockchain, co, nil, nil) pending, queued = pool.Stats() if pending != 0 { @@ -1720,8 +1720,8 @@ func TestTransactionStatusCheck(t *testing.T) { statedb, _ := state.New(common.Hash{}, state.NewDatabase(ethdb.NewMemDatabase())) blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} - pc := NewPriceComparator(nil, nil, nil) - pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, pc, nil, nil) + co := NewCurrencyOperator(nil, nil, nil) + pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain, co, nil, nil) defer pool.Stop() // Create the test accounts to check various transaction statuses with diff --git a/eth/api_backend.go b/eth/api_backend.go index a633f8723608..97ffee3a10ac 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -198,7 +198,7 @@ func (b *EthAPIBackend) ProtocolVersion() int { } func (b *EthAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx) + return gasprice.GetGasPrice(ctx, b.eth.iEvmH, b.eth.regAdd) } func (b *EthAPIBackend) ChainDb() ethdb.Database { diff --git a/eth/backend.go b/eth/backend.go index 1603fd3d98bb..814ae9c82c25 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -186,9 +186,9 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { config.TxPool.Journal = ctx.ResolvePath(config.TxPool.Journal) } - // Create an internalEVMHandler handler object that geth can use to make calls to smart contracts. Note - // that this should NOT be used when executing smart contract calls done via end user transactions. - eth.iEvmH = core.NewInternalEVMHandler(eth.chainConfig, eth.blockchain) + // Create an internalEVMHandler handler object that geth can use to make calls to smart contracts. + // Note that this should NOT be used when executing smart contract calls done via end user transactions. + eth.iEvmH = core.NewInternalEVMHandler(eth.blockchain) // Object used to retrieve and cache registered addresses from the Registry smart contract. eth.regAdd = core.NewRegisteredAddresses(eth.iEvmH) @@ -198,16 +198,16 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { eth.gcWl = core.NewGasCurrencyWhitelist(eth.regAdd, eth.iEvmH) // Object used to compare two different prices using any of the whitelisted gas currencies. - pc := core.NewPriceComparator(eth.gcWl, eth.regAdd, eth.iEvmH) + co := core.NewCurrencyOperator(eth.gcWl, eth.regAdd, eth.iEvmH) - eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain, pc, eth.gcWl, eth.iEvmH) + eth.txPool = core.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain, co, eth.gcWl, eth.iEvmH) eth.blockchain.Processor().SetGasCurrencyWhitelist(eth.gcWl) eth.blockchain.Processor().SetRegisteredAddresses(eth.regAdd) if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.SyncMode, config.NetworkId, eth.eventMux, eth.txPool, eth.engine, eth.blockchain, chainDb, config.Whitelist); err != nil { return nil, err } - eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil, eth.isLocalBlock, config.MinerVerificationServiceUrl, config.MinerVerificationRewards, pc) + eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine, config.MinerRecommit, config.MinerGasFloor, config.MinerGasCeil, eth.isLocalBlock, config.MinerVerificationServiceUrl, config.MinerVerificationRewards, co) eth.miner.SetExtra(makeExtraData(config.MinerExtraData)) eth.APIBackend = &EthAPIBackend{eth, nil} @@ -215,7 +215,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if gpoParams.Default == nil { gpoParams.Default = config.MinerGasPrice } - eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams) + eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams, co) return eth, nil } diff --git a/eth/gasprice/access_gasprice.go b/eth/gasprice/access_gasprice.go new file mode 100644 index 000000000000..1ac715acff73 --- /dev/null +++ b/eth/gasprice/access_gasprice.go @@ -0,0 +1,64 @@ +// Copyright 2017 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see . + +package gasprice + +import ( + "context" + "errors" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/params" +) + +// TODO (jarmg 5/22/18): Store contract function ABIs in a central location +const ( + getGasPriceABIString = `[{ + "constant": true, + "inputs": [], + "name": "getGasPriceSuggestion", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }]` +) + +var ( + gasPriceOracleABI, _ = abi.JSON(strings.NewReader(getGasPriceABIString)) +) + +func GetGasPrice(ctx context.Context, iEvmH *core.InternalEVMHandler, regAdd *core.RegisteredAddresses) (*big.Int, error) { + + var gasPrice *big.Int + gasPriceOracleAddress := regAdd.GetRegisteredAddress(params.GasPriceOracleRegistryId) + + if gasPriceOracleAddress == nil { + return nil, errors.New("no gasprice oracle contract address found") + } + + _, err := iEvmH.MakeCall(*gasPriceOracleAddress, gasPriceOracleABI, "getGasPriceSuggestion", []interface{}{}, &gasPrice, 2000, nil, nil) + + return gasPrice, err +} diff --git a/eth/gasprice/gasprice.go b/eth/gasprice/gasprice.go index a0cbd6563bc2..620bf9676fa0 100644 --- a/eth/gasprice/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -23,8 +23,10 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/internal/ethapi" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" ) @@ -51,10 +53,11 @@ type Oracle struct { percentile int defaultPrice *big.Int alwaysZero bool + co *core.CurrencyOperator } // NewOracle returns a new oracle. -func NewOracle(backend ethapi.Backend, params Config) *Oracle { +func NewOracle(backend ethapi.Backend, params Config, co *core.CurrencyOperator) *Oracle { blocks := params.Blocks if blocks < 1 { blocks = 1 @@ -75,6 +78,7 @@ func NewOracle(backend ethapi.Backend, params Config) *Oracle { percentile: percent, defaultPrice: params.Default, alwaysZero: params.AlwaysZero, + co: co, } } @@ -85,29 +89,27 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { return big.NewInt(0), nil } - gpo.cacheLock.RLock() - lastHead := gpo.lastHead - lastPrice := gpo.lastPrice - gpo.cacheLock.RUnlock() - - head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) - headHash := head.Hash() - if headHash == lastHead { - return lastPrice, nil + price := getCachedPrice(gpo, ctx) + if price != nil { + return price, nil } + return calculateGasSuggestion(gpo, ctx) +} +func calculateGasSuggestion(gpo *Oracle, ctx context.Context) (*big.Int, error) { gpo.fetchLock.Lock() defer gpo.fetchLock.Unlock() - // try checking the cache again, maybe the last fetch fetched what we need - gpo.cacheLock.RLock() - lastHead = gpo.lastHead - lastPrice = gpo.lastPrice - gpo.cacheLock.RUnlock() - if headHash == lastHead { - return lastPrice, nil + // check if the cache was refreshed while we waited for the lock + price := getCachedPrice(gpo, ctx) + if price != nil { + return price, nil } + head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + headHash := head.Hash() + lastPrice := gpo.lastPrice + blockNum := head.Number.Uint64() ch := make(chan getBlockPricesResult, gpo.checkBlocks) sent := 0 @@ -141,7 +143,7 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { blockNum-- } } - price := gpo.defaultPrice + price = gpo.defaultPrice if len(blockPrices) > 0 { sort.Sort(bigIntArray(blockPrices)) price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100] @@ -157,17 +159,27 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) { return price, nil } +// getCachedPrice is a private function which checks if the last gasPrice +// suggestion was pulled from the current block and, if so, returns it +func getCachedPrice(gpo *Oracle, ctx context.Context) *big.Int { + gpo.cacheLock.RLock() + lastHead := gpo.lastHead + lastPrice := gpo.lastPrice + gpo.cacheLock.RUnlock() + + head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + headHash := head.Hash() + if headHash == lastHead { + return lastPrice + } + return nil +} + type getBlockPricesResult struct { price *big.Int err error } -type transactionsByGasPrice []*types.Transaction - -func (t transactionsByGasPrice) Len() int { return len(t) } -func (t transactionsByGasPrice) Swap(i, j int) { t[i], t[j] = t[j], t[i] } -func (t transactionsByGasPrice) Less(i, j int) bool { return t[i].GasPrice().Cmp(t[j].GasPrice()) < 0 } - // getBlockPrices calculates the lowest transaction gas price in a given block // and sends it to the result channel. If the block is empty, price is nil. func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, ch chan getBlockPricesResult) { @@ -178,18 +190,25 @@ func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, bloc } blockTxs := block.Transactions() - txs := make([]*types.Transaction, len(blockTxs)) - copy(txs, blockTxs) - sort.Sort(transactionsByGasPrice(txs)) + prices := make([]*big.Int, len(blockTxs)) - for _, tx := range txs { + for _, tx := range blockTxs { sender, err := types.Sender(signer, tx) if err == nil && sender != block.Coinbase() { - ch <- getBlockPricesResult{tx.GasPrice(), nil} - return + gpInGold, err := gpo.co.ConvertToGold(tx.GasPrice(), tx.GasCurrency()) + if err != nil { + log.Error("Error converting gas to gold", "error", err) + } else { + prices = append(prices, gpInGold) + } } } - ch <- getBlockPricesResult{nil, nil} + if len(prices) == 0 { + ch <- getBlockPricesResult{nil, nil} + } else { + sort.Sort(bigIntArray(prices)) + ch <- getBlockPricesResult{prices[0], nil} + } } type bigIntArray []*big.Int diff --git a/les/api_backend.go b/les/api_backend.go index 65d51e208c6e..881a8af204d5 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -40,7 +40,6 @@ import ( type LesApiBackend struct { eth *LightEthereum - gpo *gasprice.Oracle } func (b *LesApiBackend) ChainConfig() *params.ChainConfig { @@ -173,7 +172,7 @@ func (b *LesApiBackend) ProtocolVersion() int { } func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx) + return gasprice.GetGasPrice(ctx, b.eth.iEvmH, b.eth.regAdd) } func (b *LesApiBackend) ChainDb() ethdb.Database { diff --git a/les/backend.go b/les/backend.go index da157062b0c4..1f82f701a7d9 100644 --- a/les/backend.go +++ b/les/backend.go @@ -33,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" - "github.com/ethereum/go-ethereum/eth/gasprice" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/light" @@ -74,6 +73,9 @@ type LightEthereum struct { networkId uint64 netRPCService *ethapi.PublicNetAPI + regAdd *core.RegisteredAddresses + iEvmH *core.InternalEVMHandler + wg sync.WaitGroup } @@ -135,6 +137,16 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine); err != nil { return nil, err } + + // Create an internalEVMHandler handler object that geth can use to make calls to smart contracts. + // Note: that this should NOT be used when executing smart contract calls done via end user transactions. + leth.iEvmH = core.NewInternalEVMHandler(leth.blockchain) + + // 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() + // Note: AddChildIndexer starts the update process for the child leth.bloomIndexer.AddChildIndexer(leth.bloomTrieIndexer) leth.chtIndexer.Start(leth.blockchain) @@ -151,12 +163,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { if leth.protocolManager, err = NewProtocolManager(leth.chainConfig, light.DefaultClientIndexerConfig, syncMode, config.NetworkId, leth.eventMux, leth.engine, leth.peers, leth.blockchain, nil, chainDb, leth.odr, leth.relay, leth.serverPool, quitSync, &leth.wg, config.Etherbase); err != nil { return nil, err } - leth.ApiBackend = &LesApiBackend{leth, nil} - gpoParams := config.GPO - if gpoParams.Default == nil { - gpoParams.Default = config.MinerGasPrice - } - leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams) + leth.ApiBackend = &LesApiBackend{leth} return leth, nil } diff --git a/les/handler_test.go b/les/handler_test.go index e3567257f706..fbdee28a2733 100644 --- a/les/handler_test.go +++ b/les/handler_test.go @@ -498,8 +498,8 @@ func TestTransactionStatusLes2(t *testing.T) { chain := pm.blockchain.(*core.BlockChain) config := core.DefaultTxPoolConfig config.Journal = "" - pc := core.NewPriceComparator(nil, nil, nil) - txpool := core.NewTxPool(config, params.TestChainConfig, chain, pc, nil, nil) + co := core.NewCurrencyOperator(nil, nil, nil) + txpool := core.NewTxPool(config, params.TestChainConfig, chain, co, nil, nil) pm.txpool = txpool peer, _ := newTestPeer(t, "peer", 2, pm, true) defer peer.close() diff --git a/light/lightchain.go b/light/lightchain.go index 9b650ee2c6d1..ed0c9fbeda77 100644 --- a/light/lightchain.go +++ b/light/lightchain.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/core/rawdb" "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/ethdb" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" @@ -213,7 +214,7 @@ func (bc *LightChain) Genesis() *types.Block { // State returns a new mutable state based on the current HEAD block. func (bc *LightChain) State() (*state.StateDB, error) { - return nil, errors.New("not implemented, needs client/server interface split") + return NewState(context.Background(), bc.CurrentHeader(), bc.odr), nil // TODO: Any issues with using context.Background() here? } // GetBody retrieves a block body (transactions and uncles) from the database @@ -475,6 +476,10 @@ func (self *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) return GetHeaderByNumber(ctx, self.odr, number) } +func (self *LightChain) GetVMConfig() *vm.Config { + return &vm.Config{} +} + // Config retrieves the header chain's chain configuration. func (self *LightChain) Config() *params.ChainConfig { return self.hc.Config() } diff --git a/miner/miner.go b/miner/miner.go index 41ea831abb27..de76b351667a 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -57,13 +57,13 @@ type Miner struct { shouldStart int32 // should start indicates whether we should start after sync } -func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, recommit time.Duration, gasFloor, gasCeil uint64, isLocalBlock func(block *types.Block) bool, verificationService string, verificationRewards common.Address, pc *core.PriceComparator) *Miner { +func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, recommit time.Duration, gasFloor, gasCeil uint64, isLocalBlock func(block *types.Block) bool, verificationService string, verificationRewards common.Address, co *core.CurrencyOperator) *Miner { miner := &Miner{ eth: eth, mux: mux, engine: engine, exitCh: make(chan struct{}), - worker: newWorker(config, engine, eth, mux, recommit, gasFloor, gasCeil, isLocalBlock, verificationService, verificationRewards, pc), + worker: newWorker(config, engine, eth, mux, recommit, gasFloor, gasCeil, isLocalBlock, verificationService, verificationRewards, co), canStart: 1, } go miner.update() diff --git a/miner/worker.go b/miner/worker.go index 573ea5a5fe15..20cf186a28b6 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -186,10 +186,10 @@ type worker struct { lastBlockVerified uint64 // Transaction processing - pc *core.PriceComparator + co *core.CurrencyOperator } -func newWorker(config *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, recommit time.Duration, gasFloor, gasCeil uint64, isLocalBlock func(*types.Block) bool, verificationService string, verificationRewards common.Address, pc *core.PriceComparator) *worker { +func newWorker(config *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, recommit time.Duration, gasFloor, gasCeil uint64, isLocalBlock func(*types.Block) bool, verificationService string, verificationRewards common.Address, co *core.CurrencyOperator) *worker { worker := &worker{ config: config, engine: engine, @@ -215,7 +215,7 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, eth Backend, startCh: make(chan struct{}, 1), resubmitIntervalCh: make(chan time.Duration), resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), - pc: pc, + co: co, } // Subscribe NewTxsEvent for tx pool worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh) @@ -322,7 +322,7 @@ func (w *worker) close() { } func (w *worker) txCmp(tx1 *types.Transaction, tx2 *types.Transaction) int { - return w.pc.Cmp(tx1.GasPrice(), tx1.GasCurrency(), tx2.GasPrice(), tx2.GasCurrency()) + return w.co.Cmp(tx1.GasPrice(), tx1.GasCurrency(), tx2.GasPrice(), tx2.GasCurrency()) } // newWorkLoop is a standalone goroutine to submit new mining work upon received events. diff --git a/miner/worker_test.go b/miner/worker_test.go index 41f4143e2cf3..025d2e48417f 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -120,8 +120,8 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine genesis := gspec.MustCommit(db) chain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil) - pc := core.NewPriceComparator(nil, nil, nil) - txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain, pc, nil, nil) + co := core.NewCurrencyOperator(nil, nil, nil) + txpool := core.NewTxPool(testTxPoolConfig, chainConfig, chain, co, nil, nil) // Generate a small n-block chain and an uncle block for it if n > 0 { @@ -166,8 +166,8 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens if shouldAddPendingTxs { backend.txPool.AddLocals(pendingTxs) } - pc := core.NewPriceComparator(nil, nil, nil) - w := newWorker(chainConfig, engine, backend, new(event.TypeMux), time.Second, params.GenesisGasLimit, params.GenesisGasLimit, nil, testVerificationService, testVerificationRewardsAddress, pc) + co := core.NewCurrencyOperator(nil, nil, nil) + w := newWorker(chainConfig, engine, backend, new(event.TypeMux), time.Second, params.GenesisGasLimit, params.GenesisGasLimit, nil, testVerificationService, testVerificationRewardsAddress, co) w.setEtherbase(testBankAddress) return w, backend } diff --git a/params/protocol_params.go b/params/protocol_params.go index 4d9430b2a874..b83660ff8ebb 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -101,6 +101,7 @@ const ( SortedOraclesRegistryId = "SortedOracles" GasCurrencyWhitelistRegistryId = "GasCurrencyWhitelist" ValidatorsRegistryId = "Validators" + GasPriceOracleRegistryId = "GasPriceOracle" ) var (