-
Notifications
You must be signed in to change notification settings - Fork 20.6k
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
EIP-1559: Fee market change for transaction gas #20618
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some things that caught my eye while getting an overview
consensus/clique/clique.go
Outdated
@@ -301,6 +304,18 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header, | |||
return c.verifyCascadingFields(chain, header, parents) | |||
} | |||
|
|||
// VerifyTransactions verifies a the transactions in a block do not exceed the per-transaction gas limit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// VerifyTransactions verifies a the transactions in a block do not exceed the per-transaction gas limit | |
// VerifyTransactions verifies that the transactions in a block do not exceed the per-transaction gas limit |
consensus/ethash/consensus.go
Outdated
@@ -169,6 +170,18 @@ func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*type | |||
return abort, errorsOut | |||
} | |||
|
|||
// VerifyTransactions verifies a the transactions in a block do not exceed the per-transaction gas limit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// VerifyTransactions verifies a the transactions in a block do not exceed the per-transaction gas limit | |
// VerifyTransactions verifies that the transactions in a block do not exceed the per-transaction gas limit |
core/types/block_test.go
Outdated
"github.com/ethereum/go-ethereum/params" | ||
|
||
//"fmt" | ||
//"github.com/ethereum/go-ethereum/params" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @ligi making these updates now!
This will get reviewed when the EIP is through ACD. |
consensus/clique/clique.go
Outdated
if header.BaseFee == nil { | ||
err = rlp.Encode(w, []interface{}{ | ||
header.ParentHash, | ||
header.UncleHash, | ||
header.Coinbase, | ||
header.Root, | ||
header.TxHash, | ||
header.ReceiptHash, | ||
header.Bloom, | ||
header.Difficulty, | ||
header.Number, | ||
header.GasLimit, | ||
header.GasUsed, | ||
header.Time, | ||
header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short | ||
header.MixDigest, | ||
header.Nonce, | ||
}) | ||
} else { | ||
err = rlp.Encode(w, []interface{}{ | ||
header.ParentHash, | ||
header.UncleHash, | ||
header.Coinbase, | ||
header.Root, | ||
header.TxHash, | ||
header.ReceiptHash, | ||
header.Bloom, | ||
header.Difficulty, | ||
header.Number, | ||
header.GasLimit, | ||
header.GasUsed, | ||
header.Time, | ||
header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short | ||
header.MixDigest, | ||
header.Nonce, | ||
header.BaseFee, | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't this be reduced to:
if header.BaseFee == nil { | |
err = rlp.Encode(w, []interface{}{ | |
header.ParentHash, | |
header.UncleHash, | |
header.Coinbase, | |
header.Root, | |
header.TxHash, | |
header.ReceiptHash, | |
header.Bloom, | |
header.Difficulty, | |
header.Number, | |
header.GasLimit, | |
header.GasUsed, | |
header.Time, | |
header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short | |
header.MixDigest, | |
header.Nonce, | |
}) | |
} else { | |
err = rlp.Encode(w, []interface{}{ | |
header.ParentHash, | |
header.UncleHash, | |
header.Coinbase, | |
header.Root, | |
header.TxHash, | |
header.ReceiptHash, | |
header.Bloom, | |
header.Difficulty, | |
header.Number, | |
header.GasLimit, | |
header.GasUsed, | |
header.Time, | |
header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short | |
header.MixDigest, | |
header.Nonce, | |
header.BaseFee, | |
}) | |
} | |
interfaces := []interface{}{ | |
header.ParentHash, | |
header.UncleHash, | |
header.Coinbase, | |
header.Root, | |
header.TxHash, | |
header.ReceiptHash, | |
header.Bloom, | |
header.Difficulty, | |
header.Number, | |
header.GasLimit, | |
header.GasUsed, | |
header.Time, | |
header.Extra[:len(header.Extra)-crypto.SignatureLength], // Yes, this will panic if extra is too short | |
header.MixDigest, | |
header.Nonce, | |
} | |
if header.BaseFee == nil { | |
inferfaces = append(interfaces, header.BaseFee | |
} | |
if err = rlp.Encode(w, interfaces); err != nil { | |
panic("can't encode: " + err.Error()) | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Love the reductions/simplifications, will add these changes. There's a missing closing parenthesis on the append
on line 752 so I can't commit the suggestions directly.
eth/gasprice/gasprice.go
Outdated
if err == nil && sender != block.Coinbase() { | ||
price := tx.GasPrice() | ||
if price == nil { | ||
price = new(big.Int).Add(block.BaseFee(), tx.GasPremium()) | ||
if price.Cmp(tx.FeeCap()) > 0 { | ||
price.Set(tx.FeeCap()) | ||
} | ||
} | ||
ch <- getBlockPricesResult{price, nil} | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if err == nil && sender != block.Coinbase() { | |
price := tx.GasPrice() | |
if price == nil { | |
price = new(big.Int).Add(block.BaseFee(), tx.GasPremium()) | |
if price.Cmp(tx.FeeCap()) > 0 { | |
price.Set(tx.FeeCap()) | |
} | |
} | |
ch <- getBlockPricesResult{price, nil} | |
return | |
} | |
if err == nil && sender != block.Coinbase() { | |
continue | |
} | |
price := tx.GasPrice() | |
if price == nil { | |
price = new(big.Int).Add(block.BaseFee(), tx.GasPremium()) | |
if price.Cmp(tx.FeeCap()) > 0 { | |
price.Set(tx.FeeCap()) | |
} | |
} | |
ch <- getBlockPricesResult{price, nil} | |
return |
To reduce the amount of nested if statements.
eth/gasprice/gasprice.go
Outdated
if err == nil && sender != block.Coinbase() { | ||
premium := tx.GasPremium() | ||
if premium == nil { | ||
premium = new(big.Int).Sub(tx.GasPrice(), block.BaseFee()) | ||
if premium.Cmp(common.Big0) < 0 { | ||
premium.Set(common.Big0) | ||
} | ||
} | ||
ch <- getBlockPremiumsResult{premium, nil} | ||
return | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if err == nil && sender != block.Coinbase() { | |
premium := tx.GasPremium() | |
if premium == nil { | |
premium = new(big.Int).Sub(tx.GasPrice(), block.BaseFee()) | |
if premium.Cmp(common.Big0) < 0 { | |
premium.Set(common.Big0) | |
} | |
} | |
ch <- getBlockPremiumsResult{premium, nil} | |
return | |
} | |
} | |
if err == nil && sender != block.Coinbase() { | |
continue | |
} | |
premium := tx.GasPremium() | |
if premium == nil { | |
premium = new(big.Int).Sub(tx.GasPrice(), block.BaseFee()) | |
if premium.Cmp(common.Big0) < 0 { | |
premium.Set(common.Big0) | |
} | |
} | |
ch <- getBlockPremiumsResult{premium, nil} | |
return | |
} |
Same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, although the conditions need to be switched on the continue
check.
consensus/misc/forks.go
Outdated
if header.BaseFee == nil { | ||
return errMissingBaseFee | ||
} | ||
delta := new(big.Int).Sub(new(big.Int).SetUint64(parent.GasUsed), new(big.Int).SetUint64(params.TargetGasUsed)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello,
I am wondering if there is an issue on the delta computation. The EIP says Let delta = block.gas_used - TARGET_GASUSED
.
I would expect to have header.GasUsed
instead of parent.GasUsed
. Am i missing something ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for bringing this up, this highlights an issue in the spec. It should clarify that we are using the delta
/gasUsed
of the current header to calculate the baseFee
of the next header; the parent to calculate the current. So in this function we are confirming that the baseFee
provided in a header is valid by deriving it from its parent using the consensus rules and checking that they are equal.
Currently, miners use the baseFee
of the header for the block they are working on to calculate the gasPrice
of the EIP1559 txs they include, so a header needs its baseFee
value known before its gasUsed
value is resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for bringing this up, this highlights an issue in the spec. It should clarify that we are using the
delta
/gasUsed
of the current header to calculate thebaseFee
of the next header; the parent to calculate the current. So in this function we are confirming that thebaseFee
provided in a header is valid by deriving it from its parent using the consensus rules and checking that they are equal.Currently, miners use the
baseFee
of the header for the block they are working on to calculate thegasPrice
of the EIP1559 txs they include, so a header needs itsbaseFee
value known before itsgasUsed
value is resolved.
Thanks for the clarification 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had time to take another look, thanks for doing this!
It's a bit large of a PR, if its possible it would be nice to make a temporary branch or fork so you can split this up into smaller PRs (if it's possible of course).
Mainly more code quality improvements, functionality wise I still have to look deeper to provide commentary there.
consensus/ethash/consensus.go
Outdated
if header.BaseFee == nil { | ||
rlp.Encode(hasher, []interface{}{ | ||
header.ParentHash, | ||
header.UncleHash, | ||
header.Coinbase, | ||
header.Root, | ||
header.TxHash, | ||
header.ReceiptHash, | ||
header.Bloom, | ||
header.Difficulty, | ||
header.Number, | ||
header.GasLimit, | ||
header.GasUsed, | ||
header.Time, | ||
header.Extra, | ||
}) | ||
} else { | ||
rlp.Encode(hasher, []interface{}{ | ||
header.ParentHash, | ||
header.UncleHash, | ||
header.Coinbase, | ||
header.Root, | ||
header.TxHash, | ||
header.ReceiptHash, | ||
header.Bloom, | ||
header.Difficulty, | ||
header.Number, | ||
header.GasLimit, | ||
header.GasUsed, | ||
header.Time, | ||
header.Extra, | ||
header.BaseFee, | ||
}) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if header.BaseFee == nil { | |
rlp.Encode(hasher, []interface{}{ | |
header.ParentHash, | |
header.UncleHash, | |
header.Coinbase, | |
header.Root, | |
header.TxHash, | |
header.ReceiptHash, | |
header.Bloom, | |
header.Difficulty, | |
header.Number, | |
header.GasLimit, | |
header.GasUsed, | |
header.Time, | |
header.Extra, | |
}) | |
} else { | |
rlp.Encode(hasher, []interface{}{ | |
header.ParentHash, | |
header.UncleHash, | |
header.Coinbase, | |
header.Root, | |
header.TxHash, | |
header.ReceiptHash, | |
header.Bloom, | |
header.Difficulty, | |
header.Number, | |
header.GasLimit, | |
header.GasUsed, | |
header.Time, | |
header.Extra, | |
header.BaseFee, | |
}) | |
} | |
encodedHeader := []interface{}{ | |
header.ParentHash, | |
header.UncleHash, | |
header.Coinbase, | |
header.Root, | |
header.TxHash, | |
header.ReceiptHash, | |
header.Bloom, | |
header.Difficulty, | |
header.Number, | |
header.GasLimit, | |
header.GasUsed, | |
header.Time, | |
header.Extra, | |
} | |
if header.BaseFee == nil { | |
encodedHeader= append(encodedHeader, header.BaseFee) | |
} | |
rlp.Encode(hasher, encodedHeader) | |
Can use a similar strategy here.
if tx.GasPrice() != nil { | ||
if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 { | ||
return false, nil | ||
} | ||
} else { | ||
eip1559 = true | ||
newGasPrice := new(big.Int).Add(baseFee, tx.GasPremium()) | ||
if newGasPrice.Cmp(tx.FeeCap()) > 0 { | ||
newGasPrice.Set(tx.FeeCap()) | ||
} | ||
if old.GasPrice().Cmp(newGasPrice) >= 0 || threshold.Cmp(newGasPrice) > 0 { | ||
return false, nil | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if tx.GasPrice() != nil { | |
if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 { | |
return false, nil | |
} | |
} else { | |
eip1559 = true | |
newGasPrice := new(big.Int).Add(baseFee, tx.GasPremium()) | |
if newGasPrice.Cmp(tx.FeeCap()) > 0 { | |
newGasPrice.Set(tx.FeeCap()) | |
} | |
if old.GasPrice().Cmp(newGasPrice) >= 0 || threshold.Cmp(newGasPrice) > 0 { | |
return false, nil | |
} | |
} | |
if tx.GasPrice() != nil && old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 { | |
return false, nil | |
} | |
eip1559 = true | |
newGasPrice := new(big.Int).Add(baseFee, tx.GasPremium()) | |
if newGasPrice.Cmp(tx.FeeCap()) > 0 { | |
newGasPrice.Set(tx.FeeCap()) | |
} | |
if old.GasPrice().Cmp(newGasPrice) >= 0 || threshold.Cmp(newGasPrice) > 0 { | |
return false, nil | |
} |
Lots of nested here so trying to clean it up. Would this work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Made all these suggested changes except this one as it's not quite equivalent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also agree that the code could be broken up much better, apologies. If I find the time I will do that. In the original repo there are PRs/issues which follow the progress on specific sections. It's not perfect since the process was somewhat iterative, but that does provide some more structure to the commits: https://github.com/vulcanize/go-ethereum-EIP1559/pulls?q=is%3Apr+is%3Aclosed
core/types/block.go
Outdated
h.Nonce, | ||
}) | ||
} | ||
return rlp.Encode(w, []interface{}{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar strategy as before here as well.
core/types/transaction_signing.go
Outdated
return rlpHash([]interface{}{ | ||
tx.data.AccountNonce, | ||
tx.data.Price, | ||
tx.data.GasLimit, | ||
tx.data.Recipient, | ||
tx.data.Amount, | ||
tx.data.Payload, | ||
tx.data.GasPremium, | ||
tx.data.FeeCap, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar strategy possible here too right?
core/types/transaction_signing.go
Outdated
return rlpHash([]interface{}{ | ||
tx.data.AccountNonce, | ||
tx.data.Price, | ||
tx.data.GasLimit, | ||
tx.data.Recipient, | ||
tx.data.Amount, | ||
tx.data.Payload, | ||
tx.data.GasPremium, | ||
tx.data.FeeCap, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here as well.
core/block_validator.go
Outdated
delta := new(big.Int).Sub(new(big.Int).SetUint64(parent.GasUsed()), new(big.Int).SetUint64(params.TargetGasUsed)) | ||
mul := new(big.Int).Mul(parent.BaseFee(), delta) | ||
div := new(big.Int).Div(mul, new(big.Int).SetUint64(params.TargetGasUsed)) | ||
div2 := new(big.Int).Div(div, new(big.Int).SetUint64(params.BaseFeeMaxChangeDenominator)) | ||
baseFee := new(big.Int).Add(parent.BaseFee(), div2) | ||
|
||
// A valid BASEFEE is one such that abs(BASEFEE - PARENT_BASEFEE) <= max(1, PARENT_BASEFEE // BASEFEE_MAX_CHANGE_DENOMINATOR) | ||
diff := new(big.Int).Sub(baseFee, parent.BaseFee()) | ||
neg := false | ||
if diff.Sign() < 0 { | ||
neg = true | ||
diff.Neg(diff) | ||
} | ||
max := new(big.Int).Div(parent.BaseFee(), new(big.Int).SetUint64(params.BaseFeeMaxChangeDenominator)) | ||
if max.Cmp(common.Big1) < 0 { | ||
max = common.Big1 | ||
} | ||
// If derived BaseFee is not valid, restrict it within the bounds | ||
if diff.Cmp(max) > 0 { | ||
if neg { | ||
max.Neg(max) | ||
} | ||
baseFee.Set(new(big.Int).Add(parent.BaseFee(), max)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could it be possible to have a common method to do that ? This is the same code used in consensus/misc/forks.go
9932405
to
7f98ea5
Compare
core/state_transition.go
Outdated
if st.isEIP1559 { | ||
// block.coinbase gains (gasprice - BASEFEE) * gasused | ||
coinBaseCredit := new(big.Int).Mul(new(big.Int).Sub(st.eip1559GasPrice, st.evm.BaseFee), new(big.Int).SetUint64(st.gasUsed())) | ||
// If gasprice < BASEFEE (due to the fee_cap), this means that the block.coinbase loses funds from this operation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we accept the block coinbase to lose funds from a transaction if gas price < BASEFEE because of fee cap ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Vitalik thinks we should not accept. I quote : problem with allowing that is that a miner could choose someone else as coinbase and drain their money
See https://discordapp.com/channels/595666850260713488/692078615269212180/703225803143643238
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the heads up! Making the adjustment now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW could you change the MaxGasEIP1559
to 20 million ? The EIP needs to be updated as well.
For context: https://discordapp.com/channels/595666850260713488/692078615269212180/705078808167841852
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you on the discord channel ? I am working on Besu implementation so that would be easier to sync our efforts if we could chat on discord ? Does that make sense ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated MaxGasEIP1559
! Yeah I am in the discord channel, let's coordinate there.
0ef5a0d
to
215c210
Compare
_, size, _ := s.Kind() | ||
err := s.Decode(&tx.data) | ||
if err == nil { | ||
func (tx *Transaction) DecodeRLP(stream *rlp.Stream) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What we've historically done in these kinds of situations, is to first attempt to decode into one, and then the other. E.g.
var newTx Eip1559Transaction
var oldTx Transaction
if err := s.Decode( &newTx); err != nil{
err = s.Decode(&oldTx)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recall trying this approach originally and running into issues but it's been so long that I don't recall the exact nature of them. A quick conversion to this is yielding errors in unit tests but I need to look into it more!
…we set EIP1559 params by CLI
4345d31
to
a683326
Compare
a683326
to
0757426
Compare
Closing in favor of the updated version at #21971 |
This PR implements EIP-1559. The updated documentation for EIP-1559 can be found here. The magicians thread for this implementation can be found here. I'll continue to incorporate feedback from that thread in addition to the review received here. Thanks!
Edit: A mirror repo was created to track progress on this EIP with issues/PRs before switching over to a forked branch to check the CI prior to opening this PR. Those issues/PRs may be helpful in review/duplicating the code but keep in mind compartmentalization was not perfect and there have been updates on this branch since the PR has been opened.