Skip to content

Commit

Permalink
feat: add rundler instance definition (#9)
Browse files Browse the repository at this point in the history
* feat: add rundler instance definition

* chore: tweaks

* fix: types

---------

Co-authored-by: jxom <[email protected]>
  • Loading branch information
moldy530 and jxom authored Jun 22, 2024
1 parent 7d8d3f4 commit 8b2cb57
Show file tree
Hide file tree
Showing 14 changed files with 808 additions and 48 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ jobs:
- name: Set up Foundry
uses: foundry-rs/foundry-toolchain@v1

- name: Set up Rundler
uses: jaxxstorm/[email protected]
with:
repo: alchemyplatform/rundler
platform: linux

- name: Set up Docker
uses: docker-practice/actions-setup-docker@master

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ coverage
node_modules
tsconfig.*.tsbuildinfo
tsconfig.tsbuildinfo

.tool-versions
bin
60 changes: 60 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 45 additions & 2 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Prool is a library that provides programmatic HTTP testing instances for Ethereu
Prool contains a set of pre-configured instances that can be used to simulate Ethereum server environments, being:

- **Local Execution Nodes:** [`anvil`](#anvil-execution-node)
- **Bundler Nodes:** [`alto`](#alto-bundler-node), `rundler`⚠️, `silius`⚠️, [`stackup`](#stackup-bundler-node)
- **Bundler Nodes:** [`alto`](#alto-bundler-node), [`rundler`](#rundler), `silius`⚠️, [`stackup`](#stackup-bundler-node)
- **Indexer Nodes:** `ponder`⚠️

⚠️ = soon
Expand All @@ -52,6 +52,7 @@ You can also create your own custom instances by using the [`defineInstance` fun
- [Getting Started](#getting-started)
- [Anvil (Execution Node)](#anvil-execution-node)
- [Alto (Bundler Node)](#alto-bundler-node)
- [Rundler (Bundler Node)](#rundler-bundler-node)
- [Stackup (Bundler Node)](#stackup-bundler-node)
- [Reference](#reference)
- [`createServer`](#createserver)
Expand Down Expand Up @@ -79,7 +80,8 @@ bun i prool

#### Requirements

- [Foundry](https://getfoundry.sh/): `curl -L https://foundry.paradigm.xyz | bash`
- [Foundry](https://getfoundry.sh/) binary installed
- Download: `curl -L https://foundry.paradigm.xyz | bash`

#### Usage

Expand Down Expand Up @@ -145,6 +147,47 @@ await bundlerServer.start()

See [`AltoParameters`](https://github.com/wevm/prool/blob/801ede06ded8b2cb2d59c95294aae795e548897c/src/instances/alto.ts#L7).

### Rundler (Bundler Node)

#### Requirements

- [Rundler](https://github.com/alchemyplatform/rundler) binary installed
- [Download](https://github.com/alchemyplatform/rundler/releases)

#### Usage

```ts
import { createServer } from 'prool'
import { anvil, rundler } from 'prool/instances'

const executionServer = createServer({
instance: anvil(),
port: 8545
})
await executionServer.start()
// Instances accessible at:
// "http://localhost:8545/1"
// "http://localhost:8545/2"
// "http://localhost:8545/3"
// "http://localhost:8545/n"

const bundlerServer = createServer({
instance: (key) => rundler({
nodeHttp: `http://localhost:8545/${key}`,
})
})
await bundlerServer.start()
// Instances accessible at:
// "http://localhost:3000/1" (→ http://localhost:8545/1)
// "http://localhost:3000/2" (→ http://localhost:8545/2)
// "http://localhost:3000/3" (→ http://localhost:8545/3)
// "http://localhost:3000/n" (→ http://localhost:8545/n)
```

#### Parameters

See [RundlerParameters]().

### Stackup (Bundler Node)

#### Requirements
Expand Down
1 change: 1 addition & 0 deletions src/exports/instances.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ test('exports', () => {
[
"alto",
"anvil",
"rundler",
"stackup",
]
`)
Expand Down
7 changes: 4 additions & 3 deletions src/exports/instances.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { type AltoParameters, alto } from '../instances/alto.js'
export { type AnvilParameters, anvil } from '../instances/anvil.js'
export { type StackupParameters, stackup } from '../instances/stackup.js'
export { alto, type AltoParameters } from '../instances/alto.js'
export { anvil, type AnvilParameters } from '../instances/anvil.js'
export { rundler, type RundlerParameters } from '../instances/rundler.js'
export { stackup, type StackupParameters } from '../instances/stackup.js'
115 changes: 115 additions & 0 deletions src/instances/rundler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { afterEach, expect, test } from 'vitest'
import type { Instance } from '../instance.js'
import { type RundlerParameters, rundler } from './rundler.js'

const instances: Instance[] = []

const defineInstance = (parameters: RundlerParameters = {}) => {
const instance = rundler(parameters)
instances.push(instance)
return instance
}

afterEach(async () => {
for (const instance of instances) await instance.stop()
})

test('default', async () => {
const messages: string[] = []
const stdouts: string[] = []

const instance = defineInstance()

instance.on('message', (m) => messages.push(m))
instance.on('stdout', (m) => stdouts.push(m))

expect(instance.messages.get()).toMatchInlineSnapshot('[]')

await instance.start()
expect(instance.status).toEqual('started')

expect(messages.join('')).toBeDefined()
expect(stdouts.join('')).toBeDefined()
expect(instance.messages.get().join('')).toBeDefined()

await instance.stop()
expect(instance.status).toEqual('stopped')

expect(messages.join('')).toBeDefined()
expect(stdouts.join('')).toBeDefined()
expect(instance.messages.get()).toMatchInlineSnapshot('[]')
})

test('behavior: instance errored (duplicate ports)', async () => {
const instance_1 = defineInstance({
rpc: {
port: 1337,
},
})
const instance_2 = defineInstance({
rpc: {
port: 1337,
},
})

await instance_1.start()
await expect(() =>
instance_2.start(),
).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Failed to start process "rundler": exited]`,
)
})

test('behavior: start and stop multiple times', async () => {
const instance = defineInstance()

await instance.start()
await instance.stop()
await instance.start()
await instance.stop()
await instance.start()
await instance.stop()
await instance.start()
await instance.stop()
})

test('behavior: can subscribe to stderr', async () => {
const messages: string[] = []

const instance_1 = defineInstance({
rpc: {
port: 1338,
},
})
const instance_2 = defineInstance({
rpc: {
port: 1338,
},
})

await instance_1.start()
instance_2.on('stderr', (message) => messages.push(message))
await expect(() =>
instance_2.start(),
).rejects.toThrowErrorMatchingInlineSnapshot(
`[Error: Failed to start process "rundler": exited]`,
)
})

test('behavior: exit', async () => {
const instance = defineInstance()

let exitCode: number | null | undefined = undefined
instance.on('exit', (code) => {
exitCode = code
})

await instance.start()
expect(instance.status).toEqual('started')

instance._internal.process.kill()

await new Promise<void>((res) => setTimeout(res, 100))
expect(instance.status).toEqual('stopped')
expect(typeof exitCode !== 'undefined').toBeTruthy()
})
Loading

0 comments on commit 8b2cb57

Please sign in to comment.