Commit e8ea75be authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge pull request #4556 from ethereum-optimism/jm/monitoring/split-1

bmon: Add basic alerting logic
parents d8b6ca8d 459a4801
---
'minimum-balance-agent': patch
---
Added basic balance monitoring
......@@ -866,6 +866,12 @@ workflows:
dependencies: "(contracts|core-utils)"
requires:
- yarn-monorepo
- js-lint-test:
name: balance-monitor-tests
coverage_flag: balance-monitor-tests
package_name: balance-monitor
requires:
- yarn-monorepo
- depcheck:
requires:
- yarn-monorepo
......
......@@ -482,6 +482,33 @@ jobs:
push: true
tags: ethereumoptimism/deployer-bedrock:${{ needs.release.outputs.contracts-bedrock }},ethereumoptimism/deployer-bedrock:latest
balance-monitor:
name: Publish balance-monitor Version ${{ needs.release.outputs.balance-monitor }}
needs: release
if: needs.release.outputs.balance-monitor != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
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: balance-monitor
push: true
tags: ethereumoptimism/balance-monitor:${{ needs.release.outputs.balance-monitor }},ethereumoptimism/balance-monitor:latest
integration_tests:
name: Publish Integration tests ${{ needs.release.outputs.integration-tests }}
needs: release
......
......@@ -47,6 +47,7 @@ COPY packages/message-relayer/package.json ./packages/message-relayer/package.js
COPY packages/fault-detector/package.json ./packages/fault-detector/package.json
COPY packages/replica-healthcheck/package.json ./packages/replica-healthcheck/package.json
COPY packages/drippie-mon/package.json ./packages/drippie-mon/package.json
COPY packages/balance-monitor/package.json ./packages/balance-monitor/package.json
COPY integration-tests/package.json ./integration-tests/package.json
RUN yarn install --frozen-lockfile && yarn cache clean
......@@ -81,7 +82,6 @@ WORKDIR /opt/optimism/packages/data-transport-layer
COPY ./ops/scripts/dtl.sh .
CMD ["node", "dist/src/services/run.js"]
FROM base as integration-tests
WORKDIR /opt/optimism/integration-tests
COPY ./ops/scripts/integration-tests.sh ./
......@@ -107,3 +107,7 @@ ENTRYPOINT ["npm", "run", "start"]
FROM base as drippie-mon
WORKDIR /opt/optimism/packages/drippie-mon
ENTRYPOINT ["npm", "run", "start"]
FROM base as drippie-mon
WORKDIR /opt/optimism/packages/balance-monitor
ENTRYPOINT ["yarn", "run", "start:prod"]
......@@ -31,7 +31,8 @@
"**/@openzeppelin/*",
"@eth-optimism/contracts-periphery/ds-test",
"@eth-optimism/contracts-periphery/forge-std",
"@eth-optimism/contracts-periphery/@rari-capital/solmate"
"@eth-optimism/contracts-periphery/@rari-capital/solmate",
"forta-agent"
]
},
"private": true,
......
CHAINID=
L1_RPC_URL=
export L1_RPC_URL=
SEQUENCER_ADDRESS=
SEQUENCER_WARNING_THRESHOLD=
SEQUENCER_DANGER_THRESHOLD=
PROPOSER_ADDRESS=
PROPOSER_WARNING_THRESHOLD=
PROPOSER_DANGER_THRESHOLD=
export SEQUENCER_ADDRESS=
export SEQUENCER_WARNING_THRESHOLD=
export SEQUENCER_DANGER_THRESHOLD=
export PROPOSER_ADDRESS=
export PROPOSER_WARNING_THRESHOLD=
export PROPOSER_DANGER_THRESHOLD=
{
"name": "minimum-balance-agent",
"name": "@eth-optimism/minimum-balance-agent",
"version": "0.0.1",
"description": "Forta Agent that reports whether certain accounts have fallen below some balance",
"homepage": "https://github.com/ethereum-optimism/optimism/tree/develop/packages/balance-monitor#readme",
"license": "MIT",
"author": "Optimism PBC",
"repository": {
"type": "git",
"url": "https://github.com/ethereum-optimism/optimism.git"
},
"scripts": {
"build": "echo 'todo'",
"build": "tsc -p tsconfig.json",
"clean": "rimraf dist/ ./tsconfig.tsbuildinfo",
"start": "yarn run start:dev",
"start:dev": "nodemon --watch src --watch forta.config.json -e js,ts,json --exec 'yarn run build && forta-agent run'",
"start:prod": "forta-agent run --prod",
"tx": "yarn run build && forta-agent run --tx",
"block": "yarn run build && forta-agent run --block",
"range": "yarn run build && forta-agent run --range",
"test": "echo 'todo'"
"test": "./scripts/test.sh",
"test:coverage": "echo 'todo: configure test coverage' && yarn test",
"lint:check": "eslint . --max-warnings=0",
"lint:fix": "yarn lint:check --fix",
"lint": "yarn lint:fix && yarn lint:check"
},
"dependencies": {
"ethers": "^5.7.2",
"forta-agent": "^0.1.1"
},
"devDependencies": {
"@types/chai": "^4.2.18",
"@types/mocha": "^8.2.2",
"@types/nodemon": "^1.19.0",
"chai": "^4.3.4",
"nodemon": "^2.0.8",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.4"
}
}
#!/bin/bash
export SEQUENCER_ADDRESS=0xabba
export SEQUENCER_WARNING_THRESHOLD=1000
export SEQUENCER_DANGER_THRESHOLD=100
export PROPOSER_ADDRESS=0xacdc
export PROPOSER_WARNING_THRESHOLD=2000
export PROPOSER_DANGER_THRESHOLD=200
yarn ts-mocha src/*.spec.ts
import { HandleBlock, createBlockEvent } from 'forta-agent'
import { BigNumber, utils } from 'ethers'
import { expect } from 'chai'
import agent from './agent'
describe('minimum balance agent', async () => {
let handleBlock: HandleBlock
let mockEthersProvider
const blockEvent = createBlockEvent({
block: { hash: '0xa', number: 1 } as any,
})
// A function which returns a mock provider to give us values based on the case we want
// to test.
const mockEthersProviderByCase = (severity: string) => {
switch (severity) {
case 'safe':
return {
getBalance: async (addr: string): Promise<BigNumber> => {
if (addr === '0xabba') {
return utils.parseEther('1001')
}
if (addr === '0xacdc') {
return utils.parseEther('2001')
}
},
} as any
default:
break
}
}
before(() => {
handleBlock = agent.provideHandleBlock(mockEthersProvider)
})
describe('handleBlock', async () => {
it('returns empty findings if balance is above threshold', async () => {
mockEthersProvider = mockEthersProviderByCase('safe')
handleBlock = agent.provideHandleBlock(mockEthersProvider)
const findings = await handleBlock(blockEvent)
expect(findings).to.deep.equal([])
})
})
})
import { BlockEvent, Finding, HandleBlock } from 'forta-agent'
import { BigNumber, providers } from 'ethers'
type AccountAlert = {
name: string
address: string
thresholds: {
warning: BigNumber
danger: BigNumber
}
}
export const accounts: AccountAlert[] = [
{
name: 'Sequencer',
address: process.env.SEQUENCER_ADDRESS,
thresholds: {
warning: BigNumber.from(process.env.SEQUENCER_WARNING_THRESHOLD),
danger: BigNumber.from(process.env.SEQUENCER_DANGER_THRESHOLD),
},
},
{
name: 'Proposer',
address: process.env.PROPOSER_ADDRESS,
thresholds: {
warning: BigNumber.from(process.env.PROPOSER_WARNING_THRESHOLD),
danger: BigNumber.from(process.env.PROPOSER_DANGER_THRESHOLD),
},
},
]
const provideHandleBlock = (
provider: providers.JsonRpcProvider
): HandleBlock => {
return async (blockEvent: BlockEvent) => {
// report finding if specified account balance falls below threshold
const findings: Finding[] = []
// iterate over accounts with the index
for (const [idx, account] of accounts.entries()) {
const accountBalance = BigNumber.from(
(
await provider.getBalance(account.address, blockEvent.blockNumber)
).toString()
)
if (accountBalance.gte(account.thresholds.warning)) {
// todo: add to the findings array when balances are below the threshold
// return if this is the last account
if (idx === accounts.length - 1) {
return findings
}
}
}
return findings
}
}
const l1Provider = new providers.JsonRpcProvider(process.env.L1_RPC_URL)
export default {
provideHandleBlock,
handleBlock: provideHandleBlock(l1Provider),
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment