Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

cmd, les, eth, eth/gasprice: using new gas price oracle #13853

Merged
merged 5 commits into from
Apr 6, 2017

Conversation

zsfelfoldi
Copy link
Contributor

This PR improves the newer version of the Gas Price Oracle (formerly used by light client mode only) and removes the old version, now using the same algorithm for both client modes.

@mention-bot
Copy link

@zsfelfoldi, thanks for your PR! By analyzing the history of the files in this pull request, we identified @fjl, @karalabe and @obscuren to be potential reviewers.

@zsfelfoldi zsfelfoldi added this to the 1.6.0 milestone Mar 30, 2017
Copy link
Member

@karalabe karalabe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR seems simple enough, but I would really like to see some code quality improvements.

GpobaseCorrectionFactor int
GpoBlocks int
GpoPercentile int
GpoDefault *big.Int
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need for these fields to have the Gpo prefix. Every field is a configuration parameter of the gas price oracle. No need to remind everywhere.

type blockPriceInfo struct {
baseGasPrice *big.Int
}

type GpoParams struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just call this struct Config. It's obvious from its type (gasprice.Config) what it does.

Usage: "Suggested gas price base correction factor (%)",
Value: 110,
GpoPercentileFlag = cli.IntFlag{
Name: "gpopercent",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please call this gpopercentile. Percent is a different concept.

Value: 110,
GpoPercentileFlag = cli.IntFlag{
Name: "gpopercent",
Usage: "Percentile of recent transaction gas prices",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't really explain what it does. Please formulate it a bit more meaningfully :P

}

// GasPriceOracle recommends gas prices based on the content of recent
// blocks.
// blocks. Suitable for both light and full clients.
type GasPriceOracle struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The full type of this (i.e. what you would see whenever referring to it) is gasprice.GasPriceOracle. There's no need imho for this stuttering. Please only use Oracle for the type name.

chn := make(chan lpResult, self.checkBlocks)
sent := 0
exp := 0
var lps bigIntArray
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please name this something meaningful, not lps. Also don't use bigIntArray as the type. If you need to sort big ints, it's enough to cast it to the sorting interface exactly at the point where you call a Sort on it. There's no need to litter the sorting interface all over the code as it makes everything harder to understand.

exp--
if len(res.prices) > 0 {
lps = append(lps, res.prices...)
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of the

if len(res.prices) > 0 {
  lps = append(lps, res.prices...)
} else {

construct, please use

if len(res.prices) > 0 {
  lps = append(lps, res.prices...)
  continue
}

It avoids these deep nestings that you just made here.

} else {
if maxEmpty > 0 {
maxEmpty--
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, please use continue instead of nesting yet another layer.

if block != nil {
self.processBlock(block)
// SuggestPrice returns the recommended gas price.
func (self *GasPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please replace all instances of self with gpo for example. The keyword self is a very bad pattern in Go code and is advised against.

}

self.fetchLock.Lock()
defer self.fetchLock.Unlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit unsure about this pattern here. Fetching the last N blocks may run for a considerable amount of time. The current approach seems to flat out block on it. That's ok for a full client since fetching the blocks should be fast. But how much will a light client block? Perhaps a saner thing would be to try and update the gas price in the background, but return the previous price to the user if X seconds pass?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocks are fetched in parallel, it usually only takes a fraction of a second in light client mode. Also, an old suggestion is not necessarily a good suggestion so I'd rather keep it like this until it proves to be a problem.

lastPrice: params.Default,
checkBlocks: blocks,
minBlocks: (blocks 1) / 2,
maxBlocks: blocks * 5,
Copy link
Member

@karalabe karalabe Apr 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the rational behind these values?

minBlocks:   (blocks   1) / 2,
maxBlocks:   blocks * 5,

And a bit below why is maxEmpty := gpo.checkBlocks - gpo.minBlocks this particular value?

For example if I tell the oracle to use 10 blocks, why is minBlocks == 5, maxEmpty == 5 and maxBlocks = 50?

Do we use minBlocks for anything apart from calcualting maxEmpty? If not, I'd suggest adding maxEmpty to the struct instead and not storing minBlocks at all.

GpoPercentileFlag = cli.IntFlag{
Name: "gpopercentile",
Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
Value: 50,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This 50th percentile seems a bit arbitrary to me. Perhaps it would be nice to "scan" the mainnet transactions and see what a sane vaue would be for the default parameters.

Copy link
Member

@karalabe karalabe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@karalabe karalabe merged commit 9aca9e6 into ethereum:master Apr 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants