Skip to content

Commit

Permalink
feat: multisig-mon (#8119)
Browse files Browse the repository at this point in the history
* feat: multisig-mon

* add to dockerfile and ci pipeline, need for bignumber

* logger.error

* safe nonces
  • Loading branch information
felipe-op authored Nov 9, 2023
1 parent 88d1b38 commit 3dea385
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 59 deletions.
32 changes: 30 additions & 2 deletions .github/workflows/release-docker-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ jobs:
runs-on: ubuntu-latest
# map the step outputs to job outputs
outputs:
fault-mon: ${{ steps.packages.outputs.fault-mon }}
balance-mon: ${{ steps.packages.outputs.balance-mon }}
drippie-mon: ${{ steps.packages.outputs.drippie-mon }}
wd-mon: ${{ steps.packages.outputs.wd-mon }}
fault-mon: ${{ steps.packages.outputs.fault-mon }}
multisig-mon: ${{ steps.packages.outputs.multisig-mon }}
replica-mon: ${{ steps.packages.outputs.replica-mon }}
wd-mon: ${{ steps.packages.outputs.wd-mon }}
canary-docker-tag: ${{ steps.docker-image-name.outputs.canary-docker-tag }}
op-exporter: ${{ steps.packages.outputs.op-exporter }}
endpoint-monitor: ${{ steps.packages.outputs.endpoint-monitor }}
Expand Down Expand Up @@ -97,6 +98,33 @@ jobs:
push: true
tags: ethereumoptimism/balance-mon:${{ needs.canary-publish.outputs.canary-docker-tag }}

multisig-mon:
name: Publish Multisig Monitor Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.multisig-mon != ''
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}

- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: multisig-mon
push: true
tags: ethereumoptimism/multisig-mon:${{ needs.canary-publish.outputs.canary-docker-tag }}

drippie-mon:
name: Publish Drippie Monitor Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
Expand Down
34 changes: 31 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ jobs:
if: github.repository == 'ethereum-optimism/optimism'
# map the step outputs to job outputs
outputs:
fault-mon: ${{ steps.packages.outputs.fault-mon }}
balance-mon: ${{ steps.packages.outputs.drippie-mon }}
balance-mon: ${{ steps.packages.outputs.balance-mon }}
drippie-mon: ${{ steps.packages.outputs.drippie-mon }}
wd-mon: ${{ steps.packages.outputs.wd-mon }}
fault-mon: ${{ steps.packages.outputs.fault-mon }}
multisig-mon: ${{ steps.packages.outputs.multisig-mon }}
replica-mon: ${{ steps.packages.outputs.replica-mon }}
wd-mon: ${{ steps.packages.outputs.wd-mon }}
op-exporter: ${{ steps.packages.outputs.op-exporter }}
endpoint-monitor: ${{ steps.packages.outputs.endpoint-monitor }}
# Permissions necessary for Changesets to push a new branch and open PRs
Expand Down Expand Up @@ -186,6 +187,33 @@ jobs:
push: true
tags: ethereumoptimism/balance-mon:${{ needs.release.outputs.balance-mon }},ethereumoptimism/balance-mon:latest

multisig-mon:
name: Publish Multisig Monitor Version ${{ needs.release.outputs.multisig-mon }}
needs: release
if: needs.release.outputs.multisig-mon != ''
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}

- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.packages
target: multisig-mon
push: true
tags: ethereumoptimism/multisig-mon:${{ needs.release.outputs.multisig-mon }},ethereumoptimism/multisig-mon:latest

drippie-mon:
name: Publish Drippie Monitor Version ${{ needs.release.outputs.drippie-mon }}
needs: release
Expand Down
22 changes: 14 additions & 8 deletions ops/docker/Dockerfile.packages
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,6 @@ WORKDIR /opt/optimism/packages/chain-mon

# TODO keeping the rest of these here for now because they are being used
# but we should really delete them we only need one image
FROM base as replica-mon
WORKDIR /opt/optimism/packages/chain-mon
CMD ["start:replica-mon"]

FROM base as balance-mon
WORKDIR /opt/optimism/packages/chain-mon
CMD ["start:balance-mon"]
Expand All @@ -98,14 +94,24 @@ FROM base as drippie-mon
WORKDIR /opt/optimism/packages/chain-mon
CMD ["start:drippie-mon"]

FROM base as wd-mon
from base as fault-mon
WORKDIR /opt/optimism/packages/chain-mon
CMD ["start:wd-mon"]
CMD ["start:fault-mon"]

from base as multisig-mon
WORKDIR /opt/optimism/packages/multisig-mon
CMD ["start:multisig-mon"]

FROM base as replica-mon
WORKDIR /opt/optimism/packages/chain-mon
CMD ["start:replica-mon"]

FROM base as wallet-mon
WORKDIR /opt/optimism/packages/chain-mon
CMD ["start:wallet-mon"]

from base as fault-mon
FROM base as wd-mon
WORKDIR /opt/optimism/packages/chain-mon
CMD ["start:fault-mon"]
CMD ["start:wd-mon"]


12 changes: 12 additions & 0 deletions packages/chain-mon/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ BALANCE_MON__RPC=
# JSON array in the format [{ "address": <address>, "nickname": <nickname> }, ... ]
BALANCE_MON__ACCOUNTS=


###############################################################################
# ↓ multisig-mon ↓ #
###############################################################################

# RPC pointing to network to monitor safe nonces on
MULTISIG_MON__RPC=

# JSON array in the format [{ "address": <address>, "nickname": <nickname> }, ... ]
MULTISIG_MON__ACCOUNTS=


###############################################################################
# ↓ wallet-mon ↓ #
###############################################################################
Expand Down
12 changes: 8 additions & 4 deletions packages/chain-mon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,21 @@
],
"scripts": {
"dev:balance-mon": "tsx watch ./src/balance-mon/service.ts",
"dev:wallet-mon": "tsx watch ./src/wallet-mon/service.ts",
"dev:drippie-mon": "tsx watch ./src/drippie-mon/service.ts",
"dev:wd-mon": "tsx watch ./src/wd-mon/service.ts",
"dev:fault-mon": "tsx watch ./src/fault-mon/service.ts",
"dev:multisig-mon": "tsx watch ./src/multisig-mon/service.ts",
"dev:replica-mon": "tsx watch ./src/replica-mon/service.ts",
"dev:wallet-mon": "tsx watch ./src/wallet-mon/service.ts",
"dev:wd-mon": "tsx watch ./src/wd-mon/service.ts",

"start:balance-mon": "tsx ./src/balance-mon/service.ts",
"start:wallet-mon": "tsx ./src/wallet-mon/service.ts",
"start:drippie-mon": "tsx ./src/drippie-mon/service.ts",
"start:wd-mon": "tsx ./src/wd-mon/service.ts",
"start:fault-mon": "tsx ./src/fault-mon/service.ts",
"start:multisig-mon": "tsx ./src/multisig-mon/service.ts",
"start:replica-mon": "tsx ./src/replica-mon/service.ts",
"start:wallet-mon": "tsx ./src/wallet-mon/service.ts",
"start:wd-mon": "tsx ./src/wd-mon/service.ts",

"test": "hardhat test",
"test:coverage": "nyc hardhat test && nyc merge .nyc_output coverage.json",
"build": "tsc -p ./tsconfig.json",
Expand Down
42 changes: 1 addition & 41 deletions packages/chain-mon/src/balance-mon/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import {
validators,
} from '@eth-optimism/common-ts'
import { Provider } from '@ethersproject/abstract-provider'
import { BigNumber, ethers } from 'ethers'

import Safe from '../abi/IGnosisSafe.0.8.19.json'
import { version } from '../../package.json'

type BalanceMonOptions = {
Expand All @@ -18,12 +16,11 @@ type BalanceMonOptions = {

type BalanceMonMetrics = {
balances: Gauge
safeNonces: Gauge
unexpectedRpcErrors: Counter
}

type BalanceMonState = {
accounts: Array<{ address: string; nickname: string; safe: boolean }>
accounts: Array<{ address: string; nickname: string }>
}

export class BalanceMonService extends BaseServiceV2<
Expand Down Expand Up @@ -57,11 +54,6 @@ export class BalanceMonService extends BaseServiceV2<
desc: 'Balances of addresses',
labels: ['address', 'nickname'],
},
safeNonces: {
type: Gauge,
desc: 'Safe nonce',
labels: ['address', 'nickname'],
},
unexpectedRpcErrors: {
type: Counter,
desc: 'Number of unexpected RPC errors',
Expand Down Expand Up @@ -103,38 +95,6 @@ export class BalanceMonService extends BaseServiceV2<
name: 'getBalance',
})
}

// Get the safe nonce to report
if (account.safe) {
try {
const safeContract = new ethers.Contract(
account.address,
Safe.abi,
this.options.rpc
)
const safeNonce = BigNumber.from(await safeContract.nonce())
this.logger.info(`got nonce`, {
address: account.address,
nickname: account.nickname,
nonce: safeNonce.toString(),
})

this.metrics.safeNonces.set(
{ address: account.address, nickname: account.nickname },
parseInt(safeNonce.toString(), 10)
)
} catch (err) {
this.logger.info(`got unexpected RPC error`, {
section: 'safeNonce',
name: 'getSafeNonce',
err,
})
this.metrics.unexpectedRpcErrors.inc({
section: 'safeNonce',
name: 'getSafeNonce',
})
}
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/chain-mon/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './balance-mon/service'
export * from './drippie-mon/service'
export * from './fault-mon/index'
export * from './multisig-mon/service'
export * from './wd-mon/service'
export * from './wallet-mon/service'
export * from './fault-mon/index'
109 changes: 109 additions & 0 deletions packages/chain-mon/src/multisig-mon/service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {
BaseServiceV2,
StandardOptions,
Gauge,
Counter,
validators,
} from '@eth-optimism/common-ts'
import { Provider } from '@ethersproject/abstract-provider'
import { ethers } from 'ethers'

import Safe from '../abi/IGnosisSafe.0.8.19.json'
import { version } from '../../package.json'

type MultisigMonOptions = {
rpc: Provider
accounts: string
}

type MultisigMonMetrics = {
safeNonce: Gauge
unexpectedRpcErrors: Counter
}

type MultisigMonState = {
accounts: Array<{ address: string; nickname: string }>
}

export class MultisigMonService extends BaseServiceV2<
MultisigMonOptions,
MultisigMonMetrics,
MultisigMonState
> {
constructor(options?: Partial<MultisigMonOptions & StandardOptions>) {
super({
version,
name: 'multisig-mon',
loop: true,
options: {
loopIntervalMs: 60_000,
...options,
},
optionsSpec: {
rpc: {
validator: validators.provider,
desc: 'Provider for network to monitor balances on',
},
accounts: {
validator: validators.str,
desc: 'JSON array of [{ address, nickname, safe }] to monitor balances and nonces of',
public: true,
},
},
metricsSpec: {
safeNonce: {
type: Gauge,
desc: 'Safe nonce',
labels: ['address', 'nickname'],
},
unexpectedRpcErrors: {
type: Counter,
desc: 'Number of unexpected RPC errors',
labels: ['section', 'name'],
},
},
})
}

protected async init(): Promise<void> {
this.state.accounts = JSON.parse(this.options.accounts)
}

protected async main(): Promise<void> {
for (const account of this.state.accounts) {
try {
const safeContract = new ethers.Contract(
account.address,
Safe.abi,
this.options.rpc
)
const safeNonce = await safeContract.nonce()
this.logger.info(`got nonce`, {
address: account.address,
nickname: account.nickname,
nonce: safeNonce.toString(),
})

this.metrics.safeNonce.set(
{ address: account.address, nickname: account.nickname },
parseInt(safeNonce.toString(), 10)
)
} catch (err) {
this.logger.error(`got unexpected RPC error`, {
section: 'safeNonce',
name: 'getSafeNonce',
err,
})
this.metrics.unexpectedRpcErrors.inc({
section: 'safeNonce',
name: 'getSafeNonce',
})
}
}
}
}

if (require.main === module) {
const service = new MultisigMonService()
service.run()
}

0 comments on commit 3dea385

Please sign in to comment.