Commit a1927636 authored by Annie Ke's avatar Annie Ke Committed by Kelvin Fichter

refactor: move genesis provider to regenesis package

parent 7c352b1e
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
'@eth-optimism/core-utils': patch '@eth-optimism/core-utils': patch
--- ---
Add GenesisJsonProvider Add bytes32ify
...@@ -46,12 +46,9 @@ ...@@ -46,12 +46,9 @@
}, },
"dependencies": { "dependencies": {
"@ethersproject/abstract-provider": "^5.4.1", "@ethersproject/abstract-provider": "^5.4.1",
"@ethersproject/bignumber": "^5.5.0",
"@ethersproject/bytes": "^5.5.0", "@ethersproject/bytes": "^5.5.0",
"@ethersproject/properties": "^5.5.0",
"@ethersproject/providers": "^5.4.5", "@ethersproject/providers": "^5.4.5",
"chai": "^4.3.4", "chai": "^4.3.4",
"ethereumjs-util": "^7.1.3",
"ethers": "^5.4.5", "ethers": "^5.4.5",
"lodash": "^4.17.21" "lodash": "^4.17.21"
} }
......
...@@ -3,26 +3,7 @@ ...@@ -3,26 +3,7 @@
*/ */
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { BigNumber, BigNumberish } from '@ethersproject/bignumber'
import { Deferrable } from '@ethersproject/properties'
import { Provider } from '@ethersproject/providers' import { Provider } from '@ethersproject/providers'
import {
Provider as AbstractProvider,
EventType,
TransactionRequest,
TransactionResponse,
TransactionReceipt,
Filter,
Log,
Block,
BlockWithTransactions,
BlockTag,
Listener,
} from '@ethersproject/abstract-provider'
import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util'
import { State, Genesis } from './types'
import { bytes32ify, remove0x, add0x } from './common/hex-strings'
// Copied from @ethersproject/providers since it is not // Copied from @ethersproject/providers since it is not
// currently exported // currently exported
...@@ -57,200 +38,3 @@ export const FallbackProvider = (config: string | FallbackProviderConfig[]) => { ...@@ -57,200 +38,3 @@ export const FallbackProvider = (config: string | FallbackProviderConfig[]) => {
} }
return new ethers.providers.FallbackProvider(config) return new ethers.providers.FallbackProvider(config)
} }
export class GenesisJsonProvider implements AbstractProvider {
genesis: Genesis
constructor(genesis: string | Genesis) {
if (typeof genesis === 'string') {
this.genesis = require(genesis)
} else if (typeof genesis === 'object') {
this.genesis = genesis
}
if (this.genesis === null) {
throw new Error('Must initialize with genesis object')
}
}
async getBalance(
addressOrName: string,
blockTag?: BlockTag
): Promise<BigNumber> {
const address = remove0x(addressOrName)
const account = this.genesis.alloc[address]
if (!account) {
return BigNumber.from(0)
}
return BigNumber.from(account.balance)
}
async getTransactionCount(
addressOrName: string,
blockTag?: BlockTag
): Promise<number> {
const address = remove0x(addressOrName)
const account = this.genesis.alloc[address]
if (!account) {
return 0
}
return account.nonce
}
async getCode(addressOrName: string, blockTag?: BlockTag): Promise<string> {
const address = remove0x(addressOrName)
const account = this.genesis.alloc[address]
if (!account) {
return '0x'
}
return add0x(account.code)
}
async getStorageAt(
addressOrName: string,
position: BigNumber | number,
blockTag?: BlockTag
): Promise<string> {
const address = remove0x(addressOrName)
const account = this.genesis.alloc[address]
if (!account) {
return '0x'
}
const bytes32 = bytes32ify(position)
const storage = account.storage[remove0x(bytes32)]
if (!storage) {
return '0x'
}
return add0x(storage)
}
async call(
transaction: Deferrable<TransactionRequest>,
blockTag?: BlockTag | Promise<BlockTag>
): Promise<string> {
throw new Error('Unsupported Method: call')
}
async send(method: string, args: Array<any>): Promise<any> {
switch (method) {
case 'eth_getProof': {
const address = args[0]
if (!address) {
throw new Error('Must pass address as first arg')
}
const account = this.genesis.alloc[remove0x(address)]
// The account doesn't exist or is an EOA
if (!account || !account.code || account.code === '0x') {
return {
codeHash: add0x(KECCAK256_NULL_S),
storageHash: add0x(KECCAK256_RLP_S),
}
}
return {
codeHash: ethers.utils.keccak256('0x' + account.code),
storageHash: add0x(account.root),
}
}
default:
throw new Error(`Unsupported Method: send ${method}`)
}
}
async getNetwork() {
return undefined
}
async getBlockNumber(): Promise<number> {
return 0
}
async getGasPrice(): Promise<BigNumber> {
return BigNumber.from(0)
}
async getFeeData() {
return undefined
}
async sendTransaction(
signedTransaction: string | Promise<string>
): Promise<TransactionResponse> {
throw new Error('Unsupported Method: sendTransaction')
}
async estimateGas(
transaction: Deferrable<TransactionRequest>
): Promise<BigNumber> {
return BigNumber.from(0)
}
async getBlock(
blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>
): Promise<Block> {
throw new Error('Unsupported Method: getBlock')
}
async getBlockWithTransactions(
blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>
): Promise<BlockWithTransactions> {
throw new Error('Unsupported Method: getBlockWithTransactions')
}
async getTransaction(transactionHash: string): Promise<TransactionResponse> {
throw new Error('Unsupported Method: getTransaction')
}
async getTransactionReceipt(
transactionHash: string
): Promise<TransactionReceipt> {
throw new Error('Unsupported Method: getTransactionReceipt')
}
async getLogs(filter: Filter): Promise<Array<Log>> {
throw new Error('Unsupported Method: getLogs')
}
async resolveName(name: string | Promise<string>): Promise<null | string> {
throw new Error('Unsupported Method: resolveName')
}
async lookupAddress(
address: string | Promise<string>
): Promise<null | string> {
throw new Error('Unsupported Method: lookupAddress')
}
on(eventName: EventType, listener: Listener): Provider {
throw new Error('Unsupported Method: on')
}
once(eventName: EventType, listener: Listener): Provider {
throw new Error('Unsupported Method: once')
}
emit(eventName: EventType, ...args: Array<any>): boolean {
throw new Error('Unsupported Method: emit')
}
listenerCount(eventName?: EventType): number {
throw new Error('Unsupported Method: listenerCount')
}
listeners(eventName?: EventType): Array<Listener> {
throw new Error('Unsupported Method: listeners')
}
off(eventName: EventType, listener?: Listener): Provider {
throw new Error('Unsupported Method: off')
}
removeAllListeners(eventName?: EventType): Provider {
throw new Error('Unsupported Method: removeAllListeners')
}
addListener(eventName: EventType, listener: Listener): Provider {
throw new Error('Unsupported Method: addListener')
}
removeListener(eventName: EventType, listener: Listener): Provider {
throw new Error('Unsupported Method: removeListener')
}
async waitForTransaction(
transactionHash: string,
confirmations?: number,
timeout?: number
): Promise<TransactionReceipt> {
throw new Error('Unsupported Method: waitForTransaction')
}
readonly _isProvider: boolean
}
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
"devDependencies": { "devDependencies": {
"@discoveryjs/json-ext": "^0.5.3", "@discoveryjs/json-ext": "^0.5.3",
"@eth-optimism/core-utils": "^0.6.0", "@eth-optimism/core-utils": "^0.6.0",
"@ethersproject/abstract-provider": "^5.5.1",
"@ethersproject/bignumber": "^5.5.0",
"@ethersproject/properties": "^5.5.0",
"@ethersproject/providers": "^5.5.0",
"@types/node": "^15.12.2", "@types/node": "^15.12.2",
"@types/node-fetch": "^3.0.3", "@types/node-fetch": "^3.0.3",
"@uniswap/sdk-core": "^3.0.1", "@uniswap/sdk-core": "^3.0.1",
...@@ -24,13 +28,13 @@ ...@@ -24,13 +28,13 @@
"byline": "^5.0.0", "byline": "^5.0.0",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"ethereumjs-util": "^7.1.2", "ethereumjs-util": "^7.1.3",
"ethers": "^5.4.5", "ethers": "^5.4.5",
"hardhat": "^2.6.5", "hardhat": "^2.6.5",
"lint-staged": "11.0.0",
"mocha": "^9.1.2", "mocha": "^9.1.2",
"node-fetch": "2.6.5", "node-fetch": "2.6.5",
"solc": "0.8.7-fixed", "solc": "0.8.7-fixed",
"ts-node": "^10.0.0", "ts-node": "^10.0.0"
"lint-staged": "11.0.0"
} }
} }
import { expect } from './setup' import { expect } from '@eth-optimism/core-utils/test/setup'
import { ethers, BigNumber } from 'ethers' import { ethers, BigNumber } from 'ethers'
import { GenesisJsonProvider } from '../src/provider' import { GenesisJsonProvider } from './provider'
import { Genesis } from '../src/types' import { Genesis } from '@eth-optimism/core-utils/src/types'
import { remove0x, add0x } from '../src/common/hex-strings' import {
remove0x,
add0x,
} from '@eth-optimism/core-utils/src/common/hex-strings'
import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util' import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util'
const account = '0x66a84544bed4ca45b3c024776812abf87728fbaf' const account = '0x66a84544bed4ca45b3c024776812abf87728fbaf'
...@@ -110,4 +113,9 @@ describe.only('GenesisJsonProvider', () => { ...@@ -110,4 +113,9 @@ describe.only('GenesisJsonProvider', () => {
expect(proof.codeHash).to.eq(add0x(KECCAK256_NULL_S)) expect(proof.codeHash).to.eq(add0x(KECCAK256_NULL_S))
expect(proof.storageHash).to.eq(add0x(KECCAK256_RLP_S)) expect(proof.storageHash).to.eq(add0x(KECCAK256_RLP_S))
}) })
it('should also initialize correctly with state dump', async () => {
provider = new GenesisJsonProvider(genesis.alloc)
expect(provider).to.be.instanceOf(GenesisJsonProvider)
})
}) })
import { ethers } from 'ethers'
import { BigNumber } from '@ethersproject/bignumber'
import { Deferrable } from '@ethersproject/properties'
import { Provider } from '@ethersproject/providers'
import {
Provider as AbstractProvider,
EventType,
TransactionRequest,
TransactionResponse,
TransactionReceipt,
Filter,
Log,
Block,
BlockWithTransactions,
BlockTag,
Listener,
} from '@ethersproject/abstract-provider'
import { KECCAK256_RLP_S, KECCAK256_NULL_S } from 'ethereumjs-util'
import { bytes32ify, remove0x, add0x } from '@eth-optimism/core-utils'
// Represents the ethereum state
export interface State {
[address: string]: {
nonce: number
balance: string
codeHash: string
root: string
code?: string
storage?: {
[key: string]: string
}
}
}
// Represents a genesis file that geth can consume
export interface Genesis {
config: {
chainId: number
homesteadBlock: number
eip150Block: number
eip155Block: number
eip158Block: number
byzantiumBlock: number
constantinopleBlock: number
petersburgBlock: number
istanbulBlock: number
muirGlacierBlock: number
clique: {
period: number
epoch: number
}
}
difficulty: string
gasLimit: string
extraData: string
alloc: State
}
export class GenesisJsonProvider implements AbstractProvider {
state: State
constructor(dump: string | Genesis | State) {
let input
if (typeof dump === 'string') {
input = require(dump)
} else if (typeof dump === 'object') {
input = dump
}
this.state = input.alloc ? input.alloc : input
if (this.state === null) {
throw new Error('Must initialize with genesis or state object')
}
}
async getBalance(
addressOrName: string,
blockTag?: BlockTag
): Promise<BigNumber> {
const address = remove0x(addressOrName)
const account = this.state[address]
if (!account) {
return BigNumber.from(0)
}
return BigNumber.from(account.balance)
}
async getTransactionCount(
addressOrName: string,
blockTag?: BlockTag
): Promise<number> {
const address = remove0x(addressOrName)
const account = this.state[address]
if (!account) {
return 0
}
return account.nonce
}
async getCode(addressOrName: string, blockTag?: BlockTag): Promise<string> {
const address = remove0x(addressOrName)
const account = this.state[address]
if (!account) {
return '0x'
}
return add0x(account.code)
}
async getStorageAt(
addressOrName: string,
position: BigNumber | number,
blockTag?: BlockTag
): Promise<string> {
const address = remove0x(addressOrName)
const account = this.state[address]
if (!account) {
return '0x'
}
const bytes32 = bytes32ify(position)
const storage = account.storage[remove0x(bytes32)]
if (!storage) {
return '0x'
}
return add0x(storage)
}
async call(
transaction: Deferrable<TransactionRequest>,
blockTag?: BlockTag | Promise<BlockTag>
): Promise<string> {
throw new Error('Unsupported Method: call')
}
async send(method: string, args: Array<any>): Promise<any> {
switch (method) {
case 'eth_getProof': {
const address = args[0]
if (!address) {
throw new Error('Must pass address as first arg')
}
const account = this.state[remove0x(address)]
// The account doesn't exist or is an EOA
if (!account || !account.code || account.code === '0x') {
return {
codeHash: add0x(KECCAK256_NULL_S),
storageHash: add0x(KECCAK256_RLP_S),
}
}
return {
codeHash: ethers.utils.keccak256('0x' + account.code),
storageHash: add0x(account.root),
}
}
default:
throw new Error(`Unsupported Method: send ${method}`)
}
}
async getNetwork() {
return undefined
}
async getBlockNumber(): Promise<number> {
return 0
}
async getGasPrice(): Promise<BigNumber> {
return BigNumber.from(0)
}
async getFeeData() {
return undefined
}
async sendTransaction(
signedTransaction: string | Promise<string>
): Promise<TransactionResponse> {
throw new Error('Unsupported Method: sendTransaction')
}
async estimateGas(
transaction: Deferrable<TransactionRequest>
): Promise<BigNumber> {
return BigNumber.from(0)
}
async getBlock(
blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>
): Promise<Block> {
throw new Error('Unsupported Method: getBlock')
}
async getBlockWithTransactions(
blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>
): Promise<BlockWithTransactions> {
throw new Error('Unsupported Method: getBlockWithTransactions')
}
async getTransaction(transactionHash: string): Promise<TransactionResponse> {
throw new Error('Unsupported Method: getTransaction')
}
async getTransactionReceipt(
transactionHash: string
): Promise<TransactionReceipt> {
throw new Error('Unsupported Method: getTransactionReceipt')
}
async getLogs(filter: Filter): Promise<Array<Log>> {
throw new Error('Unsupported Method: getLogs')
}
async resolveName(name: string | Promise<string>): Promise<null | string> {
throw new Error('Unsupported Method: resolveName')
}
async lookupAddress(
address: string | Promise<string>
): Promise<null | string> {
throw new Error('Unsupported Method: lookupAddress')
}
on(eventName: EventType, listener: Listener): Provider {
throw new Error('Unsupported Method: on')
}
once(eventName: EventType, listener: Listener): Provider {
throw new Error('Unsupported Method: once')
}
emit(eventName: EventType, ...args: Array<any>): boolean {
throw new Error('Unsupported Method: emit')
}
listenerCount(eventName?: EventType): number {
throw new Error('Unsupported Method: listenerCount')
}
listeners(eventName?: EventType): Array<Listener> {
throw new Error('Unsupported Method: listeners')
}
off(eventName: EventType, listener?: Listener): Provider {
throw new Error('Unsupported Method: off')
}
removeAllListeners(eventName?: EventType): Provider {
throw new Error('Unsupported Method: removeAllListeners')
}
addListener(eventName: EventType, listener: Listener): Provider {
throw new Error('Unsupported Method: addListener')
}
removeListener(eventName: EventType, listener: Listener): Provider {
throw new Error('Unsupported Method: removeListener')
}
async waitForTransaction(
transactionHash: string,
confirmations?: number,
timeout?: number
): Promise<TransactionReceipt> {
throw new Error('Unsupported Method: waitForTransaction')
}
readonly _isProvider: boolean
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment