Skip to content

Commit

Permalink
Merge 9c6f2f6 into 93c1110
Browse files Browse the repository at this point in the history
  • Loading branch information
jochem-brouwer authored Mar 2, 2021
2 parents 93c1110 + 9c6f2f6 commit 8e02910
Show file tree
Hide file tree
Showing 41 changed files with 2,049 additions and 494 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[submodule "ethereum-tests"]
path = packages/ethereum-tests
url = https://github.com/ethereum/tests.git
url = https://github.com/qbzzt/tests.git
branch = develop
4 changes: 4 additions & 0 deletions packages/block/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
(modification: no type change headlines) and this project adheres to
[Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## UNRELEASED

- Integration of [EIP2718](https://eips.ethereum.org/EIPS/eip-2718) (Typed Transactions) and [EIP2930](https://eips.ethereum.org/EIPS/eip-2930) (Access List Transaction), PR [#1048](https://github.com/ethereumjs/ethereumjs-monorepo/pull/1048). It is now possible to create blocks with access list transactions.

## 3.1.0 - 2021-02-22

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

Expand Down Expand Up @@ -31,7 +31,7 @@ export class Block {
// parse transactions
const transactions = []
for (const txData of txsData || []) {
const tx = Transaction.fromTxData(txData, {
const tx = TransactionFactory.fromTxData(txData, {
...opts,
// Use header common in case of hardforkByBlockNumber being activated
common: header._common,
Expand Down Expand Up @@ -91,7 +91,7 @@ export class Block {
const transactions = []
for (const txData of txsData || []) {
transactions.push(
Transaction.fromValuesArray(txData, {
TransactionFactory.fromBlockBodyData(txData, {
...opts,
// Use header common in case of hardforkByBlockNumber being activated
common: header._common,
Expand Down Expand Up @@ -154,7 +154,7 @@ export class Block {
raw(): BlockBuffer {
return [
this.header.raw(),
this.transactions.map((tx) => tx.raw()),
this.transactions.map((tx) => <Buffer[]>tx.raw()),
this.uncleHeaders.map((uh) => uh.raw()),
]
}
Expand Down Expand Up @@ -223,7 +223,7 @@ export class Block {
const errors: string[] = []

this.transactions.forEach(function (tx, i) {
const errs = tx.validate(true)
const errs = <string[]>tx.validate(true)
if (errs.length > 0) {
errors.push(`errors at tx ${i}: ${errs.join(', ')}`)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/block/src/from-rpc.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Transaction, TxData } from '@ethereumjs/tx'
import { TransactionFactory, Transaction, TxData } from '@ethereumjs/tx'
import { toBuffer, setLengthLeft } from 'ethereumjs-util'
import { Block, BlockOptions } from './index'

Expand Down Expand Up @@ -37,7 +37,7 @@ export default function blockFromRpc(blockParams: any, uncles: any[] = [], optio
const opts = { common: header._common }
for (const _txParams of blockParams.transactions) {
const txParams = normalizeTxParams(_txParams)
const tx = Transaction.fromTxData(txParams as TxData, opts)
const tx = TransactionFactory.fromTxData(txParams as TxData, opts)
transactions.push(tx)
}
}
Expand Down
12 changes: 12 additions & 0 deletions packages/common/src/eips/2718.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "EIP-2718",
"comment": "Typed Transaction Envelope",
"url": "https://eips.ethereum.org/EIPS/eip-2718",
"status": "Draft",
"minimumHardfork": "chainstart",
"gasConfig": {},
"gasPrices": {},
"vm": {},
"pow": {}
}

22 changes: 22 additions & 0 deletions packages/common/src/eips/2930.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "EIP-2929",
"comment": "Optional access lists",
"url": "https://eips.ethereum.org/EIPS/eip-2930",
"status": "Draft",
"minimumHardfork": "istanbul",
"requiredEIPs": [2718, 2929],
"gasConfig": {},
"gasPrices": {
"accessListStorageKeyCost": {
"v": 1900,
"d": "Gas cost per storage key in an Access List transaction"
},
"accessListAddressCost": {
"v": 2400,
"d": "Gas cost per storage key in an Access List transaction"
}
},
"vm": {},
"pow": {}
}

2 changes: 2 additions & 0 deletions packages/common/src/eips/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ export const EIPs: eipsType = {
2315: require('./2315.json'),
2537: require('./2537.json'),
2565: require('./2565.json'),
2718: require('./2718.json'),
2929: require('./2929.json'),
2930: require('./2930.json'),
}
2 changes: 1 addition & 1 deletion packages/common/src/hardforks/berlin.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"comment": "HF targeted for July 2020 following the Muir Glacier HF",
"url": "https://eips.ethereum.org/EIPS/eip-2070",
"status": "Draft",
"eips": [ 2315, 2565, 2929 ]
"eips": [ 2315, 2565, 2929, 2718, 2930 ]
}
8 changes: 8 additions & 0 deletions packages/common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,14 @@ export default class Common extends EventEmitter {
`${eip} cannot be activated on hardfork ${this.hardfork()}, minimumHardfork: ${minHF}`
)
}
if (EIPs[eip].requiredEIPs) {
// eslint-disable-next-line prettier/prettier
(<number[]>EIPs[eip].requiredEIPs).forEach((elem: number) => {
if (!(eips.includes(elem) || this.isActivatedEIP(elem))) {
throw new Error(`${eip} requires EIP ${elem}, but is not included in the EIP list`)
}
})
}
}
this._eips = eips
}
Expand Down
15 changes: 14 additions & 1 deletion packages/common/tests/eips.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,22 @@ import Common from '../src/'

tape('[Common]: Initialization / Chain params', function (t: tape.Test) {
t.test('Correct initialization', function (st: tape.Test) {
const eips = [2537, 2929]
let eips = [2537, 2929]
const c = new Common({ chain: 'mainnet', eips })
st.equal(c.eips(), eips, 'should initialize with supported EIP')

eips = [2718, 2929, 2930]
new Common({ chain: 'mainnet', eips, hardfork: 'istanbul' })
st.pass('Should not throw when initializing with a consistent EIP list')

eips = [2930]
const msg =
'should throw when initializing with an EIP with required EIPs not being activated along'
const f = () => {
new Common({ chain: 'mainnet', eips, hardfork: 'istanbul' })
}
st.throws(f, msg)

st.end()
})

Expand Down
2 changes: 1 addition & 1 deletion packages/ethereum-tests
10 changes: 10 additions & 0 deletions packages/tx/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
(modification: no type change headlines) and this project adheres to
[Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## UNRELEASED

- Integration of [EIP2718](https://eips.ethereum.org/EIPS/eip-2718) (Typed Transactions) and [EIP2930](https://eips.ethereum.org/EIPS/eip-2930) (Access List Transaction), PR [#1048](https://github.com/ethereumjs/ethereumjs-monorepo/pull/1048).

This PR integrates the Typed Transactions. In order to produce the right transactions, there is a new class called the `TransactionFactory`. This factory helps to create the correct transaction. When decoding directly from blocks, use the `fromBlockBodyData` method. The PR also refactors the internals of the legacy transaction and the new typed transaction: there is a base transaction class, which both transaction classes extends. It is also possible to import `EIP2930Transaction` (an access list transaction).

### How to migrate

The old `Transaction` class has been renamed to `LegacyTransaction`. This class works just how it used to work.


## 3.0.2 - 2021-02-16

Expand Down
2 changes: 1 addition & 1 deletion packages/tx/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = function (config) {
browserNoActivityTimeout: 60000,
frameworks: ['browserify', 'tap'],
// the official transaction's test suite is disabled for now, see https://github.com/ethereumjs/ethereumjs-testing/issues/40
files: ['./test-build/test/api.js'],
files: ['./test-build/test/legacy.spec.js'],
preprocessors: {
'./test-build/**/*.js': ['browserify'],
},
Expand Down
195 changes: 195 additions & 0 deletions packages/tx/src/baseTransaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import Common from '@ethereumjs/common'
import {
Address,
BN,
toBuffer,
MAX_INTEGER,
unpadBuffer,
ecsign,
publicToAddress,
} from 'ethereumjs-util'
import { BaseTransactionData, BaseTxOptions, DEFAULT_COMMON, JsonTx } from './types'

export abstract class BaseTransaction<TransactionObject> {
public readonly nonce: BN
public readonly gasLimit: BN
public readonly gasPrice: BN
public readonly to?: Address
public readonly value: BN
public readonly data: Buffer
public readonly common: Common

constructor(txData: BaseTransactionData, txOptions: BaseTxOptions = {}) {
const { nonce, gasLimit, gasPrice, to, value, data } = txData

this.nonce = new BN(toBuffer(nonce))
this.gasPrice = new BN(toBuffer(gasPrice))
this.gasLimit = new BN(toBuffer(gasLimit))
this.to = to ? new Address(toBuffer(to)) : undefined
this.value = new BN(toBuffer(value))
this.data = toBuffer(data)

const validateCannotExceedMaxInteger = {
nonce: this.nonce,
gasPrice: this.gasPrice,
gasLimit: this.gasLimit,
value: this.value,
}

this.validateExceedsMaxInteger(validateCannotExceedMaxInteger)

this.common =
(txOptions.common &&
Object.assign(Object.create(Object.getPrototypeOf(txOptions.common)), txOptions.common)) ??
DEFAULT_COMMON
}

protected validateExceedsMaxInteger(validateCannotExceedMaxInteger: { [key: string]: BN }) {
for (const [key, value] of Object.entries(validateCannotExceedMaxInteger)) {
if (value && value.gt(MAX_INTEGER)) {
throw new Error(`${key} cannot exceed MAX_INTEGER, given ${value}`)
}
}
}

/**
* If the tx's `to` is to the creation address
*/
toCreationAddress(): boolean {
return this.to === undefined || this.to.buf.length === 0
}

/**
* Computes a sha3-256 hash of the serialized unsigned tx, which is used to sign the transaction.
*/
rawTxHash(): Buffer {
return this.getMessageToSign()
}

abstract getMessageToSign(): Buffer

/**
* Returns chain ID
*/
getChainId(): number {
return this.common.chainId()
}

/**
* The amount of gas paid for the data in this tx
*/
getDataFee(): BN {
const txDataZero = this.common.param('gasPrices', 'txDataZero')
const txDataNonZero = this.common.param('gasPrices', 'txDataNonZero')

let cost = 0
for (let i = 0; i < this.data.length; i++) {
this.data[i] === 0 ? (cost += txDataZero) : (cost += txDataNonZero)
}
return new BN(cost)
}

/**
* The minimum amount of gas the tx must have (DataFee + TxFee + Creation Fee)
*/
getBaseFee(): BN {
const fee = this.getDataFee().addn(this.common.param('gasPrices', 'tx'))
if (this.common.gteHardfork('homestead') && this.toCreationAddress()) {
fee.iaddn(this.common.param('gasPrices', 'txCreation'))
}
return fee
}

/**
* The up front amount that an account must have for this transaction to be valid
*/
getUpfrontCost(): BN {
return this.gasLimit.mul(this.gasPrice).add(this.value)
}

/**
* Checks if the transaction has the minimum amount of gas required
* (DataFee + TxFee + Creation Fee).
*/
/**
* Checks if the transaction has the minimum amount of gas required
* (DataFee + TxFee + Creation Fee).
*/
validate(): boolean
/* eslint-disable-next-line no-dupe-class-members */
validate(stringError: false): boolean
/* eslint-disable-next-line no-dupe-class-members */
validate(stringError: true): string[]
/* eslint-disable-next-line no-dupe-class-members */
validate(stringError: boolean = false): boolean | string[] {
const errors = []

if (this.getBaseFee().gt(this.gasLimit)) {
errors.push(`gasLimit is too low. given ${this.gasLimit}, need at least ${this.getBaseFee()}`)
}

if (this.isSigned() && !this.verifySignature()) {
errors.push('Invalid Signature')
}

return stringError ? errors : errors.length === 0
}

/**
* Returns the encoding of the transaction.
*/
abstract serialize(): Buffer

/**
* Returns an object with the JSON representation of the transaction
*/
abstract toJSON(): JsonTx

abstract isSigned(): boolean

/**
* Determines if the signature is valid
*/
verifySignature(): boolean {
try {
// Main signature verification is done in `getSenderPublicKey()`
const publicKey = this.getSenderPublicKey()
return unpadBuffer(publicKey).length !== 0
} catch (e) {
return false
}
}

/**
* Returns the raw `Buffer[]` (LegacyTransaction) or `Buffer` (typed transaction).
* This is the data which is found in the transactions of the block body.
*/
abstract raw(): Buffer[] | Buffer
abstract hash(): Buffer

abstract getMessageToVerifySignature(): Buffer
/**
* Returns the sender's address
*/
getSenderAddress(): Address {
return new Address(publicToAddress(this.getSenderPublicKey()))
}
abstract getSenderPublicKey(): Buffer

sign(privateKey: Buffer): TransactionObject {
if (privateKey.length !== 32) {
throw new Error('Private key must be 32 bytes in length.')
}

const msgHash = this.getMessageToSign()

// Only `v` is reassigned.
/* eslint-disable-next-line prefer-const */
let { v, r, s } = ecsign(msgHash, privateKey)

return this.processSignature(v, r, s)
}

// Accept the v,r,s values from the `sign` method, and convert this into a TransactionObject
protected abstract processSignature(v: number, r: Buffer, s: Buffer): TransactionObject
}
Loading

0 comments on commit 8e02910

Please sign in to comment.