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

initializes service:bridge-relay command #4314

Merged
merged 5 commits into from
Sep 22, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 136 additions & 0 deletions ironfish-cli/src/commands/service/bridge/relay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { Assert, GetTransactionStreamResponse, Meter, TimeUtils, WebApi } from '@ironfish/sdk'
import { Flags } from '@oclif/core'
import { IronfishCommand } from '../../../command'
import { RemoteFlags } from '../../../flags'

export default class BridgeRelay extends IronfishCommand {
static hidden = true

static description = `
Relay Iron Fish deposits to the Sepolia bridge contract
`

static flags = {
...RemoteFlags,
endpoint: Flags.string({
char: 'e',
description: 'API host to sync to',
parse: (input: string) => Promise.resolve(input.trim()),
env: 'IRONFISH_API_HOST',
}),
token: Flags.string({
char: 't',
description: 'API token to authenticate with',
parse: (input: string) => Promise.resolve(input.trim()),
env: 'IRONFISH_API_TOKEN',
}),
incomingViewKey: Flags.string({
char: 'k',
description: 'View key to watch transactions with',
parse: (input: string): Promise<string> => Promise.resolve(input.trim()),
required: true,
}),
confirmations: Flags.integer({
char: 'c',
description: 'Minimum number of block confirmations needed to process deposits',
required: false,
}),
fromHead: Flags.string({
char: 'f',
description: 'The block hash to start following at',
required: false,
}),
}

async start(): Promise<void> {
const { flags } = await this.parse(BridgeRelay)

if (!flags.endpoint) {
this.log(
`No api host set. You must set IRONFISH_API_HOST env variable or pass --endpoint flag.`,
)
this.exit(1)
}

if (!flags.token) {
this.log(
`No api token set. You must set IRONFISH_API_TOKEN env variable or pass --token flag.`,
)
this.exit(1)
}

const api = new WebApi({ host: flags.endpoint, token: flags.token })

const confirmations = flags.confirmations ?? this.sdk.config.get('confirmations')

await this.syncBlocks(api, flags.incomingViewKey, confirmations, flags.fromHead)
}

async syncBlocks(
api: WebApi,
incomingViewKey: string,
confirmations: number,
head?: string,
): Promise<void> {
this.log('Connecting to node...')
const client = await this.sdk.connectRpc()

this.log('Watching with view key:', incomingViewKey)

// TODO: track chain state of relay in API
if (!head) {
const chainInfo = await client.chain.getChainInfo()
head = chainInfo.content.genesisBlockIdentifier.hash
}
this.log(`Starting from head ${head}`)

const response = client.chain.getTransactionStream({
incomingViewKey: incomingViewKey,
head,
})

const speed = new Meter()
speed.start()

const buffer = new Array<GetTransactionStreamResponse>()

for await (const content of response.contentStream()) {
if (content.type === 'connected') {
buffer.push(content)
speed.add(1)
} else if (content.type === 'disconnected') {
buffer.pop()
}

this.logger.debug(
`${content.type}: ${content.block.hash} - ${content.block.sequence}${
' - ' +
TimeUtils.renderEstimate(content.block.sequence, content.head.sequence, speed.rate5m)
}`,
)

if (buffer.length > confirmations) {
const response = buffer.shift()
Assert.isNotUndefined(response)
this.commit(api, response)
}
}
}

commit(api: WebApi, response: GetTransactionStreamResponse): void {
Assert.isNotUndefined(response)

const transactions = response.transactions

for (const transaction of transactions) {
for (const note of transaction.notes) {
this.log(`Processing deposit ${note.memo}, from transaction ${transaction.hash}`)
// TODO: get Eth deposit address from API
// TODO: call Eth bridge contract to mint
}
}
}
}