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: ...@@ -866,6 +866,12 @@ workflows:
dependencies: "(contracts|core-utils)" dependencies: "(contracts|core-utils)"
requires: requires:
- yarn-monorepo - yarn-monorepo
- js-lint-test:
name: balance-monitor-tests
coverage_flag: balance-monitor-tests
package_name: balance-monitor
requires:
- yarn-monorepo
- depcheck: - depcheck:
requires: requires:
- yarn-monorepo - yarn-monorepo
......
...@@ -482,6 +482,33 @@ jobs: ...@@ -482,6 +482,33 @@ jobs:
push: true push: true
tags: ethereumoptimism/deployer-bedrock:${{ needs.release.outputs.contracts-bedrock }},ethereumoptimism/deployer-bedrock:latest 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: integration_tests:
name: Publish Integration tests ${{ needs.release.outputs.integration-tests }} name: Publish Integration tests ${{ needs.release.outputs.integration-tests }}
needs: release needs: release
......
...@@ -47,6 +47,7 @@ COPY packages/message-relayer/package.json ./packages/message-relayer/package.js ...@@ -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/fault-detector/package.json ./packages/fault-detector/package.json
COPY packages/replica-healthcheck/package.json ./packages/replica-healthcheck/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/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 COPY integration-tests/package.json ./integration-tests/package.json
RUN yarn install --frozen-lockfile && yarn cache clean RUN yarn install --frozen-lockfile && yarn cache clean
...@@ -81,7 +82,6 @@ WORKDIR /opt/optimism/packages/data-transport-layer ...@@ -81,7 +82,6 @@ WORKDIR /opt/optimism/packages/data-transport-layer
COPY ./ops/scripts/dtl.sh . COPY ./ops/scripts/dtl.sh .
CMD ["node", "dist/src/services/run.js"] CMD ["node", "dist/src/services/run.js"]
FROM base as integration-tests FROM base as integration-tests
WORKDIR /opt/optimism/integration-tests WORKDIR /opt/optimism/integration-tests
COPY ./ops/scripts/integration-tests.sh ./ COPY ./ops/scripts/integration-tests.sh ./
...@@ -107,3 +107,7 @@ ENTRYPOINT ["npm", "run", "start"] ...@@ -107,3 +107,7 @@ ENTRYPOINT ["npm", "run", "start"]
FROM base as drippie-mon FROM base as drippie-mon
WORKDIR /opt/optimism/packages/drippie-mon WORKDIR /opt/optimism/packages/drippie-mon
ENTRYPOINT ["npm", "run", "start"] ENTRYPOINT ["npm", "run", "start"]
FROM base as drippie-mon
WORKDIR /opt/optimism/packages/balance-monitor
ENTRYPOINT ["yarn", "run", "start:prod"]
...@@ -31,7 +31,8 @@ ...@@ -31,7 +31,8 @@
"**/@openzeppelin/*", "**/@openzeppelin/*",
"@eth-optimism/contracts-periphery/ds-test", "@eth-optimism/contracts-periphery/ds-test",
"@eth-optimism/contracts-periphery/forge-std", "@eth-optimism/contracts-periphery/forge-std",
"@eth-optimism/contracts-periphery/@rari-capital/solmate" "@eth-optimism/contracts-periphery/@rari-capital/solmate",
"forta-agent"
] ]
}, },
"private": true, "private": true,
......
CHAINID= export L1_RPC_URL=
L1_RPC_URL=
SEQUENCER_ADDRESS= export SEQUENCER_ADDRESS=
SEQUENCER_WARNING_THRESHOLD= export SEQUENCER_WARNING_THRESHOLD=
SEQUENCER_DANGER_THRESHOLD= export SEQUENCER_DANGER_THRESHOLD=
PROPOSER_ADDRESS=
PROPOSER_WARNING_THRESHOLD=
PROPOSER_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", "version": "0.0.1",
"description": "Forta Agent that reports whether certain accounts have fallen below some balance", "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": { "scripts": {
"build": "echo 'todo'", "build": "tsc -p tsconfig.json",
"clean": "rimraf dist/ ./tsconfig.tsbuildinfo",
"start": "yarn run start:dev", "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: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", "start:prod": "forta-agent run --prod",
"tx": "yarn run build && forta-agent run --tx", "tx": "yarn run build && forta-agent run --tx",
"block": "yarn run build && forta-agent run --block", "block": "yarn run build && forta-agent run --block",
"range": "yarn run build && forta-agent run --range", "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": { "dependencies": {
"ethers": "^5.7.2", "ethers": "^5.7.2",
"forta-agent": "^0.1.1" "forta-agent": "^0.1.1"
}, },
"devDependencies": { "devDependencies": {
"@types/chai": "^4.2.18",
"@types/mocha": "^8.2.2",
"@types/nodemon": "^1.19.0", "@types/nodemon": "^1.19.0",
"chai": "^4.3.4",
"nodemon": "^2.0.8", "nodemon": "^2.0.8",
"ts-mocha": "^10.0.0",
"typescript": "^4.3.4" "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