Skip to content

Commit

Permalink
support hardforks
Browse files Browse the repository at this point in the history
  • Loading branch information
egasimus committed Nov 28, 2024
1 parent 6efb688 commit 980815c
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 29 deletions.
19 changes: 15 additions & 4 deletions Dockerfile.parameterized
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
FROM ghcr.io/anoma/namada:v0.46.0 AS namada
FROM ghcr.io/anoma/namada:v0.45.1 AS namada-0.45.1
FROM ghcr.io/anoma/namada:v0.46.0 AS namada-0.46.0

# Install system dependencies
USER root
# Install both versions of Namada from before and after hard fork
COPY --from=namada-0.45.1 /usr/local/bin/namada /usr/local/bin/namada-0.45.1
COPY --from=namada-0.45.1 /usr/local/bin/namadac /usr/local/bin/namadac-0.45.1
COPY --from=namada-0.45.1 /usr/local/bin/namadan /usr/local/bin/namadan-0.45.1
COPY --from=namada-0.45.1 /usr/local/bin/namadaw /usr/local/bin/namadaw-0.45.1
RUN mv /usr/local/bin/namada /usr/local/bin/namada-0.46.0 \
&& mv /usr/local/bin/namadac /usr/local/bin/namadac-0.46.0 \
&& mv /usr/local/bin/namadan /usr/local/bin/namadan-0.46.0 \
&& mv /usr/local/bin/namadaw /usr/local/bin/namadaw-0.46.0

# Install system dependencies
RUN apt update \
&& apt install -y vim curl wget unzip procps simpleproxy jq less iputils-ping iproute2 tcpdump strace netcat-traditional
RUN cd /tmp \
Expand All @@ -17,7 +28,7 @@ ENV CHAIN_ID="namada-dryrun.abaaeaf7b78cb3ac"
ENV DATA_DIR="/home/namada/.local/share/namada/$CHAIN_ID"
ENV WASM_DIR="$DATA_DIR/wasm"
ENV CONFIG_DIR="$DATA_DIR/config.toml"
RUN namadac utils join-network --chain-id $CHAIN_ID --wasm-dir $WASM_DIR
RUN namadac-0.45.1 utils join-network --chain-id $CHAIN_ID --wasm-dir $WASM_DIR
RUN sed -i.bak "s#^log_level *=.*#log_level = \"debug\"#" $CONFIG_DIR
RUN sed -i.bak "s#^laddr = \"tcp://127.0.0.1:26657\"#laddr = \"tcp://0.0.0.0:26657\"#" $CONFIG_DIR
RUN sed -i.bak "s#^persistent_peers_max_dial_period *=.*#persistent_peers_max_dial_period = \"5000ms\"#" $CONFIG_DIR
Expand All @@ -37,4 +48,4 @@ ADD deno.json deno.lock deps.js /
RUN deno cache --import-map=/deno.json --lock=/deno.lock deps.js
ADD lib.js services.js status.js rpc_proxy.js control_node.js sync_proxy.js peers.json /
ENTRYPOINT [ "/bin/bash" ]
CMD [ "-c", "/control.js" ]
CMD [ "-c", "/control_node.js" ]
115 changes: 91 additions & 24 deletions control_node.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env -S deno run --allow-net --allow-run=namadan,pkill,pgrep --allow-env=HOST,PORT,NAMADA,CHAIN_ID,NODE_OUT --allow-read=/home/namada/.local/share/namada --allow-write=/home/namada/.local/share/namada
#!/usr/bin/env -S deno run --allow-net --allow-run=namadan-0.45.1,namadan-0.46.0,pkill,pgrep --allow-env=HOST,PORT,NAMADA,CHAIN_ID,NODE_OUT --allow-read=/home/namada/.local/share/namada --allow-write=/home/namada/.local/share/namada
// This service runs the node. In order for the indexer to have time to fetch all data
// before epoched data is pruned, this service parses the log output of the node, and
// when the epoch has incremented it tells the outgoing proxy to cut off outgoing
Expand All @@ -11,15 +11,48 @@ import { Service } from './services.js'
if (import.meta.main) setTimeout(main, 0)

function main () {
// Initialize and configure
initialize()
const { HOST, PORT, NAMADA, CHAIN_ID, NODE_OUT } = environment({
HOST: "0.0.0.0",
PORT: "25551",
NAMADA: "namadan",
NAMADA: "0=namadan-0.45.1,182000=namadan-0.46.0",
CHAIN_ID: "namada-dryrun.abaaeaf7b78cb3ac",
NODE_OUT: "http://sync-proxy:25552"
})

// Namada node service manager
const service = new NamadaService(NAMADA, CHAIN_ID)

// When the node log contains block height and epoch, do the magic
service.events.addEventListener('synced', async ({detail: {block, epoch}}) => {
// Switch to next version of Namada node if hardfork has occurred
block = BigInt(block)
let namada = service.namadas[0n]
// Find the next version to run
for (const hardfork of Object.keys(service.namadas)) {
if (block > hardfork) {
namada = service.namadas[hardfork]
break
}
}
// If the next version is different to the current one, launch it
if (namada != service.namada) {
await service.pause()
service.namada = namada
await service.start()
}
// Pause if epoch has incremented
epoch = BigInt(epoch)
if (epoch > service.epoch) {
service.epoch = epoch
console.log('🟠 Epoch has increased to', epoch)
service.events.dispatchEvent(new RequestPauseEvent())
}
})

// When pause is requested, tell the sync-proxy to disconnect.
// The undexer will tell it to reenable connections when ready to continue.
service.events.addEventListener('request-pause', async () => {
let canConnect = true
while (canConnect) {
Expand All @@ -31,13 +64,18 @@ function main () {
await new Promise(resolve=>setTimeout(resolve, 100))
}
})

// Run HTTP+WS API server
api('Node', HOST, PORT, service.routes(), {
// Notify undexer of sync progress
onOpen: ({ send }) => {
service.events.addEventListener('synced', send)
service.events.addEventListener('synced', event => send(event))
},
// Stop trying to notify undexer of sync progress on disconnect
onClose: ({ send }) => {
service.events.removeEventListener('synced', send)
service.events.removeEventListener('synced', event => send(event))
},
// Respond to resync command from undexer
onMessage: async ({ event }) => {
const data = JSON.parse(event.data)
if (data.restart) {
Expand All @@ -48,46 +86,75 @@ function main () {
}
}
})

// And away we go!
service.start()
}

export class NamadaService extends Service {
constructor (namada = 'namadan', chainId) {

constructor (namadas = "0=namadan-0.45.1,182000=namadan-0.46.0", chainId) {
// Multiple versions of Namada to support hard forks
namadas = Object.fromEntries(namadas
.split(',')
.map(x=>x.split('='))
.map(([block, bin])=>[BigInt(block), bin])
)
const namada = namadas[0n]
if (!namada) {
throw new Error('NAMADA format: 0=...[,HardForkHeight=...]')
}
// Start with 1st version of Namada node
super('Namada', namada, 'ledger', 'run')
// Which version to run starting from which block
this.namadas = namadas
// Currently selected version
this.namada = namada
// Used to find config file
this.chainId = chainId
// Match block increment in log output
this.regex = new RegExp('Block height: (\\d+).+epoch: (\\d+)')
// Brokers events asynchronously
this.events = new EventTarget()
// Current epoch (FIXME: need to persist this!)
this.epoch = 0n
this.start()
}

// Print config before launching node
async start () {
await this.printConfig()
return super.start()
}

// Print config
async printConfig () {
const configPath = `/home/namada/.local/share/namada/${this.chainId}/config.toml`
const config = (await Deno.readTextFile(configPath)).split('\n')
for (const line of config.filter(line=>line.includes('persistent_peers'))) {
console.log('ℹ️ Config:', line)
}
return super.start()
}

// Output from service is parsed line-by-line and passed to callback
pipe (stream, _kind) {
stream
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TextLineStream())
.pipeTo(new WritableStream({ write: (chunk, _) => {
//if (!this.muted) console.log(`:: ${this.name} :: ${kind} :: ${chunk}`)
//this.muted || console.log(`:: ${this.name} :: ${kind} :: ${chunk}`)
if (!this.muted) console.log(chunk)
const match = chunk.match(this.regex)
if (match) {
let [block, epoch] = match.slice(1)
console.log(`🟢 Sync: block ${block} of epoch ${epoch}`)
this.events.dispatchEvent(new SyncEvent({ block, epoch }))
epoch = BigInt(epoch)
if (epoch > this.epoch) {
this.epoch = epoch
console.log('🟠 Epoch has increased to', epoch)
this.events.dispatchEvent(new RequestPauseEvent())
}
}
} }))
.pipeTo(new WritableStream({ write: (chunk, _) => this.onChunk(chunk) }))
}

// Handle block and epoch increments
onChunk (chunk) {
if (!this.muted) {
console.log(chunk)
}
const match = chunk.match(this.regex)
if (match) {
// Report block and epoch progress
const [block, epoch] = match.slice(1)
console.log(`🟢 Sync: block ${block} of epoch ${epoch}`)
this.events.dispatchEvent(new SyncEvent({ block, epoch }))
}
}
/** Delete node state, allowing the sync to start from scratch.
* This is invoked by the indexer when it finds that it is more
Expand Down
2 changes: 1 addition & 1 deletion services.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export class Service extends LogPipe {
return false
}
const { pid } = this.process
await new Deno.Command('pkill', { args: ['-9', 'simpleproxy'] }).spawn().status
await new Deno.Command('pkill', { args: ['-9', this.command] }).spawn().status
console.log('🟠 Stopped:', this.name, 'at PID:', pid)
return await this.state()
}
Expand Down

0 comments on commit 980815c

Please sign in to comment.