Skip to content

Commit ee03ab8

Browse files
committed
chore: up
1 parent 8374a9f commit ee03ab8

3 files changed

Lines changed: 187 additions & 99 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,7 @@ await server.start()
101101
#### Requirements
102102

103103
```bash
104-
curl -L https://get.docker.com/ | bash # Install Docker
105-
npm i testcontainers # Install `testcontainers`
104+
curl -L https://tempo.xyz/install | bash # Install Tempo
106105
```
107106

108107
#### Usage

‎src/Instance.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { EventEmitter } from 'eventemitter3'
22

33
export { alto } from './instances/alto.js'
44
export { anvil } from './instances/anvil.js'
5-
export { tempo } from './instances/tempo.js'
5+
export { tempo, tempoDocker } from './instances/tempo.js'
66

77
type EventTypes = {
88
exit: [code: number | null, signal: NodeJS.Signals | null]

‎src/instances/tempo.ts‎

Lines changed: 185 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,59 @@ import {
66
Wait,
77
} from 'testcontainers'
88
import * as Instance from '../Instance.js'
9+
import { execa } from '../processes/execa.js'
10+
11+
export function command(parameters: tempo.Parameters): string[] {
12+
const { faucet, port } = parameters
13+
const dataDir = path.join(os.tmpdir(), '.prool', `tempo.${port}`)
14+
return [
15+
'node',
16+
`--authrpc.port=${port! + 30}`,
17+
`--datadir=${dataDir}`,
18+
'--dev',
19+
`--dev.block-time=${parameters?.blockTime ?? '50ms'}`,
20+
'--engine.disable-precompile-cache',
21+
'--engine.legacy-state-root',
22+
'--faucet.address',
23+
...(faucet?.addresses ?? [
24+
'0x20c0000000000000000000000000000000000000',
25+
'0x20c0000000000000000000000000000000000001',
26+
'0x20c0000000000000000000000000000000000002',
27+
'0x20c0000000000000000000000000000000000003',
28+
]),
29+
`--faucet.amount=${faucet?.amount ?? '1000000000000'}`,
30+
'--faucet.enabled',
31+
`--faucet.private-key=${faucet?.privateKey ?? '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'}`,
32+
'--http',
33+
'--http.addr=0.0.0.0',
34+
'--http.api=all',
35+
'--http.corsdomain=*',
36+
`--http.port=${port!}`,
37+
`--port=${port! + 10}`,
38+
'--txpool.basefee-max-count=10000000000000',
39+
'--txpool.basefee-max-size=10000',
40+
'--txpool.max-account-slots=500000',
41+
'--txpool.pending-max-count=10000000000000',
42+
'--txpool.pending-max-size=10000',
43+
'--txpool.queued-max-count=10000000000000',
44+
'--txpool.queued-max-size=10000',
45+
`--ws.port=${port! + 20}`,
46+
]
47+
}
948

1049
/**
1150
* Defines a Tempo instance.
1251
*
1352
* @example
1453
* ```ts
15-
* const instance = tempo({ port: 8545 })
54+
* const instance = Instance.tempo({ port: 8545 })
1655
* await instance.start()
1756
* // ...
1857
* await instance.stop()
1958
* ```
2059
*/
2160
export const tempo = Instance.define((parameters?: tempo.Parameters) => {
22-
const {
23-
containerName = `tempo.${crypto.randomUUID()}`,
24-
image = 'ghcr.io/tempoxyz/tempo:latest',
25-
log: log_,
26-
...args
27-
} = parameters || {}
61+
const { binary = 'tempo', log: log_, ...args } = parameters || {}
2862

2963
const log = (() => {
3064
try {
@@ -36,115 +70,60 @@ export const tempo = Instance.define((parameters?: tempo.Parameters) => {
3670
const RUST_LOG = log && typeof log !== 'boolean' ? log : ''
3771

3872
const name = 'tempo'
39-
let container: StartedTestContainer | undefined
73+
const process = execa({ name })
4074

4175
return {
4276
_internal: {
4377
args,
78+
get process() {
79+
return process._internal.process
80+
},
4481
},
4582
host: args.host ?? 'localhost',
4683
name,
4784
port: args.port ?? 8545,
48-
async start({ port = args.port }, { emitter }) {
49-
const promise = Promise.withResolvers<void>()
50-
51-
const dataDir = path.join(os.tmpdir(), '.prool', containerName)
52-
const c = new GenericContainer(image)
53-
.withPlatform('linux/x86_64')
54-
.withNetworkMode('host')
55-
.withExtraHosts([
56-
{ host: 'host.docker.internal', ipAddress: 'host-gateway' },
57-
{ host: 'localhost', ipAddress: 'host-gateway' },
58-
])
59-
.withName(containerName)
60-
.withEnvironment({ RUST_LOG })
61-
.withCommand([
62-
'node',
63-
`--authrpc.port=${port! + 30}`,
64-
`--datadir=${dataDir}`,
65-
'--dev',
66-
`--dev.block-time=${parameters?.blockTime ?? '50ms'}`,
67-
'--engine.disable-precompile-cache',
68-
'--engine.legacy-state-root',
69-
'--faucet.address',
70-
...(parameters?.faucet?.addresses ?? [
71-
'0x20c0000000000000000000000000000000000000',
72-
'0x20c0000000000000000000000000000000000001',
73-
'0x20c0000000000000000000000000000000000002',
74-
'0x20c0000000000000000000000000000000000003',
75-
]),
76-
`--faucet.amount=${parameters?.faucet?.amount ?? '1000000000000'}`,
77-
'--faucet.enabled',
78-
`--faucet.private-key=${parameters?.faucet?.privateKey ?? '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'}`,
79-
'--http',
80-
'--http.addr=0.0.0.0',
81-
'--http.api=all',
82-
'--http.corsdomain=*',
83-
`--http.port=${port!}`,
84-
`--port=${port! + 10}`,
85-
'--txpool.basefee-max-count=10000000000000',
86-
'--txpool.basefee-max-size=10000',
87-
'--txpool.max-account-slots=500000',
88-
'--txpool.pending-max-count=10000000000000',
89-
'--txpool.pending-max-size=10000',
90-
'--txpool.queued-max-count=10000000000000',
91-
'--txpool.queued-max-size=10000',
92-
`--ws.port=${port! + 20}`,
93-
])
94-
.withWaitStrategy(Wait.forLogMessage(/RPC HTTP server started/))
95-
.withLogConsumer((stream) => {
96-
stream.on('data', (data) => {
97-
const message = data.toString()
98-
emitter.emit('message', message)
99-
emitter.emit('stdout', message)
100-
if (log) console.log(message)
101-
if (message.includes('shutting down'))
102-
promise.reject(new Error(`Failed to start: ${message}`))
103-
})
104-
stream.on('error', (error) => {
105-
if (log) console.error(error.message)
106-
emitter.emit('message', error.message)
107-
emitter.emit('stderr', error.message)
108-
promise.reject(new Error(`Failed to start: ${error.message}`))
109-
})
110-
})
111-
.withStartupTimeout(120_000)
112-
113-
c.start()
114-
.then((c) => {
115-
container = c
116-
promise.resolve()
117-
})
118-
.catch(promise.reject)
119-
120-
return promise.promise
85+
async start({ port = args.port }, options) {
86+
return await process.start(
87+
($) =>
88+
$({
89+
env: {
90+
RUST_LOG,
91+
},
92+
})`${[binary, ...command({ ...parameters, port })]}`,
93+
{
94+
...options,
95+
// Resolve when the process is listening via "RPC HTTP server started" message.
96+
resolver({ process, reject, resolve }) {
97+
process.stdout.on('data', (data) => {
98+
const message = data.toString()
99+
if (log) console.log(message)
100+
if (message.includes('RPC HTTP server started')) resolve()
101+
if (message.includes('shutting down')) reject('shutting down')
102+
})
103+
process.stderr.on('data', (data) => {
104+
const message = data.toString()
105+
if (log) console.error(message)
106+
})
107+
},
108+
},
109+
)
121110
},
122111
async stop() {
123-
if (!container) return
124-
await container.stop()
125-
container = undefined
112+
await process.stop()
126113
},
127114
}
128115
})
129116

130117
export declare namespace tempo {
131118
export type Parameters = {
132119
/**
133-
* Interval between blocks.
134-
*/
135-
blockTime?: string | undefined
136-
/**
137-
* Name of the container.
138-
*/
139-
containerName?: string | undefined
140-
/**
141-
* Chain this node is running.
120+
* Path or alias to the Tempo binary.
142121
*/
143-
chain?: string | undefined
122+
binary?: string | undefined
144123
/**
145-
* Docker image to use.
124+
* Interval between blocks.
146125
*/
147-
image?: string | undefined
126+
blockTime?: string | undefined
148127
/**
149128
* Faucet options.
150129
*/
@@ -187,3 +166,113 @@ export declare namespace tempo {
187166
port?: number | undefined
188167
}
189168
}
169+
170+
/**
171+
* Defines a Tempo instance.
172+
*
173+
* @example
174+
* ```ts
175+
* const instance = Instance.tempoDocker({ port: 8545 })
176+
* await instance.start()
177+
* // ...
178+
* await instance.stop()
179+
* ```
180+
*/
181+
export const tempoDocker = Instance.define(
182+
(parameters?: tempoDocker.Parameters) => {
183+
const {
184+
containerName = `tempo.${crypto.randomUUID()}`,
185+
image = 'ghcr.io/tempoxyz/tempo:latest',
186+
log: log_,
187+
...args
188+
} = parameters || {}
189+
190+
const log = (() => {
191+
try {
192+
return JSON.parse(log_ as string)
193+
} catch {
194+
return log_
195+
}
196+
})()
197+
const RUST_LOG = log && typeof log !== 'boolean' ? log : ''
198+
199+
const name = 'tempo'
200+
let container: StartedTestContainer | undefined
201+
202+
return {
203+
_internal: {
204+
args,
205+
},
206+
host: args.host ?? 'localhost',
207+
name,
208+
port: args.port ?? 8545,
209+
async start({ port = args.port }, { emitter }) {
210+
const promise = Promise.withResolvers<void>()
211+
212+
const c = new GenericContainer(image)
213+
.withPlatform('linux/x86_64')
214+
.withNetworkMode('host')
215+
.withExtraHosts([
216+
{ host: 'host.docker.internal', ipAddress: 'host-gateway' },
217+
{ host: 'localhost', ipAddress: 'host-gateway' },
218+
])
219+
.withName(containerName)
220+
.withEnvironment({ RUST_LOG })
221+
.withCommand(command({ ...parameters, port }))
222+
.withWaitStrategy(Wait.forLogMessage(/RPC HTTP server started/))
223+
.withLogConsumer((stream) => {
224+
stream.on('data', (data) => {
225+
const message = data.toString()
226+
emitter.emit('message', message)
227+
emitter.emit('stdout', message)
228+
if (log) console.log(message)
229+
if (message.includes('shutting down'))
230+
promise.reject(new Error(`Failed to start: ${message}`))
231+
})
232+
stream.on('error', (error) => {
233+
if (log) console.error(error.message)
234+
emitter.emit('message', error.message)
235+
emitter.emit('stderr', error.message)
236+
promise.reject(new Error(`Failed to start: ${error.message}`))
237+
})
238+
})
239+
.withStartupTimeout(10_000)
240+
241+
c.start()
242+
.then((c) => {
243+
container = c
244+
promise.resolve()
245+
})
246+
.catch(promise.reject)
247+
248+
return promise.promise
249+
},
250+
async stop() {
251+
if (!container) return
252+
await container.stop()
253+
container = undefined
254+
},
255+
}
256+
},
257+
)
258+
259+
export declare namespace tempoDocker {
260+
export type Parameters = Omit<tempo.Parameters, 'binary'> & {
261+
/**
262+
* Name of the container.
263+
*/
264+
containerName?: string | undefined
265+
/**
266+
* Docker image to use.
267+
*/
268+
image?: string | undefined
269+
/**
270+
* Host the server will listen on.
271+
*/
272+
host?: string | undefined
273+
/**
274+
* Port the server will listen on.
275+
*/
276+
port?: number | undefined
277+
}
278+
}

0 commit comments

Comments
 (0)