Skip to content

Commit

Permalink
Merge branch 'master' into eth-66
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanio authored Jul 8, 2021
2 parents 7b8145c + fa15aba commit 4340054
Show file tree
Hide file tree
Showing 25 changed files with 369 additions and 42 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ and open a new PR with the changes. Be sure to update both the diagram and the e

## Getting Started

See our [monorepo](config/monorepo.md) documentation to get started on setting up the respository and installing dependencies. The [config](config/) folder gives an overview on shared configuration and scripts between packages.
See our [monorepo](config/monorepo.md) documentation to get started on setting up the repository and installing dependencies. The [config](config/) folder gives an overview on shared configuration and scripts between packages.

# EthereumJS

Expand Down
24 changes: 14 additions & 10 deletions packages/block/src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,13 @@ export class Block {
*
* Throws if invalid.
*
* @param blockchain - validate against a @ethereumjs/blockchain
* @param blockchain - validate against an @ethereumjs/blockchain
* @param onlyHeader - if should only validate the header (skips validating txTrie and unclesHash) (default: false)
*/
async validate(blockchain: Blockchain): Promise<void> {
async validate(blockchain: Blockchain, onlyHeader: boolean = false): Promise<void> {
await this.header.validate(blockchain)
await this.validateUncles(blockchain)
await this.validateData()
await this.validateData(onlyHeader)
}
/**
* Validates the block data, throwing if invalid.
Expand All @@ -277,21 +278,24 @@ export class Block {
* - All transactions are valid
* - The transactions trie is valid
* - The uncle hash is valid
* @param onlyHeader if only passed the header, skip validating txTrie and unclesHash (default: false)
*/
async validateData(): Promise<void> {
async validateData(onlyHeader: boolean = false): Promise<void> {
const txErrors = this.validateTransactions(true)
if (txErrors.length > 0) {
const msg = `invalid transactions: ${txErrors.join(' ')}`
throw this.header._error(msg)
}

const validateTxTrie = await this.validateTransactionsTrie()
if (!validateTxTrie) {
throw new Error('invalid transaction trie')
}
if (!onlyHeader) {
const validateTxTrie = await this.validateTransactionsTrie()
if (!validateTxTrie) {
throw new Error('invalid transaction trie')
}

if (!this.validateUnclesHash()) {
throw new Error('invalid uncle hash')
if (!this.validateUnclesHash()) {
throw new Error('invalid uncle hash')
}
}
}

Expand Down
9 changes: 9 additions & 0 deletions packages/block/src/header-from-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ export default function blockHeaderFromRpc(blockParams: any, options?: BlockOpti
nonce,
} = blockParams

let baseFeePerGas

// Check if the field baseFeePerGas is present in the block.
// This field was introduced after: https://eips.ethereum.org/EIPS/eip-1559
if ('baseFeePerGas' in blockParams) {
baseFeePerGas = blockParams.baseFeePerGas
}

const blockHeader = BlockHeader.fromHeaderData(
{
parentHash,
Expand All @@ -45,6 +53,7 @@ export default function blockHeaderFromRpc(blockParams: any, options?: BlockOpti
extraData,
mixHash,
nonce,
baseFeePerGas,
},
options
)
Expand Down
10 changes: 9 additions & 1 deletion packages/block/src/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ export class BlockHeader {
*/
_validateHeaderFields() {
const { parentHash, stateRoot, transactionsTrie, receiptTrie, mixHash, nonce } = this

if (parentHash.length !== 32) {
throw new Error(`parentHash must be 32 bytes, received ${parentHash.length} bytes`)
}
Expand All @@ -320,7 +321,14 @@ export class BlockHeader {
}

if (nonce.length !== 8) {
throw new Error(`nonce must be 8 bytes, received ${nonce.length} bytes`)
// Hack to check for Kovan due to non-standard nonce length (65 bytes)
if (this._common.networkIdBN().eqn(42)) {
if (nonce.length !== 65) {
throw new Error(`nonce must be 65 bytes on kovan, received ${nonce.length} bytes`)
}
} else {
throw new Error(`nonce must be 8 bytes, received ${nonce.length} bytes`)
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/blockchain/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -877,9 +877,9 @@ export default class Blockchain implements BlockchainInterface {
})
: item
const isGenesis = block.isGenesis()
const isHeader = item instanceof BlockHeader

// we cannot overwrite the Genesis block after initializing the Blockchain

if (isGenesis) {
throw new Error('Cannot put a genesis block: create a new Blockchain')
}
Expand All @@ -897,7 +897,7 @@ export default class Blockchain implements BlockchainInterface {

if (this._validateBlocks && !isGenesis) {
// this calls into `getBlock`, which is why we cannot lock yet
await block.validate(this)
await block.validate(this, isHeader)
}

if (this._validateConsensus) {
Expand Down
3 changes: 3 additions & 0 deletions packages/client/lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default class EthereumClient extends events.EventEmitter {

public opened: boolean
public started: boolean
public synchronized: boolean

/**
* Create new node
Expand All @@ -68,6 +69,7 @@ export default class EthereumClient extends events.EventEmitter {
]
this.opened = false
this.started = false
this.synchronized = false
}

/**
Expand All @@ -92,6 +94,7 @@ export default class EthereumClient extends events.EventEmitter {
})
s.on('synchronized', () => {
this.emit('synchronized')
this.synchronized = true
})
})
await Promise.all(this.services.map((s) => s.open()))
Expand Down
2 changes: 2 additions & 0 deletions packages/client/lib/net/peer/rlpxpeer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { RlpxServer } from '../server'
const devp2pCapabilities: any = {
eth66: Devp2pETH.eth66,
les2: Devp2pLES.les2,
les3: Devp2pLES.les3,
les4: Devp2pLES.les4,
}

export interface RlpxPeerOptions extends Omit<PeerOptions, 'address' | 'transport'> {
Expand Down
2 changes: 1 addition & 1 deletion packages/client/lib/net/protocol/boundprotocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ export class BoundProtocol extends EventEmitter {
;(this as any)[camel] = async (args: any[]) =>
this.request(name, args).catch((error: Error) => {
this.emit('error', error)
return []
return undefined
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/client/lib/net/protocol/ethprotocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ export class EthProtocol extends Protocol {
* @return {Object}
*/
encodeStatus(): any {
// TODO: add latestBlock for more precise ETH/64 forkhash switch
return {
networkId: this.chain.networkId.toArrayLike(Buffer),
td: this.chain.blocks.td.toArrayLike(Buffer),
bestHash: this.chain.blocks.latest!.hash(),
genesisHash: this.chain.genesis.hash,
latestBlock: this.chain.blocks.latest!.header.number.toArrayLike(Buffer),
}
}

Expand Down
15 changes: 13 additions & 2 deletions packages/client/lib/net/protocol/lesprotocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export class LesProtocol extends Protocol {
* @type {number[]}
*/
get versions(): number[] {
return [2, 1]
return [4, 3, 2]
}

/**
Expand Down Expand Up @@ -163,7 +163,7 @@ export class LesProtocol extends Protocol {
serveHeaders: 1,
serveChainSince: 0,
serveStateSince: 0,
txRelay: 1,
// txRelay: 1, TODO: uncomment with client tx pool functionality
'flowControl/BL': new BN(this.flow.bl).toArrayLike(Buffer),
'flowControl/MRR': new BN(this.flow.mrr).toArrayLike(Buffer),
'flowControl/MRC': Object.entries(this.flow.mrc).map(([name, { base, req }]) => {
Expand All @@ -173,12 +173,21 @@ export class LesProtocol extends Protocol {
}
}

const forkHash = this.config.chainCommon.forkHash(this.config.chainCommon.hardfork())
const nextFork = this.config.chainCommon.nextHardforkBlockBN(this.config.chainCommon.hardfork())
const forkID = [
Buffer.from(forkHash.slice(2), 'hex'),
nextFork ? nextFork.toArrayLike(Buffer) : Buffer.from([]),
]

return {
networkId: this.chain.networkId.toArrayLike(Buffer),
headTd: this.chain.headers.td.toArrayLike(Buffer),
headHash: this.chain.headers.latest?.hash(),
headNum: this.chain.headers.latest?.number.toArrayLike(Buffer),
genesisHash: this.chain.genesis.hash,
forkID,
recentTxLookup: new BN(1).toArrayLike(Buffer),
...serveOptions,
}
}
Expand Down Expand Up @@ -207,6 +216,8 @@ export class LesProtocol extends Protocol {
headHash: status.headHash,
headNum: new BN(status.headNum),
genesisHash: status.genesisHash,
forkID: status.forkID,
recentTxLookup: status.recentTxLookup,
serveHeaders: this.isServer,
serveChainSince: status.serveChainSince ?? 0,
serveStateSince: status.serveStateSince ?? 0,
Expand Down
53 changes: 52 additions & 1 deletion packages/client/lib/rpc/modules/eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { INTERNAL_ERROR, INVALID_PARAMS, PARSE_ERROR } from '../error-code'
import { RpcTx } from '../types'
import type { Chain } from '../../blockchain'
import type { EthereumClient } from '../..'
import type { EthereumService } from '../../service'
import { EthereumService } from '../../service'
import type { EthProtocol } from '../../net/protocol'
import type VM from '@ethereumjs/vm'
import { Block } from '@ethereumjs/block'
Expand Down Expand Up @@ -50,6 +50,8 @@ export class Eth {
[validators.blockOption],
])

this.chainId = middleware(this.chainId.bind(this), 0, [])

this.estimateGas = middleware(this.estimateGas.bind(this), 2, [
[validators.transaction()],
[validators.blockOption],
Expand Down Expand Up @@ -95,6 +97,8 @@ export class Eth {
this.sendRawTransaction = middleware(this.sendRawTransaction.bind(this), 1, [[validators.hex]])

this.protocolVersion = middleware(this.protocolVersion.bind(this), 0, [])

this.syncing = middleware(this.syncing.bind(this), 0, [])
}

/**
Expand Down Expand Up @@ -161,6 +165,16 @@ export class Eth {
return bufferToHex(execResult.returnValue)
}

/**
* Returns the currently configured chain id, a value used in replay-protected transaction signing as introduced by EIP-155.
* @param _params An empty array
* @returns The chain ID.
*/
async chainId(_params = []) {
const chainId = this._chain.config.chainCommon.chainIdBN()
return `0x${chainId.toString(16)}`
}

/**
* Generates and returns an estimate of how much gas is necessary to allow the transaction to complete.
* The transaction will not be added to the blockchain.
Expand Down Expand Up @@ -490,4 +504,41 @@ export class Eth {
}
}
}
/**
* Returns an object with data about the sync status or false.
* @param params An empty array
* @returns An object with sync status data or false (when not syncing)
* * startingBlock - The block at which the import started (will only be reset after the sync reached his head)
* * currentBlock - The current block, same as eth_blockNumber
* * highestBlock - The estimated highest block
*/
async syncing(_params = []) {
if (this.client.synchronized) {
return false
}

const currentBlockHeader = await this._chain.getLatestHeader()
const currentBlock = `0x${currentBlockHeader.number.toString(16)}`

const synchronizer = this.client.services[0].synchronizer
const startingBlock = `0x${synchronizer.startingBlock.toString(16)}`
const bestPeer = synchronizer.best()
if (!bestPeer) {
return {
code: INTERNAL_ERROR,
message: `no peer available for synchronization`,
}
}

const highestBlockHeader = await synchronizer.latest(bestPeer)
if (!highestBlockHeader) {
return {
code: INTERNAL_ERROR,
message: `highest block header unavailable`,
}
}
const highestBlock = `0x${highestBlockHeader.number.toString(16)}`

return { startingBlock, currentBlock, highestBlock }
}
}
2 changes: 1 addition & 1 deletion packages/client/lib/service/fullethereumservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ interface FullEthereumServiceOptions extends EthereumServiceOptions {
}

/**
* Ethereum service
* Full Ethereum service
* @memberof module:service
*/
export class FullEthereumService extends EthereumService {
Expand Down
7 changes: 3 additions & 4 deletions packages/client/lib/service/lightethereumservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { LightSynchronizer } from '../sync/lightsync'
import { LesProtocol } from '../net/protocol/lesprotocol'

/**
* Ethereum service
* Light Ethereum service
* @memberof module:service
*/
export class LightEthereumService extends EthereumService {
public synchronizer: LightSynchronizer

/**
* Create new ETH service
* @param {Object} options constructor parameters
* Create new LES service
* @param options
*/
constructor(options: EthereumServiceOptions) {
super(options)
Expand All @@ -35,7 +35,6 @@ export class LightEthereumService extends EthereumService {
new LesProtocol({
config: this.config,
chain: this.chain,
flow: this.flow,
timeout: this.timeout,
}),
]
Expand Down
1 change: 1 addition & 0 deletions packages/client/lib/sync/fullsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export class FullSynchronizer extends Synchronizer {
const number = this.chain.blocks.height.toNumber()
const td = this.chain.blocks.td.toString(10)
const hash = this.chain.blocks.latest!.hash()
this.startingBlock = number
this.config.chainCommon.setHardforkByBlockNumber(number)
this.config.logger.info(
`Latest local block: number=${number} td=${td} hash=${short(
Expand Down
15 changes: 14 additions & 1 deletion packages/client/lib/sync/lightsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ export class LightSynchronizer extends Synchronizer {
return best
}

/**
* Get latest header of peer
* @return {Promise} Resolves with header
*/
async latest(peer: Peer) {
const headers = await peer.eth?.getBlockHeaders({
block: peer.eth!.status.bestHash,
max: 1,
})
return headers?.[0]
}

/**
* Sync all headers and state from peer starting from current height.
* @param peer remote peer to sync with
Expand Down Expand Up @@ -118,9 +130,10 @@ export class LightSynchronizer extends Synchronizer {
async open(): Promise<void> {
await this.chain.open()
await this.pool.open()
const number = this.chain.headers.height.toString(10)
const number = this.chain.headers.height.toNumber()
const td = this.chain.headers.td.toString(10)
const hash = this.chain.blocks.latest!.hash()
this.startingBlock = number
this.config.logger.info(`Latest local header: number=${number} td=${td} hash=${short(hash)}`)
}

Expand Down
Loading

0 comments on commit 4340054

Please sign in to comment.