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

Add snapshot and revert endpoints #32

Merged
merged 11 commits into from
Mar 10, 2020
44 changes: 33 additions & 11 deletions packages/rollup-full-node/src/app/test-web3-rpc-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,24 @@ export class TestWeb3Handler extends DefaultWeb3Handler {
* Override to add some test RPC methods.
*/
public async handleRequest(method: string, params: any[]): Promise<string> {
if (method === Web3RpcMethods.increaseTimestamp) {
this.assertParameters(params, 1)
this.increaseTimestamp(params[0])
log.debug(`Set increased timestamp by ${params[0]} seconds.`)
return TestWeb3Handler.successString
switch (method) {
case Web3RpcMethods.increaseTimestamp:
this.assertParameters(params, 1)
this.increaseTimestamp(params[0])
log.debug(`Set increased timestamp by ${params[0]} seconds.`)
return TestWeb3Handler.successString
case Web3RpcMethods.getTimestamp:
this.assertParameters(params, 0)
return add0x(this.getTimestamp().toString(16))
case Web3RpcMethods.snapshot:
this.assertParameters(params, 0)
return this.snapshot()
case Web3RpcMethods.revert:
this.assertParameters(params, 1)
return this.revert(params[0])
default:
return super.handleRequest(method, params)
}
if (method === Web3RpcMethods.getTimestamp) {
this.assertParameters(params, 0)
return add0x(this.getTimestamp().toString(16))
}

return super.handleRequest(method, params)
}

/**
Expand Down Expand Up @@ -94,4 +100,20 @@ export class TestWeb3Handler extends DefaultWeb3Handler {
throw new UnsupportedMethodError(msg)
}
}

/**
* Takes a snapshot of the current node state.
* @returns The snapshot id that can be used as an parameter of the revert endpoint
*/
private async snapshot(): Promise<string> {
return this.provider.send(Web3RpcMethods.snapshot, [])
}

/**
* Reverts state to the specified snapshot
* @param The snapshot id of the snapshot to restore
*/
private async revert(snapShotId: string): Promise<string> {
return this.provider.send(Web3RpcMethods.revert, [snapShotId])
}
}
2 changes: 1 addition & 1 deletion packages/rollup-full-node/src/app/web3-rpc-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class DefaultWeb3Handler implements Web3Handler, FullnodeHandler {
}

protected constructor(
private readonly provider: Web3Provider,
protected readonly provider: Web3Provider,
private readonly wallet: Wallet,
private readonly executionManager: Contract
) {
Expand Down
2 changes: 2 additions & 0 deletions packages/rollup-full-node/src/types/web3-rpc-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export enum Web3RpcMethods {
getTransactionReceipt = 'eth_getTransactionReceipt',
networkVersion = 'net_version',
sendRawTransaction = 'eth_sendRawTransaction',
snapshot = 'evm_snapshot',
revert = 'evm_revert',

// Test methods:
increaseTimestamp = 'evm_increaseTime',
Expand Down
26 changes: 26 additions & 0 deletions packages/rollup-full-node/test/app/handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,30 @@ describe('Web3Handler', () => {
res.should.equal(storageValue)
})
})

describe('snapshot and revert', () => {
it('should fail (snapshot and revert should only be available in the TestHandler)', async () => {
const httpProvider = new ethers.providers.JsonRpcProvider(baseUrl)
await new Promise((resolve, reject) => {
httpProvider.send('evm_snapshot', []).catch((error) => {
error.message.should.equal('Method not found')
resolve()
})
})

await new Promise((resolve, reject) => {
httpProvider.send('evm_snapshot', []).catch((error) => {
error.message.should.equal('Method not found')
resolve()
})
})

await new Promise((resolve, reject) => {
httpProvider.send('evm_revert', ['0x01']).catch((error) => {
error.message.should.equal('Method not found')
resolve()
})
})
})
})
})
63 changes: 62 additions & 1 deletion packages/rollup-full-node/test/app/test-handler.spec.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import '../setup'
/* External Imports */
import { add0x, getLogger, remove0x } from '@eth-optimism/core-utils'
import { ethers, ContractFactory } from 'ethers'

/* Internal Imports */
import { Web3RpcMethods, TestWeb3Handler } from '../../src'
import {
Web3RpcMethods,
TestWeb3Handler,
FullnodeRpcServer,
DefaultWeb3Handler,
} from '../../src'
import * as SimpleStorage from '../contracts/build/SimpleStorage.json'

const log = getLogger('test-web3-handler', true)

const secondsSinceEopch = (): number => {
return Math.round(Date.now() / 1000)
}
const host = '0.0.0.0'
const port = 9999
const baseUrl = `http://${host}:${port}`

describe('TestHandler', () => {
let testHandler: TestWeb3Handler
Expand Down Expand Up @@ -60,4 +70,55 @@ describe('TestHandler', () => {
)
})
})

describe('Snapshot and revert', () => {
it('should revert state', async () => {
const testRpcServer = new FullnodeRpcServer(testHandler, host, port)

testRpcServer.listen()
const httpProvider = new ethers.providers.JsonRpcProvider(baseUrl)
const executionManagerAddress = await httpProvider.send(
'ovm_getExecutionManagerAddress',
[]
)
const privateKey = '0x' + '60'.repeat(32)
const wallet = new ethers.Wallet(privateKey, httpProvider)
log.debug('Wallet address:', wallet.address)
const factory = new ContractFactory(
SimpleStorage.abi,
SimpleStorage.bytecode,
wallet
)

const simpleStorage = await factory.deploy()
const deploymentTxReceipt = await wallet.provider.getTransactionReceipt(
simpleStorage.deployTransaction.hash
)

const storageKey = '0x' + '01'.repeat(32)
const storageValue = '0x' + '02'.repeat(32)
const storageValue2 = '0x' + '03'.repeat(32)
// Set storage with our new storage elements
const networkInfo = await httpProvider.getNetwork()
const tx = await simpleStorage.setStorage(
executionManagerAddress,
storageKey,
storageValue
)
const snapShotId = await httpProvider.send('evm_snapshot', [])
const tx2 = await simpleStorage.setStorage(
executionManagerAddress,
storageKey,
storageValue2
)
const receipt = await httpProvider.getTransactionReceipt(tx.hash)
const receipt2 = await httpProvider.getTransactionReceipt(tx2.hash)
const response2 = await httpProvider.send('evm_revert', [snapShotId])
const res = await simpleStorage.getStorage(
executionManagerAddress,
storageKey
)
res.should.equal(storageValue)
})
})
})