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
5 changes: 1 addition & 4 deletions packages/block/src/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,7 @@ export class BlockHeader {
baseFeePerGas?: BN
) {
if (options.common) {
this._common = Object.assign(
Object.create(Object.getPrototypeOf(options.common)),
options.common
)
this._common = options.common.copy()
} else {
const chain = 'mainnet' // default
if (options.initWithGenesisHeader) {
Expand Down
56 changes: 50 additions & 6 deletions packages/block/test/block.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,27 +135,54 @@ tape('[Block]: block functions', function (t) {
async function testTransactionValidation(st: tape.Test, block: Block) {
st.ok(block.validateTransactions())
st.ok(await block.validateTransactionsTrie())
st.end()
}

t.test('should test transaction validation', async function (st) {
const blockRlp = testData.blocks[0].rlp
const block = Block.fromRLPSerializedBlock(blockRlp)
st.plan(2)
const block = Block.fromRLPSerializedBlock(blockRlp, { freeze: false })
await testTransactionValidation(st, block)
;(block.header as any).transactionsTrie = Buffer.alloc(32)
try {
await block.validateData()
st.fail('should throw')
} catch (error) {
st.equal(error.message, 'invalid transaction trie')
}
st.end()
})

t.test('should test transaction validation with empty transaction list', async function (st) {
const block = Block.fromBlockData({})
st.plan(2)
await testTransactionValidation(st, block)
st.end()
})

t.test('should test transaction validation with legacy tx in london', async function (st) {
const common = new Common({ chain: 'goerli', hardfork: 'london' })
const blockRlp = testData.blocks[0].rlp
const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false })
await testTransactionValidation(st, block)
;(block.transactions[0] as any).gasPrice = new BN(0)
const result = block.validateTransactions(true)
st.ok(
result[0].includes('tx unable to pay base fee (non EIP-1559 tx)'),
'should throw when legacy tx is unable to pay base fee'
)
st.end()
})

const testData2 = require('./testdata/testdata2.json')
t.test('should test uncles hash validation', function (st) {
t.test('should test uncles hash validation', async function (st) {
const blockRlp = testData2.blocks[2].rlp
const block = Block.fromRLPSerializedBlock(blockRlp)
const block = Block.fromRLPSerializedBlock(blockRlp, { freeze: false })
st.equal(block.validateUnclesHash(), true)
;(block.header as any).uncleHash = Buffer.alloc(32)
try {
await block.validateData()
st.fail('should throw')
} catch (error) {
st.equal(error.message, 'invalid uncle hash')
}
st.end()
})

Expand Down Expand Up @@ -477,6 +504,23 @@ tape('[Block]: block functions', function (t) {
st.end()
})

t.test('should error on invalid params', function (st) {
st.throws(() => {
Block.fromRLPSerializedBlock('1' as any)
}, 'input must be array')
st.throws(() => {
Block.fromValuesArray([1, 2, 3, 4] as any)
}, 'input length must be 3 or less')
st.end()
})

t.test('should return the same block data from raw()', function (st) {
const block = Block.fromRLPSerializedBlock(testData2.blocks[2].rlp)
const blockFromRaw = Block.fromValuesArray(block.raw())
st.ok(block.hash().equals(blockFromRaw.hash()))
st.end()
})

t.test('should test toJSON', function (st) {
const block = Block.fromRLPSerializedBlock(testData2.blocks[2].rlp)
st.equal(typeof block.toJSON(), 'object')
Expand Down
65 changes: 62 additions & 3 deletions packages/block/test/eip1559block.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,10 @@ tape('EIP1559 tests', function (t) {
const expectedError = 'A base fee for a block can only be set with EIP1559 being activated'
st.ok(e.message.includes(expectedError), 'should throw with EIP1559 not being activated')
}

st.end()
})

t.test('Header-> validate()', async function (st) {
t.test('Header -> validate()', async function (st) {
const header = BlockHeader.fromHeaderData(
{
number: new BN(1),
Expand All @@ -74,15 +73,58 @@ tape('EIP1559 tests', function (t) {
{
calcDifficultyFromHeader: genesis.header,
common,
freeze: false,
}
)

try {
await header.validate(blockchain1)
st.fail('should throw')
} catch (e) {
st.ok(e.message.includes('base fee'), 'should throw if base fee is not defined')
const expectedError = 'Initial EIP1559 block does not have initial base fee'
st.ok(
e.message.includes(expectedError),
'should throw if base fee is not set to initial value'
)
}

try {
// eslint-disable-next-line no-extra-semi
;(header as any).baseFeePerGas = undefined
await header.validate(blockchain1)
} catch (e) {
const expectedError = 'EIP1559 block has no base fee field'
st.ok(
e.message.includes(expectedError),
'should throw with no base fee field when EIP1559 is activated'
)
}

// eslint-disable-next-line no-extra-semi
;(header as any).baseFeePerGas = new BN(7) // reset for next test
const block = Block.fromBlockData({ header }, { common })
try {
const blockchain = Object.create(blockchain1)
await blockchain.putBlock(block)
const header = BlockHeader.fromHeaderData(
{
number: new BN(2),
parentHash: block.hash(),
gasLimit: block.header.gasLimit,
timestamp: new BN(10),
baseFeePerGas: new BN(1000),
},
{
calcDifficultyFromHeader: block.header,
common,
}
)
await header.validate(blockchain)
} catch (e) {
const expectedError = 'Invalid block: base fee not correct'
st.ok(e.message.includes(expectedError), 'should throw when base fee is not correct')
}

st.end()
})

Expand Down Expand Up @@ -438,4 +480,21 @@ tape('EIP1559 tests', function (t) {
}
st.end()
})

t.test('Header -> toJSON()', function (st) {
const header = BlockHeader.fromHeaderData(
{
number: new BN(1),
parentHash: genesis.hash(),
timestamp: new BN(1),
gasLimit: genesis.header.gasLimit,
baseFeePerGas: new BN(5),
},
{
common,
}
)
st.equal(header.toJSON().baseFee, '0x5')
st.end()
})
})
6 changes: 6 additions & 0 deletions packages/block/test/testdata/eip1559baseFee.json
Original file line number Diff line number Diff line change
Expand Up @@ -292,5 +292,11 @@
"parentGasUsed":10000000,
"parentTargetGasUsed":9000000,
"expectedBaseFee":3
},
{
"parentBaseFee":7,
"parentGasUsed":10000000,
"parentTargetGasUsed":5000000,
"expectedBaseFee":8
}
]
52 changes: 51 additions & 1 deletion packages/client/test/rpc/eth/sendRawTransaction.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import tape from 'tape'
import { baseSetup, params, baseRequest } from '../helpers'
import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
import Common from '@ethereumjs/common'
import { toBuffer } from 'ethereumjs-util'
import { baseSetup, params, baseRequest, createClient, createManager, startRPC } from '../helpers'

const method = 'eth_sendRawTransaction'

Expand Down Expand Up @@ -41,3 +44,50 @@ tape(`${method}: call with invalid tx (wrong chain ID)`, (t) => {
}
baseRequest(t, server, req, 200, expectRes)
})

tape(`${method}: call with unsigned tx`, (t) => {
const server = baseSetup()

// Mainnet EIP-1559 tx
const txData =
'0x02f90108018001018402625a0094cccccccccccccccccccccccccccccccccccccccc830186a0b8441a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85bf859940000000000000000000000000000000000000101f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000060a701a0afb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9a0479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64'
const common = new Common({ chain: 'mainnet', hardfork: 'london' })
const tx = FeeMarketEIP1559Transaction.fromSerializedTx(toBuffer(txData), {
common,
freeze: false,
})
;(tx as any).v = undefined
;(tx as any).r = undefined
;(tx as any).s = undefined
const txHex = '0x' + tx.serialize().toString('hex')
const req = params(method, [txHex])
const expectRes = (res: any) => {
const msg = 'should return error'
if (res.body.result.message === 'tx needs to be signed') {
t.pass(msg)
} else {
throw new Error(msg)
}
}
baseRequest(t, server, req, 200, expectRes)
})

tape(`${method}: call with no peers`, (t) => {
const client = createClient({ noPeers: true })

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah, I actually didn't know how to test this. 😄

const manager = createManager(client)
const server = startRPC(manager.getMethods())

// Mainnet EIP-1559 tx
const txData =
'0x02f90108018001018402625a0094cccccccccccccccccccccccccccccccccccccccc830186a0b8441a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85bf859940000000000000000000000000000000000000101f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000060a701a0afb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9a0479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64'
const req = params(method, [txData])
const expectRes = (res: any) => {
const msg = 'should return error'
if (res.body.result.message === 'no peer connection available') {
t.pass(msg)
} else {
throw new Error(msg)
}
}
baseRequest(t, server, req, 200, expectRes)
})
7 changes: 6 additions & 1 deletion packages/client/test/rpc/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,18 @@ export function createClient(clientOpts: any = {}) {
synchronizer = { execution: { vm: new VM({ blockchain, common }) } }
}

let peers = [1, 2, 3]
if (clientOpts.noPeers === true) {
peers = []
}

const client: any = {
config,
services: [
{
name: 'eth',
chain: clientConfig.blockchain,
pool: { peers: [1, 2, 3] },
pool: { peers },
protocols: [
{
name: 'eth',
Expand Down
4 changes: 2 additions & 2 deletions packages/tx/src/eip1559Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,8 @@ export default class FeeMarketEIP1559Transaction extends BaseTransaction<FeeMark

const msgHash = this.getMessageToVerifySignature()

// All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
// TODO: verify if this is the case for EIP-2930
// EIP-2: All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
// Reasoning: https://ethereum.stackexchange.com/a/55728
if (this.common.gteHardfork('homestead') && this.s?.gt(N_DIV_2)) {
throw new Error(
'Invalid Signature: s-values greater than secp256k1n/2 are considered invalid'
Expand Down
4 changes: 2 additions & 2 deletions packages/tx/src/eip2930Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ export default class AccessListEIP2930Transaction extends BaseTransaction<Access

const msgHash = this.getMessageToVerifySignature()

// All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
// TODO: verify if this is the case for EIP-2930
// EIP-2: All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
// Reasoning: https://ethereum.stackexchange.com/a/55728
if (this.common.gteHardfork('homestead') && this.s?.gt(N_DIV_2)) {
throw new Error(
'Invalid Signature: s-values greater than secp256k1n/2 are considered invalid'
Expand Down
3 changes: 2 additions & 1 deletion packages/tx/src/legacyTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ export default class Transaction extends BaseTransaction<Transaction> {
getSenderPublicKey(): Buffer {
const msgHash = this.getMessageToVerifySignature()

// All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
// EIP-2: All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
// Reasoning: https://ethereum.stackexchange.com/a/55728
if (this.common.gteHardfork('homestead') && this.s?.gt(N_DIV_2)) {
throw new Error(
'Invalid Signature: s-values greater than secp256k1n/2 are considered invalid'
Expand Down
29 changes: 28 additions & 1 deletion packages/tx/test/base.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import tape from 'tape'
import Common from '@ethereumjs/common'
import { Transaction, AccessListEIP2930Transaction, FeeMarketEIP1559Transaction } from '../src'
import {
Transaction,
AccessListEIP2930Transaction,
FeeMarketEIP1559Transaction,
N_DIV_2,
} from '../src'
import { TxsJsonEntry } from './types'
import { BaseTransaction } from '../src/baseTransaction'
import { privateToPublic, BN, toBuffer } from 'ethereumjs-util'
Expand Down Expand Up @@ -235,6 +240,28 @@ tape('[BaseTransaction]', function (t) {
st.end()
})

t.test(
'getSenderPublicKey() -> should throw if s-value is greater than secp256k1n/2',
function (st) {
// EIP-2: All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
// Reasoning: https://ethereum.stackexchange.com/a/55728
for (const txType of txTypes) {
txType.txs.forEach(function (tx: any, i: number) {
const { privateKey } = txType.fixtures[i]
if (privateKey) {
let signedTx = tx.sign(Buffer.from(privateKey, 'hex'))
signedTx = JSON.parse(JSON.stringify(signedTx)) // deep clone
;(signedTx as any).s = N_DIV_2.addn(1)
st.throws(() => {
signedTx.getSenderPublicKey()
}, 'should throw when s-value is greater than secp256k1n/2')
}
})
}
st.end()
}
)

t.test('verifySignature()', function (st) {
for (const txType of txTypes) {
txType.txs.forEach(function (tx: any, i: number) {
Expand Down
4 changes: 4 additions & 0 deletions packages/tx/test/transactionFactory.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ tape('[TransactionFactory]: Basic functions', function (t) {
st.throws(() => {
TransactionFactory.fromTxData({ type: 1 })
})

st.throws(() => {
TransactionFactory.fromTxData({ type: 999 })
})
st.end()
})

Expand Down
Loading