Skip to content

Commit

Permalink
Merge branch 'vm-diff-based-touched-account-checkpointing' of github.…
Browse files Browse the repository at this point in the history
…com:ethereumjs/ethereumjs-monorepo into vm-diff-based-touched-account-checkpointing
  • Loading branch information
jochem-brouwer committed Mar 15, 2023
2 parents 5cb57c9 + 62b4e0d commit ba08168
Show file tree
Hide file tree
Showing 10 changed files with 478 additions and 18 deletions.
10 changes: 10 additions & 0 deletions packages/client/lib/rpc/modules/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,10 @@ export class Engine {

blocks.push(block)

let lastBlock: Block
try {
for (const [i, block] of blocks.entries()) {
lastBlock = block
const root = (i > 0 ? blocks[i - 1] : await this.chain.getBlock(block.header.parentHash))
.header.stateRoot
await this.execution.runWithoutSetHead({
Expand All @@ -605,6 +607,14 @@ export class Engine {
this.config.logger.error(validationError)
const latestValidHash = await validHash(block.header.parentHash, this.chain)
const response = { status: Status.INVALID, latestValidHash, validationError }
try {
await this.chain.blockchain.delBlock(lastBlock!.hash())
// eslint-disable-next-line no-empty
} catch {}
try {
await this.service.beaconSync?.skeleton.deleteBlock(lastBlock!)
// eslint-disable-next-line no-empty
} catch {}
return response
}

Expand Down
2 changes: 1 addition & 1 deletion packages/client/lib/sync/skeleton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,7 @@ export class Skeleton extends MetaDBManager {
/**
* Deletes a skeleton block from the db by number
*/
private async deleteBlock(block: Block): Promise<boolean> {
async deleteBlock(block: Block): Promise<boolean> {
try {
await this.delete(DBKey.SkeletonBlock, bigIntToBuffer(block.header.number))
await this.delete(DBKey.SkeletonBlockHashToNumber, block.hash())
Expand Down
29 changes: 28 additions & 1 deletion packages/client/lib/sync/snapsync.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Event } from '../types'

import { AccountFetcher } from './fetcher'
import { Synchronizer } from './sync'

Expand Down Expand Up @@ -33,7 +35,11 @@ export class SnapSynchronizer extends Synchronizer {
/**
* Open synchronizer. Must be called before sync() is called
*/
async open(): Promise<void> {}
async open(): Promise<void> {
await super.open()
await this.chain.open()
await this.pool.open()
}

/**
* Returns true if peer can be used for syncing
Expand Down Expand Up @@ -75,6 +81,27 @@ export class SnapSynchronizer extends Synchronizer {
return result ? result[1][0] : undefined
}

/**
* Start synchronizer.
*/
async start(): Promise<void> {
if (this.running) return
this.running = true

const timeout = setTimeout(() => {
this.forceSync = true
}, this.interval * 30)
try {
await this.sync()
} catch (error: any) {
this.config.logger.error(`Snap sync error: ${error.message}`)
this.config.events.emit(Event.SYNC_ERROR, error)
}
await new Promise((resolve) => setTimeout(resolve, this.interval))
this.running = false
clearTimeout(timeout)
}

/**
* Called from `sync()` to sync blocks and state from peer starting from current height.
* @param peer remote peer to sync with
Expand Down
2 changes: 1 addition & 1 deletion packages/client/lib/sync/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,10 @@ export abstract class Synchronizer {
* Stop synchronizer.
*/
async stop(): Promise<boolean> {
this.clearFetcher()
if (!this.running) {
return false
}
this.clearFetcher()
clearInterval(this._syncedStatusCheckInterval as NodeJS.Timeout)
await new Promise((resolve) => setTimeout(resolve, this.interval))
this.running = false
Expand Down
3 changes: 3 additions & 0 deletions packages/client/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum Event {
SYNC_SYNCHRONIZED = 'sync:synchronized',
SYNC_ERROR = 'sync:error',
SYNC_FETCHER_ERROR = 'sync:fetcher:error',
SYNC_SNAPSYNC_COMPLETE = 'sync:snapsync:complete',
PEER_CONNECTED = 'peer:connected',
PEER_DISCONNECTED = 'peer:disconnected',
PEER_ERROR = 'peer:error',
Expand All @@ -40,6 +41,7 @@ export interface EventParams {
[Event.SYNC_FETCHED_BLOCKS]: [blocks: Block[]]
[Event.SYNC_FETCHED_HEADERS]: [headers: BlockHeader[]]
[Event.SYNC_SYNCHRONIZED]: [chainHeight: bigint]
[Event.SYNC_SNAPSYNC_COMPLETE]: [stateRoot: Uint8Array]
[Event.SYNC_ERROR]: [syncError: Error]
[Event.SYNC_FETCHER_ERROR]: [fetchError: Error, task: any, peer: Peer | null | undefined]
[Event.PEER_CONNECTED]: [connectedPeer: Peer]
Expand Down Expand Up @@ -67,6 +69,7 @@ export type EventBusType = EventBus<Event.CHAIN_UPDATED> &
EventBus<Event.SYNC_FETCHED_BLOCKS> &
EventBus<Event.SYNC_FETCHED_HEADERS> &
EventBus<Event.SYNC_SYNCHRONIZED> &
EventBus<Event.SYNC_SNAPSYNC_COMPLETE> &
EventBus<Event.SYNC_FETCHER_ERROR> &
EventBus<Event.PEER_CONNECTED> &
EventBus<Event.PEER_DISCONNECTED> &
Expand Down
41 changes: 38 additions & 3 deletions packages/client/test/sim/simutils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Blockchain } from '@ethereumjs/blockchain'
import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction, initKZG } from '@ethereumjs/tx'
import {
blobsToCommitments,
Expand All @@ -8,9 +9,13 @@ import { Address } from '@ethereumjs/util'
import * as kzg from 'c-kzg'
import { randomBytes } from 'crypto'
import * as fs from 'fs/promises'
import { Level } from 'level'
import { execSync, spawn } from 'node:child_process'
import * as net from 'node:net'

import { EthereumClient } from '../../lib/client'
import { Config } from '../../lib/config'

import type { Common } from '@ethereumjs/common'
import type { ChildProcessWithoutNullStreams } from 'child_process'
import type { Client } from 'jayson/promise'
Expand Down Expand Up @@ -136,7 +141,7 @@ export function runNetwork(
console.log('')
lastPrintedDot = false
}
process.stdout.write(`${runProcPrefix}:el<>cl: ${runProc.pid}: ${str}`) // str already contains a new line. console.log adds a new line
process.stdout.write(`data:${runProcPrefix}: ${runProc.pid}: ${str}`) // str already contains a new line. console.log adds a new line
} else {
if (str.includes('Synchronized')) {
process.stdout.write('.')
Expand All @@ -148,9 +153,10 @@ export function runNetwork(
})
runProc.stderr.on('data', (chunk) => {
const str = Buffer.from(chunk).toString('utf8')
const filterStr = filterKeywords.reduce((acc, next) => acc || str.includes(next), false)
const filterOutStr = filterOutWords.reduce((acc, next) => acc || str.includes(next), false)
if (!filterOutStr) {
process.stderr.write(`${runProcPrefix}:el<>cl: ${runProc.pid}: ${str}`) // str already contains a new line. console.log adds a new line
if (filterStr && !filterOutStr) {
process.stderr.write(`stderr:${runProcPrefix}: ${runProc.pid}: ${str}`) // str already contains a new line. console.log adds a new line
}
})

Expand Down Expand Up @@ -400,6 +406,34 @@ export const runBlobTxsFromFile = async (client: Client, path: string) => {
return txnHashes
}

export async function createInlineClient(config: any, common: any, customGenesisState: any) {
config.events.setMaxListeners(50)
const datadir = Config.DATADIR_DEFAULT
const chainDB = new Level<string | Buffer, string | Buffer>(
`${datadir}/${common.chainName()}/chainDB`
)
const stateDB = new Level<string | Buffer, string | Buffer>(
`${datadir}/${common.chainName()}/stateDB`
)
const metaDB = new Level<string | Buffer, string | Buffer>(
`${datadir}/${common.chainName()}/metaDB`
)

const blockchain = await Blockchain.create({
db: chainDB,
genesisState: customGenesisState,
common: config.chainCommon,
hardforkByHeadBlockNumber: true,
validateBlocks: true,
validateConsensus: false,
})
config.chainCommon.setForkHashes(blockchain.genesisBlock.hash())
const inlineClient = await EthereumClient.create({ config, blockchain, chainDB, stateDB, metaDB })
await inlineClient.open()
await inlineClient.start()
return inlineClient
}

// To minimise noise on the spec run, selective filteration is applied to let the important events
// of the testnet log to show up in the spec log
export const filterKeywords = [
Expand All @@ -414,5 +448,6 @@ export const filterKeywords = [
'pid',
'Synced - slot: 0 -',
'TxPool started',
'number=0',
]
export const filterOutWords = ['duties', 'Low peer count', 'MaxListenersExceededWarning']
126 changes: 115 additions & 11 deletions packages/client/test/sim/single-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,91 @@ then
exit;
fi;

if [ ! -n "$JWT_SECRET" ]
then
JWT_SECRET="0xdc6457099f127cf0bac78de8b297df04951281909db4f58b43def7c7151e765d"
fi;

if [ -n "$ELCLIENT" ]
then
if [ ! -n "$ELCLIENT_IMAGE" ]
then
case $ELCLIENT in
ethereumjs)
echo "ELCLIENT=$ELCLIENT using local ethereumjs binary from packages/client"
;;
geth)
if [ ! -n "$NETWORKID" ]
then
echo "geth requires NETWORKID to be passed in env, exiting..."
exit;
fi;
ELCLIENT_IMAGE="ethereum/client-go:stable"
echo "ELCLIENT=$ELCLIENT using ELCLIENT_IMAGE=$ELCLIENT_IMAGE NETWORKID=$NETWORKID"
;;
*)
echo "ELCLIENT=$ELCLIENT not implemented"
esac
fi
else
ELCLIENT="ethereumjs"
echo "ELCLIENT=$ELCLIENT using local ethereumjs binary from packages/client"
fi;

case $MULTIPEER in
syncpeer)
echo "setting up to run as a sync only peer to peer1 (bootnode)..."
DATADIR="$DATADIR/syncpeer"
EL_PORT_ARGS="--port 30305 --rpcEnginePort 8553 --rpcport 8947 --multiaddrs /ip4/127.0.0.1/tcp/50582/ws --logLevel debug"
CL_PORT_ARGS="--genesisValidators 8 --enr.tcp 9002 --port 9002 --execution.urls http://localhost:8553 --rest.port 9598 --server http://localhost:9598 --network.connectToDiscv5Bootnodes true"

case $ELCLIENT in
ethereumjs)
EL_PORT_ARGS="--port 30305 --rpcEnginePort 8553 --rpcPort 8947 --multiaddrs /ip4/127.0.0.1/tcp/50582/ws --logLevel debug"
;;
geth)
echo "syncpeer args not yet implemented for geth, exiting..."
exit;
;;
*)
echo "ELCLIENT=$ELCLIENT not implemented"
esac

CL_PORT_ARGS="--genesisValidators 8 --enr.tcp 9002 --port 9002 --execution.urls http://localhost:8553 --rest.port 9598 --server http://localhost:9598 --network.connectToDiscv5Bootnodes true --logLevel debug"
;;

peer2 )
peer2)
echo "setting up peer2 to run with peer1 (bootnode)..."
DATADIR="$DATADIR/peer2"
EL_PORT_ARGS="--port 30304 --rpcEnginePort 8552 --rpcport 8946 --multiaddrs /ip4/127.0.0.1/tcp/50581/ws --bootnodes $elBootnode --logLevel debug"

case $ELCLIENT in
ethereumjs)
EL_PORT_ARGS="--port 30304 --rpcEnginePort 8552 --rpcPort 8946 --multiaddrs /ip4/127.0.0.1/tcp/50581/ws --bootnodes $elBootnode --logLevel debug"
;;
geth)
echo "peer2 args not yet implemented for geth, exiting..."
exit;
;;
*)
echo "ELCLIENT=$ELCLIENT not implemented"
esac

CL_PORT_ARGS="--genesisValidators 8 --startValidators 4..7 --enr.tcp 9001 --port 9001 --execution.urls http://localhost:8552 --rest.port 9597 --server http://localhost:9597 --network.connectToDiscv5Bootnodes true --bootnodes $bootEnrs"
;;

* )
DATADIR="$DATADIR/peer1"
EL_PORT_ARGS="--isSingleNode --extIP 127.0.0.1 --logLevel debug"

case $ELCLIENT in
ethereumjs)
EL_PORT_ARGS="--isSingleNode --extIP 127.0.0.1 --logLevel debug"
;;
geth)
# geth will be mounted in docker with DATADIR to /data
EL_PORT_ARGS="--datadir /data/geth --authrpc.jwtsecret /data/jwtsecret --http --http.api engine,net,eth,web3,debug,admin --http.corsdomain \"*\" --http.port 8545 --http.addr 0.0.0.0 --http.vhosts \"*\" --authrpc.addr 0.0.0.0 --authrpc.vhosts \"*\" --authrpc.port=8551 --syncmode full --networkid $NETWORKID --nodiscover"
;;
*)
echo "ELCLIENT=$ELCLIENT not implemented"
esac

CL_PORT_ARGS="--enr.ip 127.0.0.1 --enr.tcp 9000 --enr.udp 9000"
if [ ! -n "$MULTIPEER" ]
then
Expand All @@ -62,11 +129,22 @@ fi;

# clean these folders as old data can cause issues
sudo rm -rf $DATADIR/ethereumjs
sudo rm -rf $DATADIR/geth
sudo rm -rf $DATADIR/lodestar

# these two commands will harmlessly fail if folders exists
mkdir $DATADIR/ethereumjs
mkdir $DATADIR/geth
mkdir $DATADIR/lodestar
echo "$JWT_SECRET" > $DATADIR/jwtsecret

# additional step for setting geth genesis now that we have datadir
if [ "$ELCLIENT" == "geth" ]
then
setupCmd="docker run --rm -v $scriptDir/configs:/config -v $DATADIR/geth:/data $ELCLIENT_IMAGE --datadir /data init /config/$NETWORK.json"
echo "$setupCmd"
$setupCmd
fi;

run_cmd(){
execCmd=$1;
Expand All @@ -90,7 +168,12 @@ cleanup() {
then
ejsPidBySearch=$(ps x | grep "ts-node bin/cli.ts --dataDir $DATADIR/ethereumjs" | grep -v grep | awk '{print $1}')
echo "cleaning ethereumjs pid:${ejsPid} ejsPidBySearch:${ejsPidBySearch}..."
kill $ejsPidBySearch
if [ -n "$ELCLIENT_IMAGE" ]
then
docker rm execution${MULTIPEER} -f
else
kill $ejsPidBySearch
fi;
fi;
if [ -n "$lodePid" ]
then
Expand All @@ -110,7 +193,18 @@ cleanup() {

if [ "$MULTIPEER" == "peer1" ]
then
ejsCmd="npm run client:start -- --dataDir $DATADIR/ethereumjs --gethGenesis $scriptDir/configs/$NETWORK.json --rpc --rpcEngine --rpcEngineAuth false $EL_PORT_ARGS"
case $ELCLIENT in
ethereumjs)
ejsCmd="npm run client:start -- --dataDir $DATADIR/ethereumjs --gethGenesis $scriptDir/configs/$NETWORK.json --rpc --rpcEngine --rpcEngineAuth false $EL_PORT_ARGS"
;;
geth)
# geth will be mounted in docker with DATADIR to /data
ejsCmd="docker run --rm --name execution${MULTIPEER} -v $DATADIR:/data --network host $ELCLIENT_IMAGE $EL_PORT_ARGS"
;;
*)
echo "ELCLIENT=$ELCLIENT not implemented"
esac

run_cmd "$ejsCmd"
ejsPid=$!
echo "ejsPid: $ejsPid"
Expand Down Expand Up @@ -159,11 +253,21 @@ else
EL_PORT_ARGS="$EL_PORT_ARGS --bootnodes $elBootnode"
CL_PORT_ARGS="$CL_PORT_ARGS --bootnodes $bootEnrs"

GENESIS_HASH=$(cat "$origDataDir/geneisHash")
GENESIS_HASH=$(cat "$origDataDir/genesisHash")
genTime=$(cat "$origDataDir/genesisTime")

case $ELCLIENT in
ethereumjs)
ejsCmd="npm run client:start -- --dataDir $DATADIR/ethereumjs --gethGenesis $scriptDir/configs/$NETWORK.json --rpc --rpcEngine --rpcEngineAuth false $EL_PORT_ARGS"
;;
geth)
echo "peer2/syncpeer args not yet implemented for geth, exiting..."
exit;
;;
*)
echo "ELCLIENT=$ELCLIENT not implemented"
esac

ejsCmd="npm run client:start -- --dataDir $DATADIR/ethereumjs --gethGenesis $scriptDir/configs/$NETWORK.json --rpc --rpcEngine --rpcEngineAuth false $EL_PORT_ARGS"
run_cmd "$ejsCmd"
ejsPid=$!
echo "ejsPid: $ejsPid"
Expand All @@ -179,9 +283,9 @@ then
then
LODE_IMAGE="chainsafe/lodestar:latest"
fi;
lodeCmd="docker run --rm --name beacon${MULTIPEER} -v $DATADIR:/data --network host $LODE_IMAGE dev --dataDir /data/lodestar $CL_PORT_ARGS"
lodeCmd="docker run --rm --name beacon${MULTIPEER} -v $DATADIR:/data --network host $LODE_IMAGE dev --dataDir /data/lodestar --jwt-secret /data/jwtsecret $CL_PORT_ARGS"
else
lodeCmd="$LODE_BINARY dev --dataDir $DATADIR/lodestar $CL_PORT_ARGS"
lodeCmd="$LODE_BINARY dev --dataDir $DATADIR/lodestar --jwt-secret $DATADIR/jwtsecret $CL_PORT_ARGS"
fi;
run_cmd "$lodeCmd"
lodePid=$!
Expand Down
Loading

0 comments on commit ba08168

Please sign in to comment.