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

Implement EIP1559 Fee Market + EIP3198 BaseFee #1148

Merged
merged 86 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
0a10b32
tx: implement EIP1559 transaction base body
jochem-brouwer Mar 14, 2021
c335254
tx: remove gasPrice from baseTransaction
jochem-brouwer Mar 18, 2021
9c0e7af
tx: fix build problems
jochem-brouwer Mar 24, 2021
2e248ce
common: add eip1559
jochem-brouwer Mar 24, 2021
673abf9
block: integrate base fee EIP1559 param in header
jochem-brouwer Mar 24, 2021
efc5dc7
common: add getEIPActivationBlockNumber
jochem-brouwer Mar 24, 2021
c8beb6c
common: add EIP1559 constants
jochem-brouwer Mar 24, 2021
a685cd2
block header: implement EIP1559 validation checks
jochem-brouwer Mar 24, 2021
93def9d
block: add baseFee tests
jochem-brouwer Mar 25, 2021
0b0ab13
block: fix bug EIP1559
jochem-brouwer Mar 25, 2021
106376f
tx: add factory support for EIP1559
jochem-brouwer Mar 26, 2021
cfc6f3e
vm: implement EIP1559
jochem-brouwer Mar 26, 2021
28a1e37
vm: implement EIP1559 test
jochem-brouwer Mar 26, 2021
9b261a9
tx/block: fix tests
jochem-brouwer Mar 27, 2021
d160b0e
tx/vm: fix EIP2930 toJSON, expand EIP1559 tests
jochem-brouwer Mar 27, 2021
a89b9b2
vm: add test to check if GASPRICE reports effective price
jochem-brouwer Mar 31, 2021
0cc73b4
tx: fixed additional BaseTransaction constructor conflict, added gasP…
holgerd77 Apr 23, 2021
68f3cc0
tx: added hashMessage parameter to EIP1559 tx getMessageToSign() method
holgerd77 Apr 23, 2021
3af8139
tx: fixed emptyBuffer accessList default value bug in EIP-1559 tx, al…
holgerd77 Apr 23, 2021
25786ea
tx: added tx type injection in the super() call in EIP-1559 tx constr…
holgerd77 Apr 23, 2021
e081629
tx: fixed BasedTransaction default value initializatioon rebase bug, …
holgerd77 Apr 23, 2021
5965296
tx: fix EIP-2930 signed tx toJSON() output comparison test
holgerd77 Apr 23, 2021
03d2e0a
block/tx: fix merge conflict + lint
jochem-brouwer Apr 23, 2021
421dedc
block/vm: fix tests
jochem-brouwer Apr 25, 2021
c8b2551
common: add london
jochem-brouwer Apr 25, 2021
4184f4e
block/tx: EIP1559 support fixes
jochem-brouwer Apr 25, 2021
ec65ff8
vm: add cliqueBeneficiary to runTx
jochem-brouwer Apr 26, 2021
cda880e
client: validateConsensus set to true
jochem-brouwer Apr 26, 2021
62b9a51
block: apply suggestions from code review
holgerd77 Apr 27, 2021
45a9d50
block: fix EIP1559 maxFee/baseFee check bug
jochem-brouwer Apr 28, 2021
43c4ee9
vm: fix EIP1559 gas limit bug
jochem-brouwer Apr 28, 2021
a127ff9
vm/common: implement EIP3198
jochem-brouwer Apr 28, 2021
afd00a8
devp2p: fix forkhash bug in case no future fork known
jochem-brouwer Apr 28, 2021
a98f9c5
block/common: fix block tests
jochem-brouwer Apr 28, 2021
a8acbaf
common: fix gteHardfork on undefined/new HFs
jochem-brouwer Apr 28, 2021
052242c
tx: add eip1559 tests
jochem-brouwer Apr 29, 2021
90bf0b0
common: switched to berlin HF as minimum HF on EIP-1559 definition fi…
holgerd77 May 6, 2021
8caf9cd
tx -> types: aligned EIP1559 tx import, removed redundant type field …
holgerd77 May 6, 2021
40e0e28
tx -> factory, types: improved type safety for TransactionFactory.fro…
holgerd77 May 6, 2021
fee683e
tx -> EIP2930Transaction: added max integer check for r and s values …
holgerd77 May 6, 2021
2fc7b37
tx: set legacy tx type in LegacyTransaction and not in BaseTransactio…
holgerd77 May 6, 2021
f3ee86c
tx: removed redundant transactionType() method from AccessListEIP2930…
holgerd77 May 6, 2021
9f051e8
vm: fixed a bug not taking in EIP1559 txs as access list txs in VM.ru…
holgerd77 May 6, 2021
b771a9f
vm, tx, block: removed tx.getEIP1559Data() calls in favor of tx type …
holgerd77 May 6, 2021
442d59f
block: remove public getCommon() API method, set explicit base fee de…
holgerd77 May 7, 2021
aa67b9a
block -> EIP-1559: include gas target checks into validateGasLimit() …
holgerd77 May 7, 2021
9b71f42
block -> EIP-1559: use parent block header as parameter for getBaseFe…
holgerd77 May 7, 2021
199670f
block: reworked getBaseFee() API -> calcNextBaseFee()
holgerd77 May 7, 2021
4637355
block: update eip1559 to latest spec
jochem-brouwer May 8, 2021
6dcdbf6
block: minor EIP-1559 check cleanup in BlockHeader.validate(), moved …
holgerd77 May 9, 2021
af14533
block: moved baseFeePerGas to be the last option in constructor for b…
holgerd77 May 9, 2021
3361b79
block: added baseFee to the toJSON() header/block output
holgerd77 May 9, 2021
a000741
vm: added missing supported EIPs to code docs, switch to EIP-2718 che…
holgerd77 May 9, 2021
7c50ed9
tx: moved r, s max length validation to BaseTransaction
holgerd77 May 9, 2021
00bbfa3
tx: improved type documentation, added TxValuesArray type alias
holgerd77 May 10, 2021
1587103
tx -> EIP-1559: initialize base fee with 0 for EIP-1559 txs on getUpf…
holgerd77 May 10, 2021
a47046a
tx: exclude src/util.ts from typedoc doc generation
holgerd77 May 10, 2021
08519a3
tx -> EIP-1559: minor clean ups
holgerd77 May 10, 2021
281779a
common: add baikal
jochem-brouwer May 10, 2021
ccea383
common: added additional bootnodes for the baikal test network
holgerd77 May 10, 2021
300c515
common: fixed mainnet berlin forkHash, added baikal london forkHash, …
holgerd77 May 11, 2021
2090d0a
common: fixed forkHash calculation for fork hashes with a leading zero
holgerd77 May 11, 2021
2786d45
block, tx: minor fixes and optimizations
holgerd77 May 11, 2021
ddd0181
common: removed getEIPActivationBlockNumber() function (replaced by e…
holgerd77 May 11, 2021
94e0d90
tx: added EIP-1559 tx to base tx functionality test execution
holgerd77 May 11, 2021
f5fd539
tx: reworked and generalized transaction factory tests
holgerd77 May 11, 2021
9c847d7
tx: generalized EIP-2930 tests and separated into functionality (also…
holgerd77 May 11, 2021
0438b88
tx: simplified getUpfrontCost() for non-EIP1559 txs, added tests, tes…
holgerd77 May 12, 2021
3f8f382
tx: fixed critical bug in isSigned() for typed txs returning false fo…
holgerd77 May 12, 2021
d62191f
tx: added an additional EIP-1559 hash() method test
holgerd77 May 12, 2021
a6c9074
tx: added EIP-1559 tx toJSON() test
holgerd77 May 12, 2021
e93165a
block: block header test cleanup, additional EIP-1559 initialization …
holgerd77 May 12, 2021
0412bf2
block, tx: added subsequent EIP-1559 blocks block validation test, ad…
holgerd77 May 12, 2021
0939df5
block -> EIP-1559: added unified gasLimit check logic, gasLimit elast…
holgerd77 May 17, 2021
fc6eade
blockchain: properly set Common HF on initialization and chain updates
holgerd77 May 17, 2021
e939bd5
client: take chain common HF setting from blockchain, directly use ha…
holgerd77 May 17, 2021
2e0b38f
runBlock/runTx: add tests for 1559 txs
ryanio May 12, 2021
06cbb40
client: added eth_sendRawTransaction RPC call (parsing the tx part), …
holgerd77 May 18, 2021
86b9303
client: added simple tx send mechanism for eth_sendRawTransaction RPC…
holgerd77 May 18, 2021
37c3d55
tx, vm, block: renamed maxInclusionFeePerGas -> maxPriorityFeePerGas
holgerd77 May 19, 2021
b03124d
monorepo: updated package-lock.json
holgerd77 May 19, 2021
5de7a2f
monorepo: fixed Prettier linting by re-linting with Prettier v2.3.0
holgerd77 May 19, 2021
e71bb60
vm, blockchain: avoid HF setting VM side effects in blockchain by saf…
holgerd77 May 19, 2021
0eae159
client: renamed RPC sendRawTransaction test file to be included in th…
holgerd77 May 19, 2021
22ecae5
client: only send tx in sendRawTransaction RPC method to peers if eth…
holgerd77 May 19, 2021
99cb89f
blockchain: deprecated main constructor, added tests for hardforkByHe…
holgerd77 May 19, 2021
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
27,660 changes: 1,064 additions & 26,596 deletions package-lock.json

Large diffs are not rendered by default.

26 changes: 22 additions & 4 deletions packages/block/src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@
import { BaseTrie as Trie } from 'merkle-patricia-tree'
import { BN, rlp, keccak256, KECCAK256_RLP } from 'ethereumjs-util'
import Common from '@ethereumjs/common'
import { TransactionFactory, TypedTransaction, TxOptions } from '@ethereumjs/tx'
import {
TransactionFactory,
TypedTransaction,
TxOptions,
FeeMarketEIP1559Transaction,
Transaction,
} from '@ethereumjs/tx'
import { BlockHeader } from './header'
import { BlockData, BlockOptions, JsonBlock, BlockBuffer, Blockchain } from './types'

Expand Down Expand Up @@ -63,7 +69,7 @@ export class Block {
* @param opts
*/
public static fromRLPSerializedBlock(serialized: Buffer, opts?: BlockOptions) {
const values = (rlp.decode(serialized) as any) as BlockBuffer
const values = rlp.decode(serialized) as any as BlockBuffer

if (!Array.isArray(values)) {
throw new Error('Invalid serialized block input. Must be array')
Expand Down Expand Up @@ -223,9 +229,21 @@ export class Block {
validateTransactions(stringError: true): string[]
validateTransactions(stringError = false) {
const errors: string[] = []

this.transactions.forEach(function (tx, i) {
this.transactions.forEach((tx, i) => {
const errs = <string[]>tx.validate(true)
if (this._common.isActivatedEIP(1559)) {
if (tx.transactionType === 2) {
tx = tx as FeeMarketEIP1559Transaction
if (tx.maxFeePerGas.lt(this.header.baseFeePerGas!)) {
errs.push('tx unable to pay base fee (EIP-1559 tx)')
}
} else {
tx = tx as Transaction
if (tx.gasPrice.lt(this.header.baseFeePerGas!)) {
errs.push('tx unable to pay base fee (non EIP-1559 tx)')
}
}
}
if (errs.length > 0) {
errors.push(`errors at tx ${i}: ${errs.join(', ')}`)
}
Expand Down
138 changes: 120 additions & 18 deletions packages/block/src/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class BlockHeader {
public readonly extraData: Buffer
public readonly mixHash: Buffer
public readonly nonce: Buffer
public readonly baseFeePerGas?: BN

public readonly _common: Common
public _errorPostfix = ''
Expand All @@ -53,7 +54,7 @@ export class BlockHeader {
* @param headerData
* @param opts
*/
public static fromHeaderData(headerData: HeaderData = {}, opts?: BlockOptions) {
public static fromHeaderData(headerData: HeaderData = {}, opts: BlockOptions = {}) {
const {
parentHash,
uncleHash,
Expand All @@ -70,6 +71,7 @@ export class BlockHeader {
extraData,
mixHash,
nonce,
baseFeePerGas,
} = headerData

return new BlockHeader(
Expand All @@ -88,7 +90,8 @@ export class BlockHeader {
extraData ? toBuffer(extraData) : Buffer.from([]),
mixHash ? toBuffer(mixHash) : zeros(32),
nonce ? toBuffer(nonce) : zeros(8),
opts
opts,
baseFeePerGas !== undefined ? new BN(toBuffer(baseFeePerGas)) : undefined
)
}

Expand All @@ -98,7 +101,7 @@ export class BlockHeader {
* @param headerData
* @param opts
*/
public static fromRLPSerializedHeader(serialized: Buffer, opts?: BlockOptions) {
public static fromRLPSerializedHeader(serialized: Buffer, opts: BlockOptions = {}) {
const values = rlp.decode(serialized)

if (!Array.isArray(values)) {
Expand All @@ -114,11 +117,7 @@ export class BlockHeader {
* @param headerData
* @param opts
*/
public static fromValuesArray(values: BlockHeaderBuffer, opts?: BlockOptions) {
if (values.length > 15) {
throw new Error('invalid header. More values than expected were received')
}

public static fromValuesArray(values: BlockHeaderBuffer, opts: BlockOptions = {}) {
const [
parentHash,
uncleHash,
Expand All @@ -135,8 +134,16 @@ export class BlockHeader {
extraData,
mixHash,
nonce,
baseFeePerGas,
] = values

if (values.length > 16) {
throw new Error('invalid header. More values than expected were received')
}
if (values.length < 15) {
throw new Error('invalid header. Less values than expected were received')
}

return new BlockHeader(
toBuffer(parentHash),
toBuffer(uncleHash),
Expand All @@ -153,7 +160,8 @@ export class BlockHeader {
toBuffer(extraData),
toBuffer(mixHash),
toBuffer(nonce),
opts
opts,
baseFeePerGas !== undefined ? new BN(toBuffer(baseFeePerGas)) : undefined
)
}

Expand All @@ -167,9 +175,10 @@ export class BlockHeader {

/**
* This constructor takes the values, validates them, assigns them and freezes the object.
* Use the public static factory methods to assist in creating a Header object from
* varying data types.
* For a default empty header, use `BlockHeader.fromHeaderData()`.
*
* @deprecated - Use the public static factory methods to assist in creating a Header object from
* varying data types. For a default empty header, use `BlockHeader.fromHeaderData()`.
*
*/
constructor(
parentHash: Buffer,
Expand All @@ -187,7 +196,8 @@ export class BlockHeader {
extraData: Buffer,
mixHash: Buffer,
nonce: Buffer,
options: BlockOptions = {}
options: BlockOptions = {},
baseFeePerGas?: BN
holgerd77 marked this conversation as resolved.
Show resolved Hide resolved
) {
if (options.common) {
this._common = Object.assign(
Expand All @@ -208,6 +218,16 @@ export class BlockHeader {
this._common.setHardforkByBlockNumber(number.toNumber())
}

if (this._common.isActivatedEIP(1559)) {
if (baseFeePerGas === undefined) {
baseFeePerGas = new BN(7)
}
} else {
if (baseFeePerGas) {
throw new Error('A base fee for a block can only be set with EIP1559 being activated')
}
}

if (options.initWithGenesisHeader) {
number = new BN(0)
if (gasLimit.eq(DEFAULT_GAS_LIMIT)) {
Expand Down Expand Up @@ -245,6 +265,7 @@ export class BlockHeader {
this.extraData = extraData
this.mixHash = mixHash
this.nonce = nonce
this.baseFeePerGas = baseFeePerGas

this._validateHeaderFields()
this._checkDAOExtraData()
Expand Down Expand Up @@ -430,7 +451,13 @@ export class BlockHeader {
* @param parentBlockHeader - the header from the parent `Block` of this header
*/
validateGasLimit(parentBlockHeader: BlockHeader): boolean {
const parentGasLimit = parentBlockHeader.gasLimit
let parentGasLimit = parentBlockHeader.gasLimit
// EIP-1559: assume double the parent gas limit on fork block
// to adopt to the new gas target centered logic
if (this.number.eq(this._common.hardforkBlockBN('london'))) {
const elasticity = new BN(this._common.param('gasConfig', 'elasticityMultiplier'))
parentGasLimit = parentGasLimit.mul(elasticity)
}
const gasLimit = this.gasLimit
const hardfork = this._getHardfork()

Expand All @@ -440,11 +467,12 @@ export class BlockHeader {
const maxGasLimit = parentGasLimit.add(a)
const minGasLimit = parentGasLimit.sub(a)

return (
const result =
gasLimit.lt(maxGasLimit) &&
gasLimit.gt(minGasLimit) &&
gasLimit.gte(this._common.paramByHardfork('gasConfig', 'minGasLimit', hardfork))
)

return result
}

/**
Expand All @@ -469,14 +497,17 @@ export class BlockHeader {
return
}
const hardfork = this._getHardfork()
// Consensus type dependent checks
if (this._common.consensusAlgorithm() !== 'clique') {
// PoW/Ethash
if (
this.extraData.length > this._common.paramByHardfork('vm', 'maxExtraDataSize', hardfork)
) {
const msg = 'invalid amount of extra data'
throw this._error(msg)
}
} else {
// PoA/Clique
const minLength = CLIQUE_EXTRA_VANITY + CLIQUE_EXTRA_SEAL
if (!this.cliqueIsEpochTransition()) {
// ExtraData length on epoch transition
Expand Down Expand Up @@ -546,13 +577,73 @@ export class BlockHeader {
throw new Error('uncle block has a parent that is too old or too young')
}
}

// check if the block used too much gas
if (this.gasUsed.gt(this.gasLimit)) {
throw new Error('Invalid block: too much gas used')
}

if (this._common.isActivatedEIP(1559)) {
if (!this.baseFeePerGas) {
throw new Error('EIP1559 block has no base fee field')
}
const isInitialEIP1559Block = this.number.eq(this._common.hardforkBlockBN('london'))
if (isInitialEIP1559Block) {
const initialBaseFee = new BN(this._common.param('gasConfig', 'initialBaseFee'))
if (!this.baseFeePerGas!.eq(initialBaseFee)) {
throw new Error('Initial EIP1559 block does not have initial base fee')
}
} else {
// check if the base fee is correct
const expectedBaseFee = parentHeader.calcNextBaseFee()

if (!this.baseFeePerGas!.eq(expectedBaseFee)) {
throw new Error('Invalid block: base fee not correct')
holgerd77 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

/**
* Calculates the base fee for a potential next block
*/
public calcNextBaseFee(): BN {
if (!this._common.isActivatedEIP(1559)) {
throw new Error('calcNextBaseFee() can only be called with EIP1559 being activated')
}
let nextBaseFee: BN
const elasticity = new BN(this._common.param('gasConfig', 'elasticityMultiplier'))
const parentGasTarget = this.gasLimit.div(elasticity)

if (parentGasTarget.eq(this.gasUsed)) {
nextBaseFee = this.baseFeePerGas!
} else if (this.gasUsed.gt(parentGasTarget)) {
const gasUsedDelta = this.gasUsed.sub(parentGasTarget)
const baseFeeMaxChangeDenominator = new BN(
this._common.param('gasConfig', 'baseFeeMaxChangeDenominator')
)
const calculatedDelta = this.baseFeePerGas!.mul(gasUsedDelta)
.div(parentGasTarget)
.div(baseFeeMaxChangeDenominator)
nextBaseFee = BN.max(calculatedDelta, new BN(1)).add(this.baseFeePerGas!)
} else {
const gasUsedDelta = parentGasTarget.sub(this.gasUsed)
const baseFeeMaxChangeDenominator = new BN(
this._common.param('gasConfig', 'baseFeeMaxChangeDenominator')
)
const calculatedDelta = this.baseFeePerGas!.mul(gasUsedDelta)
.div(parentGasTarget)
.div(baseFeeMaxChangeDenominator)
nextBaseFee = BN.max(this.baseFeePerGas!.sub(calculatedDelta), new BN(0))
}
return nextBaseFee
}

/**
* Returns a Buffer Array of the raw Buffers in this header, in order.
*/
raw(): BlockHeaderBuffer {
return [
const rawItems = [
this.parentHash,
this.uncleHash,
this.coinbase.buf,
Expand All @@ -569,6 +660,12 @@ export class BlockHeader {
this.mixHash,
this.nonce,
]

if (this._common.isActivatedEIP(1559)) {
rawItems.push(unpadBuffer(toBuffer(this.baseFeePerGas)))
holgerd77 marked this conversation as resolved.
Show resolved Hide resolved
}

return rawItems
}

/**
Expand Down Expand Up @@ -716,7 +813,7 @@ export class BlockHeader {
* Returns the block header in JSON format.
*/
toJSON(): JsonHeader {
return {
const jsonDict: JsonHeader = {
parentHash: '0x' + this.parentHash.toString('hex'),
uncleHash: '0x' + this.uncleHash.toString('hex'),
coinbase: this.coinbase.toString(),
Expand All @@ -733,6 +830,11 @@ export class BlockHeader {
mixHash: '0x' + this.mixHash.toString('hex'),
nonce: '0x' + this.nonce.toString('hex'),
}
if (this._common.isActivatedEIP(1559)) {
jsonDict['baseFee'] = '0x' + this.baseFeePerGas!.toString('hex')
}

return jsonDict
}

/**
Expand Down
6 changes: 4 additions & 2 deletions packages/block/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AddressLike, BNLike, BufferLike } from 'ethereumjs-util'
import Common from '@ethereumjs/common'
import { AccessListEIP2930TxData, TxData, JsonTx } from '@ethereumjs/tx'
import { TxData, JsonTx, AccessListEIP2930TxData, FeeMarketEIP1559TxData } from '@ethereumjs/tx'
import { Block } from './block'
import { BlockHeader } from './header'

Expand Down Expand Up @@ -83,6 +83,7 @@ export interface HeaderData {
extraData?: BufferLike
mixHash?: BufferLike
nonce?: BufferLike
baseFeePerGas?: BNLike
}

/**
Expand All @@ -93,7 +94,7 @@ export interface BlockData {
* Header data for the block
*/
header?: HeaderData
transactions?: Array<TxData | AccessListEIP2930TxData>
transactions?: Array<TxData | AccessListEIP2930TxData | FeeMarketEIP1559TxData>
uncleHeaders?: Array<HeaderData>
}

Expand Down Expand Up @@ -137,6 +138,7 @@ export interface JsonHeader {
extraData?: string
mixHash?: string
nonce?: string
baseFee?: string
}

export interface Blockchain {
Expand Down
Loading