Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 5 additions & 8 deletions core/vm/cvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,12 +500,14 @@ func (cvm *CVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}

// check whether the max code size has been exceeded
maxCodeSizeExceeded := cvm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize
if err == nil && cvm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
err = errMaxCodeSizeExceeded
}
// if the contract creation ran successfully and no errors were returned
// calculate the gas required to store the code. If the code could not
// be stored due to not enough gas set an error and let it be handled
// by the error checking condition below.
if err == nil && !maxCodeSizeExceeded {
if err == nil {
createDataGas := uint64(len(ret)) * params.CreateDataGas
if contract.UseGas(createDataGas) {
cvm.StateDB.SetCode(address, ret)
Expand All @@ -517,21 +519,16 @@ func (cvm *CVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// When an error was returned by the CVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
if maxCodeSizeExceeded || (err != nil && (cvm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas)) {
if err != nil && (cvm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
cvm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
}
}
// Assign err if contract code size exceeds the max while the err is still empty.
if maxCodeSizeExceeded && err == nil {
err = errMaxCodeSizeExceeded
}
if cvm.vmConfig.Debug && cvm.depth == 0 {
cvm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
}
return ret, address, contract.Gas, contract.ModelGas, err

}

// Create creates a new contract using code as deployment code.
Expand Down
124 changes: 70 additions & 54 deletions ctxc/gasprice/gasprice.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// Copyright 2019 The CortexTheseus Authors
// This file is part of the CortexFoundation library.
// Copyright 2015 The CortexTheseus Authors
// This file is part of the CortexTheseus library.
//
// The CortexFoundation library is free software: you can redistribute it and/or modify
// The CortexTheseus 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 CortexFoundation library is distributed in the hope that it will be useful,
// The CortexTheseus 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 CortexFoundation library. If not, see <http://www.gnu.org/licenses/>.
// along with the CortexTheseus library. If not, see <http://www.gnu.org/licenses/>.

package gasprice

Expand All @@ -29,6 +29,8 @@ import (
"github.com/CortexFoundation/CortexTheseus/rpc"
)

const sampleNumber = 3 // Number of transactions sampled in a block

var DefaultMaxPrice = big.NewInt(500 * params.GWei)

type Config struct {
Expand All @@ -55,11 +57,12 @@ type Oracle struct {
cacheLock sync.RWMutex
fetchLock sync.Mutex

checkBlocks, maxEmpty, maxBlocks int
percentile int
checkBlocks int
percentile int
}

// NewOracle returns a new oracle.
// NewOracle returns a new gasprice oracle which can recommend suitable
// gasprice for newly created transaction.
func NewOracle(backend OracleBackend, params Config) *Oracle {
blocks := params.Blocks
if blocks < 1 {
Expand All @@ -85,79 +88,79 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
lastPrice: params.Default,
maxPrice: maxPrice,
checkBlocks: blocks,
maxEmpty: blocks / 2,
maxBlocks: blocks * 5,
percentile: percent,
}
}

// SuggestPrice returns the recommended gas price.
// SuggestPrice returns a gasprice so that newly created transaction can
// have a very high chance to be included in the following blocks.
func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
gpo.cacheLock.RLock()
lastHead := gpo.lastHead
lastPrice := gpo.lastPrice
gpo.cacheLock.RUnlock()

head, _ := gpo.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
headHash := head.Hash()

// If the latest gasprice is still available, return it.
gpo.cacheLock.RLock()
lastHead, lastPrice := gpo.lastHead, gpo.lastPrice
gpo.cacheLock.RUnlock()
if headHash == lastHead {
return lastPrice, nil
}

gpo.fetchLock.Lock()
defer gpo.fetchLock.Unlock()

// try checking the cache again, maybe the last fetch fetched what we need
// Try checking the cache again, maybe the last fetch fetched what we need
gpo.cacheLock.RLock()
lastHead = gpo.lastHead
lastPrice = gpo.lastPrice
lastHead, lastPrice = gpo.lastHead, gpo.lastPrice
gpo.cacheLock.RUnlock()
if headHash == lastHead {
return lastPrice, nil
}

blockNum := head.Number.Uint64()
ch := make(chan getBlockPricesResult, gpo.checkBlocks)
sent := 0
exp := 0
var blockPrices []*big.Int
for sent < gpo.checkBlocks && blockNum > 0 {
go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
var (
sent, exp int
number = head.Number.Uint64()
result = make(chan getBlockPricesResult, gpo.checkBlocks)
quit = make(chan struct{})
txPrices []*big.Int
)
for sent < gpo.checkBlocks && number > 0 {
go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, result, quit)
sent++
exp++
blockNum--
number--
}
maxEmpty := gpo.maxEmpty
for exp > 0 {
res := <-ch
res := <-result
if res.err != nil {
close(quit)
return lastPrice, res.err
}
exp--
if res.price != nil {
blockPrices = append(blockPrices, res.price)
continue
}
if maxEmpty > 0 {
maxEmpty--
continue
// Nothing returned. There are two special cases here:
// - The block is empty
// - All the transactions included are sent by the miner itself.
// In these cases, use the latest calculated price for samping.
if len(res.prices) == 0 {
res.prices = []*big.Int{lastPrice}
}
if blockNum > 0 && sent < gpo.maxBlocks {
go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(blockNum))), blockNum, ch)
// Besides, in order to collect enough data for sampling, if nothing
// meaningful returned, try to query more blocks. But the maximum
// is 2*checkBlocks.
if len(res.prices) == 1 && len(txPrices)+1+exp < gpo.checkBlocks*2 && number > 0 {
go gpo.getBlockPrices(ctx, types.MakeSigner(gpo.backend.ChainConfig(), big.NewInt(int64(number))), number, sampleNumber, result, quit)
sent++
exp++
blockNum--
number--
}
txPrices = append(txPrices, res.prices...)
}
price := lastPrice
if len(blockPrices) > 0 {
sort.Sort(bigIntArray(blockPrices))
price = blockPrices[(len(blockPrices)-1)*gpo.percentile/100]
if len(txPrices) > 0 {
sort.Sort(bigIntArray(txPrices))
price = txPrices[(len(txPrices)-1)*gpo.percentile/100]
}
if price.Cmp(gpo.maxPrice) > 0 {
price = new(big.Int).Set(gpo.maxPrice)
}

gpo.cacheLock.Lock()
gpo.lastHead = headHash
gpo.lastPrice = price
Expand All @@ -166,8 +169,8 @@ func (gpo *Oracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
}

type getBlockPricesResult struct {
price *big.Int
err error
prices []*big.Int
err error
}

type transactionsByGasPrice []*types.Transaction
Expand All @@ -177,27 +180,40 @@ 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].GasPriceCmp(t[j]) < 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) {
// and sends it to the result channel. If the block is empty or all transactions
// are sent by the miner itself(it doesn't make any sense to include this kind of
// transaction prices for sampling), nil gasprice is returned.
func (gpo *Oracle) getBlockPrices(ctx context.Context, signer types.Signer, blockNum uint64, limit int, result chan getBlockPricesResult, quit chan struct{}) {
block, err := gpo.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum))
if block == nil {
ch <- getBlockPricesResult{nil, err}
select {
case result <- getBlockPricesResult{nil, err}:
case <-quit:
}
return
}

blockTxs := block.Transactions()
txs := make([]*types.Transaction, len(blockTxs))
copy(txs, blockTxs)
sort.Sort(transactionsByGasPrice(txs))

var prices []*big.Int
for _, tx := range txs {
if tx.GasPriceIntCmp(common.Big1) <= 0 {
continue
}
sender, err := types.Sender(signer, tx)
if err == nil && sender != block.Coinbase() {
ch <- getBlockPricesResult{tx.GasPrice(), nil}
return
prices = append(prices, tx.GasPrice())
if len(prices) >= limit {
break
}
}
}
ch <- getBlockPricesResult{nil, nil}
select {
case result <- getBlockPricesResult{prices, nil}:
case <-quit:
}
}

type bigIntArray []*big.Int
Expand Down
12 changes: 10 additions & 2 deletions ctxc/gasprice/gasprice_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2020 The CortexTheseus Authors
// This file is part of the CortexFoundation library.
// This file is part of the CortexTheseus library.
//
// The CortexTheseus 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
Expand All @@ -12,7 +12,7 @@
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the CortexTheseus library. If not, see <http://www.gnu.org/licenses/>.package gasprice
// along with the CortexTheseus library. If not, see <http://www.gnu.org/licenses/>.

package gasprice

Expand Down Expand Up @@ -89,6 +89,14 @@ func newTestBackend(t *testing.T) *testBackend {
return &testBackend{chain: chain}
}

func (b *testBackend) CurrentHeader() *types.Header {
return b.chain.CurrentHeader()
}

func (b *testBackend) GetBlockByNumber(number uint64) *types.Block {
return b.chain.GetBlockByNumber(number)
}

func TestSuggestPrice(t *testing.T) {
config := Config{
Blocks: 3,
Expand Down