OpenZeppelin Monitor - OpenZeppelin 文档

OpenZeppelin Monitor是一个区块链监控服务,可以实时监控链上活动,并根据配置的条件触发通知。它支持多链,具有可配置的监控计划,灵活的触发条件和可扩展的架构,可以方便地添加新的链。通过它,用户可以监视特定的事件和交易,并通过Slack、Discord、电子邮件等多种渠道发送警报。

OpenZeppelin 监控器

此软件正处于 alpha 阶段。在生产环境中使用风险自负。

概述

在快速发展的区块链技术世界中,有效的监控对于确保安全性和性能至关重要。OpenZeppelin 监控器 是一种区块链监控服务,它会监视特定的链上活动,并根据可配置的条件触发通知。该服务提供多链支持以及可配置的监控计划、灵活的触发条件和用于添加新链的可扩展架构。

主要功能

  • 实时监控:实时监控区块链网络中的特定事件和交易

  • 智能过滤:使用灵活的表达式来精确定义要监控的内容

  • 多通知支持:通过 Slack、Discord、电子邮件、Telegram、Webhooks 或自定义脚本发送警报

  • 可配置的调度:使用 cron 表达式设置自定义监控计划

  • 数据持久性:存储监控数据并从检查点恢复

  • 可扩展的架构:易于添加对新区块链和通知类型的支持

支持的网络

  • EVM 兼容网络

  • Stellar

通知渠道

  • Slack - 将格式化的消息发送到 Slack 频道

  • Discord - 通过 webhooks 将警报发布到 Discord 频道

  • Email - 通过 SMTP 支持发送电子邮件通知

  • Telegram - 通过 bot API 向 Telegram 聊天发送消息

  • Webhooks - 将 HTTP 请求发送到自定义端点

  • Custom Scripts - 执行 Python、JavaScript 或 Bash 脚本

要立即开始,请参阅 快速入门

安装

先决条件

  • Rust 2021 edition 或更高版本

  • Docker(可选,用于容器化部署)

本地安装

  1. 克隆存储库:
git clone https://github.com/openzeppelin/openzeppelin-monitor
cd openzeppelin-monitor
  1. 构建应用程序:
cargo build --release
  1. 将二进制文件移动到项目根目录:
mv ./target/release/openzeppelin-monitor .
  1. 验证安装:
./openzeppelin-monitor --help
  1. 查看可用选项:
./openzeppelin-monitor --help

## 启用日志记录到文件
./openzeppelin-monitor --log-file

## 启用指标服务器
./openzeppelin-monitor --metrics

## 验证配置文件而不启动服务
./openzeppelin-monitor --check

Docker 安装

  1. 克隆存储库:
git clone https://github.com/openzeppelin/openzeppelin-monitor
cd openzeppelin-monitor
  1. 设置环境:
cp .env.example .env
## 使用你的配置编辑 .env 文件
  1. 使用 Docker Compose 启动:
cargo make docker-compose-up
指标配置

可以通过在 .env 文件中设置 METRICS_ENABLED=true 来启用指标服务器、Prometheus 和 Grafana。

你可以使用 Docker Compose 直接启动服务:

## 没有指标配置文件 (默认 METRICS_ENABLED=false)
docker compose up -d

## 启用指标
docker compose --profile metrics up -d

要在 UI 中查看 prometheus 指标,你可以在浏览器上使用 http://localhost:9090

要查看 grafana 仪表板,你可以在浏览器上使用 http://localhost:3000

默认情况下,预定义的指标会在 grafana 中填充到仪表板中。

配置指南

推荐的文件命名约定
  • 网络配置:<network_type>_<network_name>.json

  • 示例:ethereum_mainnet.jsonstellar_testnet.json

  • 应与文件内的 slug 属性匹配

  • 监控配置:<asset>_<action>_monitor.json

  • 示例:usdc_transfer_monitor.jsondai_liquidation_monitor.json

  • 由监控器使用其 name 属性引用

  • 触发器配置:<type>_<purpose>.json

  • 示例:slack_notifications.jsonemail_alerts.json

  • 单个触发器由其配置键引用

配置引用
  • 监控器、网络和触发器名称在所有配置文件中必须是唯一的

  • 监控器的 networks 数组必须包含来自网络配置文件的有效网络 slug

  • 监控器的 triggers 数组必须包含有效的触发器配置键

  • 示例有效引用:

// networks/ethereum_mainnet.json
{
    "slug": "ethereum_mainnet",
    ...
}

// triggers/slack_notifications.json
{
    "large_transfer_slack": {
      ...
    }
}

// monitors/usdc_transfer_monitor.json
{
    "networks": ["ethereum_mainnet"],
    "triggers": ["large_transfer_slack"],
    ...
}
确保所有引用的 slugs 和触发器键都存在于它们各自的配置文件中。如果监控器无法解析这些引用,则会启动失败。
安全协议指南

该监控器实现了跨不同组件的协议安全验证,并在检测到潜在的不安全配置时发出警告。虽然不安全协议不会被阻止,但我们强烈建议遵循这些安全指南:

网络协议

####### RPC URLs

  • 推荐使用 HTTPS:强烈建议对 RPC 端点使用 https://

  • 推荐使用 WSS:对于 WebSocket 连接,强烈建议使用 wss://(安全 WebSocket)

  • 警告:使用 http://ws:// 会触发安全警告,因为它们会以未加密的方式传输数据

通知协议

####### Webhook 通知

  • 推荐使用 HTTPS:URLs 应使用 HTTPS 协议

  • 推荐身份验证:包括以下任一项:

  • X-API-Key 标头

  • Authorization 标头

  • 可选密钥:可以包含用于 HMAC 身份验证的密钥

  • 当提供密钥时,监控器将:

  • 生成以毫秒为单位的时间戳

  • 创建 Payload 和时间戳的 HMAC-SHA256 签名

  • 将签名添加到 X-Signature 标头中

  • 将时间戳添加到 X-Timestamp 标头中

  • 签名计算为:HMAC-SHA256(secret, payload + timestamp)

  • 警告:非 HTTPS URLs 或缺少身份验证标头会触发安全警告

####### Slack 通知

  • 推荐使用 HTTPS:Webhook URLs 应以 https://hooks.slack.com/ 开头

  • 警告:非 HTTPS URLs 会触发安全警告

####### Discord 通知

  • 推荐使用 HTTPS:Webhook URLs 应以 https://discord.com/api/webhooks/ 开头

  • 警告:非 HTTPS URLs 会触发安全警告

####### Telegram 通知

  • 协议: 使用 application/json Payload 向 sendMessage 方法发送 POST 请求。

  • 端点: https://api.telegram.org/bot<token>/sendMessage

  • 安全:

  • 需要 HTTPS: API 端点使用 HTTPS。

  • 身份验证通过 URL 中的 Bot Token 进行处理。请确保此Token的安全。

  • 格式: 消息以 parse_mode 设置为 MarkdownV2 发送。消息标题和正文中的特殊字符会自动转义,以防止格式错误。

####### 电子邮件通知

  • 推荐使用安全端口: 以下端口被认为是安全的:

  • 465:SMTPS(基于 SSL 的 SMTP)

  • 587:带有 STARTTLS 的 SMTP

  • 993:IMAPS(基于 SSL 的 IMAP)

  • 警告:使用其他端口会触发安全警告

  • 有效格式:电子邮件地址必须遵循 RFC 5322 格式

####### 通知重试策略

以下通知协议支持重试策略:

  • Slack

  • Discord

  • Telegram

  • Webhook

默认重试策略是使用指数退避,其参数如下:

参数 默认值 描述
max_retries 3 在放弃之前进行的最大重试次数
base_for_backoff 2 指数退避计算的基本持续时间(以秒为单位)
initial_backoff 250 初始退避持续时间(以毫秒为单位)
max_backoff 10 最大退避持续时间(以秒为单位)
jitter Full 应用于退避持续时间的抖动策略,目前支持 FullNone

可以通过在触发器配置中的 retry_policy 字段中提供自定义 HttpRetryConfig 结构来覆盖这些参数。

脚本安全

####### 文件权限(Unix 系统)

  • 限制写入权限:脚本文件不应具有过于宽松的写入权限

  • 推荐权限:对脚本文件使用 644 ( rw-r—​r--)

  • 警告:模式为 022 或更宽松的文件会触发安全警告

示例设置推荐权限

chmod 644 ./config/filters/my_script.sh
密钥管理

该监控器实现了安全的密钥管理系统,支持多个密钥来源和自动内存归零。

密钥来源

监控器支持三种类型的密钥来源:

  • 纯文本:直接密钥值(包装在 SecretString 中,用于安全内存处理)

  • 环境变量:存储在环境变量中的密钥

  • Hashicorp Cloud Vault:存储在 Hashicorp Cloud Vault 中的密钥

安全特性
  • 自动归零:不再需要时,密钥会自动从内存中归零

  • 类型安全解析:安全处理密钥解析,并进行适当的错误处理

  • 配置支持:Serde 支持配置文件

配置

可以在 JSON 文件中使用以下格式配置密钥:

{
  "type": "Plain",
  "value": "my-secret-value"
}
{
  "type": "Environment",
  "value": "MY_SECRET_ENV_VAR"
}
{
  "type": "HashicorpCloudVault",
  "value": "my-secret-name"
}
Hashicorp Cloud Vault 集成

要使用 Hashicorp Cloud Vault,请配置以下环境变量:

环境变量 描述
HCP_CLIENT_ID Hashicorp Cloud Vault 客户端 ID
HCP_CLIENT_SECRET Hashicorp Cloud Vault 客户端密钥
HCP_ORG_ID Hashicorp Cloud Vault 组织 ID
HCP_PROJECT_ID Hashicorp Cloud Vault 项目 ID
HCP_APP_NAME Hashicorp Cloud Vault 应用程序名称
最佳实践
  • 对生产密钥使用环境变量或 Vault

  • 避免在配置文件中存储纯文本密钥

  • 对 Vault 密钥使用适当的访问控制

  • 监视 Vault 访问模式以查找可疑活动

基本配置
  • 设置环境变量:

复制示例环境变量文件并根据你的需要更新值

cp .env.example .env

此表列出了环境变量及其默认值。

环境变量 默认值 接受的值 描述
RUST_LOG info info, debug, warn, error, trace 日志级别。
LOG_MODE stdout stdout, file 将日志写入控制台或文件。
LOG_DATA_DIR logs/ <any file path> 在主机上写入日志文件的目录。
MONITOR_DATA_DIR null <any file path> 在容器重启之间保留监控数据。
LOG_MAX_SIZE 1073741824 <size in bytes or human-readable format (e.g., "1GB", "500MB")> 日志需要回滚的大小。接受原始字节(例如,“1073741824”)或人类可读的格式(例如,“1GB”、“500MB”)。
METRICS_ENABLED false true, false 启用指标服务器以供外部工具抓取指标。
METRICS_PORT 8081 <any tcp port (preferably choose non-privileged ports i.e. (1024-65535))> 用于指标服务器的端口。
HCP_CLIENT_ID - <string> 用于密钥管理的 Hashicorp Cloud Vault 客户端 ID。
HCP_CLIENT_SECRET - <string> 用于密钥管理的 Hashicorp Cloud Vault 客户端密钥。
HCP_ORG_ID - <string> 用于密钥管理的 Hashicorp Cloud Vault 组织 ID。
HCP_PROJECT_ID - <string> 用于密钥管理的 Hashicorp Cloud Vault 项目 ID。
HCP_APP_NAME - <string> 用于密钥管理的 Hashicorp Cloud Vault 应用程序名称。
  • 复制并配置一些示例文件:
## EVM 配置
cp examples/config/monitors/evm_transfer_usdc.json config/monitors/evm_transfer_usdc.json
cp examples/config/networks/ethereum_mainnet.json config/networks/ethereum_mainnet.json

## Stellar 配置
cp examples/config/monitors/stellar_swap_dex.json config/monitors/stellar_swap_dex.json
cp examples/config/networks/stellar_mainnet.json config/networks/stellar_mainnet.json

## 通知配置
cp examples/config/triggers/slack_notifications.json config/triggers/slack_notifications.json
cp examples/config/triggers/email_notifications.json config/triggers/email_notifications.json

## 筛选器配置
cp examples/config/filters/evm_filter_block_number.sh config/filters/evm_filter_block_number.sh
cp examples/config/filters/stellar_filter_block_number.sh config/filters/stellar_filter_block_number.sh

命令行选项

该监控器支持多个命令行选项,用于配置和控制:

选项 默认值 描述
--log-file false 将日志写入文件而不是 stdout
--log-level info 设置日志级别(跟踪、调试、信息、警告、错误)
--log-path logs/ 存储日志文件的路径
--log-max-size 1GB 回滚之前的最大日志文件大小
--metrics-address 127.0.0.1:8081 启动指标服务器的地址
--metrics false 启用指标服务器
--monitor-path - 要执行的监控器的路径(用于测试)
--network - 要执行监控器的网络(用于测试)
--block - 要执行监控器的区块号(用于测试)
--check false 验证配置文件而不启动服务

数据存储配置

监控器默认使用基于文件的存储。

文件存储

当在网络配置中启用 store_blocks 时,监控器会存储:

  • 已处理的区块:./data/<network_slug>_blocks_<timestamp>.json

  • 遗漏的区块:./data/<network_slug>_missed_blocks.txt(用于存储遗漏的区块)

missed_blocks.txt 文件的内容可能有助于根据网络的区块时间和监控器的 cron 计划确定正确的 max_past_blocks 值。

此外,监控器将始终存储:

  • 上次处理的区块:./data/<network_slug>_last_block.txt(启用从上次检查点恢复)

配置文件

网络配置

网络配置定义了特定区块链网络的连接详细信息和操作参数,支持基于 EVM 和 Stellar 的链。

示例网络配置

{
  "network_type": "Stellar",
  "slug": "stellar_mainnet",
  "name": "Stellar Mainnet",
  "rpc_urls": [\
    {\
      "type_": "rpc",\
      "url": {\
        "type": "plain",\
        "value": "https://soroban.stellar.org"\
      },\
      "weight": 100\
    }\
  ],
  "network_passphrase": "Public Global Stellar Network ; September 2015",
  "block_time_ms": 5000,
  "confirmation_blocks": 2,
  "cron_schedule": "0 */1 * * * *",
  "max_past_blocks": 20,
  "store_blocks": true
}
可用字段
字段 类型 描述
network_type String 区块链类型("EVM""Stellar"
slug String 必需 - 网络的加粗唯一标识符
name String 必需 - 网络的加粗唯一人类可读名称
rpc_urls Array[Object] 具有用于负载平衡的权重的 RPC 端点列表
chain_id Number 网络链 ID(仅限 EVM
network_passphrase String 网络标识符(仅限 Stellar
block_time_ms Number 平均区块时间(以毫秒为单位)
confirmation_blocks Number 等待确认的区块数
cron_schedule String cron 格式的监控器调度
max_past_blocks Number 要处理的最大过去区块数
store_blocks Boolean 是否存储已处理的区块(默认输出到 ./data/ 目录)
重要注意事项
  • 我们强烈建议使用私有 RPC 提供程序以提高可靠性。

触发器配置

触发器定义了在满足监控条件时要执行的操作。触发器可以发送通知、发出 HTTP 请求或执行脚本。

示例触发器配置

{
  "evm_large_transfer_usdc_slack": {
    "name": "Large Transfer Slack Notification",
    "trigger_type": "slack",
    "config": {
      "slack_url": {
        "type": "plain",
        "value": "https://hooks.slack.com/services/A/B/C"
      },
      "message": {
        "title": "large_transfer_slack triggered",
        "body": "Large transfer of ${events.0.args.value} USDC from ${events.0.args.from} to ${events.0.args.to} | https://etherscan.io/tx/${transaction.hash}#eventlog"
      }
    }
  },
  "stellar_large_transfer_usdc_slack": {
    "name": "Large Transfer Slack Notification",
    "trigger_type": "slack",
    "config": {
      "slack_url": {
        "type": "environment",
        "value": "SLACK_WEBHOOK_URL"
      },
      "message": {
        "title": "large_transfer_usdc_slack triggered",
        "body": "${monitor.name} triggered because of a large transfer of ${functions.0.args.amount} USDC to ${functions.0.args.to} | https://stellar.expert/explorer/testnet/tx/${transaction.hash}"
      }
    }
  }
}
触发器类型
Slack 通知
{
  "slack_url": {
    "type": "HashicorpCloudVault",
    "value": "slack-webhook-url"
  },
  "message": {
    "title": "Alert Title",
    "body": "Alert message for ${transaction.hash}"
  }
}
Slack 通知字段
字段 类型 描述
name String 必需 - 通知的加粗唯一人类可读名称
trigger_type String 对于 Slack 通知,必须为 "slack"
config.slack_url.type String 密钥类型("Plain""Environment""HashicorpCloudVault"
config.slack_url.value String 密钥值(URL、环境变量名称或 Vault 密钥名称)
config.message.title String 出现在 Slack 消息中的标题
config.message.body String 带有变量替换的消息模板
电子邮件通知
{
  "host": "smtp.gmail.com",
  "port": 465,
  "username": {
    "type": "plain",
    "value": "sender@example.com"
  },
  "password": {
    "type": "environment",
    "value": "SMTP_PASSWORD"
  },
  "message": {
    "title": "Alert Subject",
    "body": "Alert message for ${transaction.hash}",
  },
  "sender": "sender@example.com",
  "recipients": ["recipient@example.com"]
}
电子邮件通知字段
字段 类型 描述
name String 必需 - 通知的加粗唯一人类可读名称
trigger_type String 对于电子邮件通知,必须为 "email"
config.host String SMTP 服务器主机名
config.port Number SMTP 端口(默认为 465
config.username.type String 密钥类型("Plain""Environment""HashicorpCloudVault"
config.username.value String 密钥值(用户名、环境变量名称或 Vault 密钥名称)
config.password.type String 密钥类型("Plain""Environment""HashicorpCloudVault"
config.password.value String 密钥值(密码、环境变量名称或 Vault 密钥名称)
config.message.title String 电子邮件主题行
config.message.body String 带有变量替换的电子邮件正文模板
config.sender String 发件人电子邮件地址
config.recipients Array[String] 收件人电子邮件地址列表
Webhook 通知
{
  "url": {
    "type": "HashicorpCloudVault",
    "value": "webhook-url"
  },
  "method": "POST",
  "secret": {
    "type": "environment",
    "value": "WEBHOOK_SECRET"
  },
  "headers": {
    "Content-Type": "application/json"
  },
  "message": {
    "title": "Alert Title",
    "body": "Alert message for ${transaction.hash}"
  }
}
Webhook 通知字段
字段 类型 描述
name String 必需 - 通知的加粗唯一人类可读名称
trigger_type String 对于 webhook 通知,必须为 "webhook"
config.url.type String 密钥类型("Plain""Environment""HashicorpCloudVault"
config.url.value String 密钥值(URL、环境变量名称或 Vault 密钥名称)
config.method String HTTP 方法(POST、GET 等),默认为 POST
config.secret.type String 密钥类型("Plain""Environment""HashicorpCloudVault"
config.secret.value String 密钥值(HMAC 密钥、环境变量名称或 Vault 密钥名称)
config.headers Object 要包含在 webhook 请求中的标头
config.message.title String 出现在 webhook 消息中的标题
config.message.body String 带有变量替换的消息模板
Discord 通知
{
  "discord_url": {
    "type": "plain",
    "value": "https://discord.com/api/webhooks/123-456-789"
  },
  "message": {
    "title": "Alert Title",
    "body": "Alert message for ${transaction.hash}"
  }
}
Discord 通知字段
字段 类型 描述
name String 必需 - 通知的加粗唯一人类可读名称
trigger_type String 对于 Discord 通知,必须为 "discord"
config.discord_url.type String 密钥类型("Plain""Environment""HashicorpCloudVault"
config.discord_url.value String 密钥值(URL、环境变量名称或 Vault 密钥名称)
config.message.title String 出现在 Discord 消息中的标题
config.message.body String 带有变量替换的消息模板
Telegram 通知
{
  "token": {
    "type": "HashicorpCloudVault",
    "value": "telegram-bot-token"
  },
  "chat_id": "9876543210",
  "message": {
    "title": "Alert Title",
    "body": "Alert message for ${transaction.hash}"
  }
}
Telegram 通知字段
字段 类型 描述
name String 必需 - 通知的加粗唯一人类可读名称
trigger_type String 对于 Telegram 通知,必须为 "telegram"
config.token.type String 密钥类型("Plain""Environment""HashicorpCloudVault"
config.token.value String 密钥值(bot Token、环境变量名称或 Vault 密钥名称)
config.chat_id String Telegram 聊天 ID
config.disable_web_preview Boolean 是否禁用 Telegram 消息中的 Web 预览(默认为 false)
config.message.title String 出现在 Telegram 消息中的标题
config.message.body String 带有变量替换的消息模板
自定义脚本通知
{
  "language": "Bash",
  "script_path": "./config/triggers/scripts/custom_notification.sh",
  "arguments": ["--verbose"],
  "timeout_ms": 1000
}
脚本通知字段
字段 类型 描述
name String 必需 - 通知的加粗唯一人类可读名称
trigger_type String 对于自定义脚本通知,必须为 "script"
language String 脚本的语言
script_path String 脚本的路径
arguments Array[String] 脚本的参数(可选)。
timeout_ms Number 脚本超时对于避免执行期间的无限循环非常重要。如果脚本花费的时间超过超时时间,它将被终止。

有关自定义脚本的更多信息,请参阅自定义脚本部分

安全风险:仅运行你信任并完全理解的脚本。恶意脚本可能会损害你的系统或暴露敏感数据。在执行之前,请务必查看脚本内容并验证其来源。
可用模板变量

该监控器使用结构化的 JSON 格式,其中嵌套对象用于模板变量。数据被展平为 dot 表示法以用于模板。

常用变量
变量 描述
monitor.name 触发的监控器的名称
transaction.hash 事务的哈希
functions.[index].signature 函数签名
events.[index].signature 事件签名
网络特定变量

####### EVM 变量

变量 描述
transaction.from 发件人地址
transaction.to 收件人地址
transaction.value 交易金额
events.[index].args.[param] 按名称的事件参数
functions.[index].args.[param] 按名称的函数参数

####### Stellar 变量

变量 描述
events.[index].args.[position] 按位置的事件参数
functions.[index].args.[param] 按名称的函数参数
事务相关变量(transaction.fromtransaction.totransaction.value)不适用于 Stellar 网络。
消息格式

Slack、Discord、Telegram、电子邮件和 Webhook 支持在其消息正文中使用 Markdown 格式。你可以使用 Markdown 语法来增强你的通知。

具有 Markdown 的示例电子邮件通知
{
  "email_notification": {
    "name": "Formatted Alert",
    "trigger_type": "email",
    "config": {
      "host": "smtp.example.com",
      "port": 465,
      "username": {"type": "plain", "value": "alerts@example.com"},
      "password": {"type": "plain", "value": "password"},
      "message": {
        "title": "**High Value Transfer Alert**",
        "body": "### Transaction Details\n\n* **Amount:** ${events.0.args.value} USDC\n* **From:** `${events.0.args.from}`\n* **To:** `${events.0.args.to}`\n\n> Transaction Hash: ${transaction.hash}\n\n[View on Explorer](https://etherscan.io/tx/${transaction.hash})"
      },
      "sender": "alerts@example.com",
      "recipients": ["recipient@example.com"]
    }
  }
}
具有 Markdown 的示例 Slack 通知
{
  "slack_notification": {
    "name": "Formatted Alert",
    "trigger_type": "slack",
    "config": {
      "slack_url": {"type": "plain", "value": "https://hooks.slack.com/services/XXX/YYY/ZZZ"},
      "message": {
        "title": "*🚨 High Value Transfer Alert*",
        "body": "*Transaction Details*\n\n• *Amount:* `${events.0.args.value}` USDC\n• *From:* `${events.0.args.from}`\n• *To:* `${events.0.args.to}`\n\n>Transaction Hash: `${transaction.hash}`\n\n<https://etherscan.io/tx/${transaction.hash}|View on Explorer>"
      }
    }
  }
}
具有 Markdown 的示例 Discord 通知
{
  "discord_notification": {
    "name": "Formatted Alert",
    "trigger_type": "discord",
    "config": {
      "discord_url": {"type": "plain", "value": "https://discord.com/api/webhooks/XXX/YYY"},
      "message": {
        "title": "**🚨 High Value Transfer Alert**",
        "body": "# Transaction Details\n\n* **Amount:** `${events.0.args.value}` USDC\n* **From:** `${events.0.args.from}`\n* **To:** `${events.0.args.to}`\n\n>>> Transaction Hash: `${transaction.hash}`\n\n**[View on Explorer](https://etherscan.io/tx/${transaction.hash})"
      }
    }
  }
}
具有 Markdown 的示例 Telegram 通知
{
  "telegram_notification": {
    "name": "Formatted Alert",
    "trigger_type": "telegram",
    "config": {
      "token": {"type": "plain", "value": "1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ"},
      "chat_id": "9876543210",
      "message": {
        "title": "*🚨 High Value Transfer Alert*",
        "body": "*Transaction Details*\n\n• *Amount:* `${events.0.args.value}` USDC\n• *From:* `${events.0.args.from}`\n• *To:* `${events.0.args.to}`\n\n`Transaction Hash: ${transaction.hash}`\n\n[View on Explorer](https://etherscan.io/tx/${transaction.hash})"
      }
    }
匹配交易属性。可用字段和表达式语法取决于网络类型 (EVM/Stellar)

```json hljs
{
  "transactions": [
    {
      "status": "Success", // 仅匹配成功的交易
      "expression": "value > 1500000000000000000" // 匹配 value 大于 1.5 ETH 的交易
    }
  ]
}
可用交易字段 (EVM)
字段 类型 描述
value uint256 交易 value,单位为 wei
from address 发送者地址(不区分大小写比较)
to address 接收者地址(不区分大小写比较)
hash string 交易哈希
gas_price uint256 Gas 价格,单位为 wei (传统交易)
max_fee_per_gas uint256 EIP-1559 最大 gas 费用
max_priority_fee_per_gas uint256 EIP-1559 优先级费用
gas_limit uint256 交易的 Gas 限制
nonce uint256 发送者 nonce
input string 十六进制编码的输入数据 (例如,"0xa9059cbb…​")
gas_used uint256 实际使用的 gas (来自回执)
transaction_index uint64 在区块中的位置
可用交易字段 (Stellar)
字段 类型 描述
hash string 交易哈希
ledger i64 包含该交易的账本序列号
value i64 第一个相关操作相关联的 value(例如,支付金额)。如果未找到相关操作或 value,则默认为 0。
from address 第一个相关操作的源账户地址(例如,付款发送者)。不区分大小写比较。
to address 第一个相关操作的目标账户地址(例如,付款接收者或调用的合约)。不区分大小写比较。
匹配规则
  • 如果未指定任何条件,则匹配所有交易

  • 对于多种条件类型:

  • 首先检查交易条件

  • 然后,函数或事件条件必须匹配

  • 如果同时指定了交易和(函数或事件),则必须同时匹配

表达式

表达式允许对函数参数、事件参数和交易字段进行条件检查。

支持的参数/字段类型和基本操作:

类型 描述 示例运算符 注释
Numeric (uint/int variants) 整数值(例如,42-100)或小数值(例如,3.14-0.5)。 >, >=, <, , ==, != 如果存在小数点,数字必须在小数点前后都有数字(例如,.55. 不是有效的独立数字)。
Address 区块链地址。 ==, != 比较(例如,from == '0xABC…​')通常在地址值本身的十六进制字符方面不区分大小写。
String 文本值。可以是单引号(例如,'hello'),或者在比较的右侧,不带引号(例如,active)。 ==, !=, starts_with, ends_with, contains 带引号的字符串支持 \' 来转义单引号,\\ 来转义反斜杠。所有字符串比较操作(例如,name == 'Alice'description contains 'error')在评估期间都以不区分大小写的方式执行。有关更多示例和详细信息,请参阅专门的“字符串操作”部分。
Boolean True 或 false 值。 ==, != 表示为 truefalse。这些关键字的解析不区分大小写(例如,TRUEFalse 在表达式中也有效)。
Hex String Literal 0x0X 开头,后跟十六进制字符(0-9,a-f,A-F)的字符串字面量。 ==, !=, starts_with, ends_with, contains 被视为用于比较的字符串(例如,input_data starts_with '0xa9059cbb')。对于 0x 之后的十六进制字符,比较区分大小写。
Array (EVM/Stellar) 项目的有序列表。对于 Stellar,通常是配置中的 JSON 字符串(例如,'["a", {"id":1}]')。对于 EVM,通常从 ABI 参数解码。 contains, ==, !=, [index] 详细操作,包括索引访问和 contains 的行为,因网络而异。请参阅下面的“复杂类型的操作”。
Object/Map (Stellar) 键值对,通常表示为配置中的 JSON 字符串(例如,'{"key": "value", "id": 123}')。 .key_access, ==, != 支持用于字段访问的点表示法(例如,data.id)。有关详细信息,请参阅“复杂类型的操作”。
Vec (Stellar) 有序列表,其中参数的 value 可以是 CSV 字符串(例如,"foo,bar")或 JSON 数组字符串(例如,'["foo","bar"]')。 contains, ==, != contains== 的行为根据 value 是 CSV 还是 JSON 数组字符串而有所不同。有关详细信息,请参阅“复杂类型的操作”。

逻辑运算符:

  • AND - 所有条件都必须为真

  • OR - 至少一个条件必须为真

  • () - 用于分组的括号

  • AND 的优先级高于 OR(即,如果未用括号分组,则 AND 运算在 OR 运算之前计算)

变量命名和访问(条件的左侧):

条件的左侧 (LHS) 指定要评估其 value 的数据字段或参数。

基本名称:

  • 这些是参数或字段的直接名称,例如 amountfromstatus 或事件参数索引,如 01(在 Stellar 事件中很常见)。

  • 基本名称可以包含字母数字字符(a-z、A-Z、0-9)和下划线(_)。

  • 它们可以以字母、下划线或数字开头。以数字开头主要与数字索引的参数相关(例如,Stellar 事件参数)。

  • 重要提示: 变量名称在评估期间区分大小写。表达式中使用的名称必须与源数据(例如,来自 ABI 或区块链数据结构)中字段名称的大小写完全匹配。例如,如果在数据中将字段命名为 TotalValue,则使用 totalvalue 的表达式将找不到它。

  • 变量名称不能是关键字(例如,trueANDORcontains)。关键字本身的解析不区分大小写。

路径访问器(对于复杂类型):

如果基本参数是复杂类型,如对象、映射或数组,则可以使用访问器访问其内部数据:

键访问: 使用点表示法 (.) 访问对象或映射的属性。

  • 示例:transaction.valueuser.namedata.0(如果 0 是作为字符串的有效键名)。

  • 键通常由字母数字字符和下划线组成。它们通常以字母或下划线开头,但也支持纯数字键(例如,.0.123)用于键可能是表示数字的字符串的类映射结构。

  • 键不能包含连字符 (-)。

索引访问: 使用括号表示法 ([]) 按其从零开始的整数索引访问数组的元素。

  • 示例:my_array[0]log_entries[3]

  • 索引必须是非负整数。

组合访问: 可以组合键和索引访问器来导航嵌套结构。

  • 示例:event.data_array[0].property(访问 data_array 中第一个对象的 property 字段,该字段是 event 的一部分)。

  • 示例:map.numeric_key_as_string_0[1].name(访问存储在 map 中键 0 下的数组的第二个元素的 name 属性)。

字符串操作:

有几个运算符可用于匹配模式和比较字符串值。这些对于 EVM 交易 input 数据,使用 kind: "string" 定义的 Stellar 参数或包含文本的任何其他字段特别有用。

  • string_param starts_with 'prefix':: 检查字符串参数的 value 是否以指定的 prefix 开头。 示例:transaction.input starts_with '0xa9059cbb'(检查 ERC20 转账函数选择器)。

  • string_param ends_with 'suffix':: 检查字符串参数的 value 是否以指定的 suffix 结尾。 示例:file_name ends_with '.txt'

  • string_param contains 'substring':: 检查字符串参数的 value 是否在其中任何位置包含指定的 substring。 示例:message contains 'error'

  • string_param == 'exact_string':: 检查字符串参数的 value 是否与 exact_string 完全相等。

  • string_param != 'different_string':: 检查字符串参数的 value 是否与 different_string 不相等。

字符串操作的重要说明:

  • 运算符关键字: 运算符关键字本身(starts_withends_withcontainsANDORtruefalse、比较符号如 ==>)的解析不区分大小写。例如,CONTAINScontains 的处理方式相同,TRUEtrue 相同。

  • 字符串比较的不区分大小写评估: 在将字符串数据(例如,来自事件参数、交易字段或函数参数)与表达式中的字面量字符串值进行比较时,所有标准字符串操作在评估期间都执行不区分大小写的比较。

  • 相等 (==) 和不等 (!=)

  • 模式匹配 (starts_withends_withcontains)

  • 变量名称区分大小写: 重要的是将此与变量名称(条件的左侧,例如,status)区分开来。变量名称区分大小写,并且必须与源数据(ABI 等)中的字段名称完全匹配。

空白处理: 通常允许在运算符、括号和关键字周围使用灵活的空白以提高可读性。但是,带引号的字符串字面量中的空格很重要并且会被保留。

复杂类型的操作

除了简单的原始类型之外,表达式还可以与更复杂的数据结构(如数组、对象和向量)交互。

EVM 特定

数组操作 (kind: "array")

当 EVM 参数是一个数组(通常在内部表示或配置为 kind: "array",如果手动配置,则其 value 是 JSON 字符串表示形式)时,支持以下操作:

  • array_param contains 'value' 检查字符串 'value' 是否存在于数组中。

  • array_param == '["raw_json_array_string"]' 将数组的整个 JSON 字符串表示形式与提供的字符串进行字符串比较

  • array_param != '["raw_json_array_string"]' 上述操作的否定

  • array_param[0] 索引访问

Stellar 特定

对象 (kind: "object") / 映射 (kind: "Map") 操作

  • object_param.key == 'value' 检查对象或映射是否具有名为 key 的键,其 value 为 'value'

  • object_param.nested_key.another_key > 100 检查嵌套键 nested_key 中的 another_key 的 value 是否大于 100。

  • object_param == '{"raw_json_object_string"}' 检查对象或映射是否与提供的 JSON 字符串表示形式匹配。

  • object_param != '{"raw_json_object_string"}' 上述操作的否定

数组操作 (kind: "array")

  • array_param[index] 访问数组中指定 index 处的元素。

  • array_param[0] == 'value' 检查数组中的第一个元素是否等于 'value'

  • array_param[1].property == 'value' 检查数组中的第二个元素是否具有名为 property 的属性,其 value 为 'value'

  • array_param contains 'value' 检查数组是否包含字符串 'value'

  • array_param == '["raw_json_array_string"]' 检查数组是否与提供的 JSON 字符串表示形式匹配。

  • array_param != '["raw_json_array_string"]' 上述操作的否定

向量操作 (kind: "vec") 当 Stellar 参数具有 kind: "vec" 时,其 value 可以是 CSV 字符串或 JSON 数组字符串。

  • vec_param contains 'item' 检查向量是否包含字符串 'item'。这适用于 CSV 和 JSON 数组字符串。

  • vec_param == 'raw_string_value' 检查向量是否与提供的原始字符串 value 匹配。这适用于 CSV 和 JSON 数组字符串。

  • vec_param != 'raw_string_value' 上述操作的否定

事件参数访问 (Stellar)

Stellar 事件参数通常通过其数字索引作为基本变量名称来访问(例如,012)。如果索引事件参数本身是复杂类型(例如数组或映射,表示为 JSON 字符串),则可以应用相应的访问方法:

  • 如果事件参数 0 (kind: "Map") 是 '{"id": 123, "name": "Test"}':

  • 0.id == 123

  • 0.name contains 'est'(不区分大小写)

  • 如果事件参数 1 (kind: "array") 是 '["alpha", {"val": "beta"}]':

  • 1[0] == 'ALPHA'(不区分大小写)

  • 1[1].val == 'Beta'(不区分大小写)

  • 1 contains 'beta'(不区分大小写的深度搜索)

EVM 示例

这些示例假定常见的 EVM 事件参数或交易字段。

基本比较

// Numeric
"transaction.value > 1000000000000000000" // Value 大于 1 ETH
"event.amount <= 500"
"block.number == 12345678"

// String (case-insensitive evaluation for '==' and 'contains')
"transaction.to == '0xdeadbeef...'" // 地址检查(地址 value 比较本身不区分大小写)
"event.token_name == 'mytoken'"
"transaction.input contains 'a9059cbb'" // 检查 ERC20 转账选择器

// Boolean
"receipt.status == true" // 或者如果可以直接评估布尔字段,则简单地 "receipt.status"
"event.isFinalized == false"

逻辑运算符

"transaction.value > 1000 AND event.type == 'Deposit'"
"(receipt.status == true OR event.fallback_triggered == true) AND user.is_whitelisted == false"

字符串操作

"transaction.input starts_with '0xa9059cbb'" // 操作不区分大小写
"event.message ends_with 'failed'"
"event.details contains 'critical alert'"

数组操作

假定 event.ids[10, 20, 30]event.participants[{"user": "Alice", "role": "admin"}, {"user": "Bob", "role": "editor"}]

"event.ids[0] == 10"
"event.ids contains '20'" // 检查字符串 '20'(不区分大小写)

"event.participants contains 'Alice'"  // True(深度搜索,不区分大小写)
"event.participants contains 'editor'" // True(深度搜索,不区分大小写)
"event.participants == '[{\"user\": \"Alice\", \"role\": \"admin\"}, {\"user\": \"Bob\", \"role\": \"editor\"}]'" // 原始 JSON 匹配(结构和键区分大小写)
Stellar 示例

基本比较

// Numeric
"event.params.amount > 10000000" // 访问对象 'params' 中的 'amount' 字段
"ledger.sequence >= 123456"

// String (case-insensitive evaluation for '==' and 'contains')
"event.params.recipient == 'GBD22...'" // 地址检查
"event.type == 'payment_processed'"

// Boolean
"transaction.successful == true"
"event.data.is_verified == false"

逻辑运算符

"event.data.value > 500 AND event.source_account == 'GCA7Z...'"
"(event.type == 'TRANSFER' OR event.type == 'PAYMENT') AND event.params.asset_code == 'XLM'"

字符串操作

"event.contract_id starts_with 'CA23...'"
"event.memo ends_with '_TEST'"
"event.params.description contains 'urgent'"

对象 (kind: "object") / 映射 (kind: "Map") 操作

假定 event.details (kind: "Map") 是 '{"id": 123, "user": {"name": "CHarlie", "status": "Active"}, "tags": ["new"]}'

"event.details.id == 123"
"event.details.user.name == 'charlie'"  // 不区分大小写的字符串比较
"event.details.user.status contains 'act'" // 不区分大小写的 contains
"event.details.tags == '[\"new\"]'" // 'tags' 字段的原始 JSON 字符串匹配

数组操作 (kind: "array")

假定 event.items (kind: "array") 是 '[{"sku": "A1", "qty": 10}, {"sku": "B2", "qty": 5, "notes":"Rush order"}]'

"event.items[0].sku == 'a1'"
"event.items[1].qty < 10"
"event.items contains 'A1'"       // 深度搜索(不区分大小写)
"event.items contains 'rush order'" // 深度搜索(不区分大小写)

向量操作 (kind: "vec")

假定 csv_data (kind: "vec") 是 "ALPHA,Bravo,Charlie"json_array_data (kind: "vec") 是 '["Delta", {"id": "ECHO"}, "Foxtrot"]'

"csv_data contains 'bravo'"   // 不区分大小写的 CSV 元素匹配
"csv_data == 'ALPHA,Bravo,Charlie'" // 原始字符串匹配

"json_array_data contains 'delta'" // 不区分大小写的深度搜索(如数组)
"json_array_data contains 'ECHO'"  // 不区分大小写的深度搜索(如数组)

事件参数访问(数字索引)

假定事件参数 012345 (u64),1 (kind: "array") 是 '["Val1", "VAL2"]'2 (kind: "Map") 是 '{"keyA": "dataX", "keyB": 789}'

"0 > 10000"
"1[0] == 'val1'"
"1 contains 'val2'"
"2.keyA == 'DATAX'"
"2.keyB < 1000"
通过 SEP-48 支持,Stellar 函数现在可以通过名称(例如,amount > 1000)而不是位置(例如,2 > 1000)引用参数。事件仍然使用索引参数,直到为事件添加 SEP-48 支持为止。<br>你可以通过 Stellar 合约浏览器工具找到合约规范。例如:<br>Stellar DEX 合约接口
触发条件(自定义过滤器)

自定义过滤器允许你为处理监视器匹配项创建复杂的过滤逻辑。这些过滤器充当额外的验证层,确定匹配项是否应触发触发器的执行。

有关自定义脚本的更多信息,请参阅自定义脚本部分

安全风险:仅运行你信任并完全理解的脚本。恶意脚本可能会损害你的系统或暴露敏感数据。在执行之前,请务必查看脚本内容并验证其来源。

触发器条件配置示例

{
  "script_path": "./config/filters/evm_filter_block_number.sh",
  "language": "Bash",
  "arguments": ["--verbose"],
  "timeout_ms": 1000
}
可用字段
触发器条件字段
字段 类型 描述
script_path String 脚本的路径
language String 脚本的语言
arguments Array[String] 脚本的参数(可选)。
timeout_ms Number 脚本的超时非常重要,可以避免执行期间的无限循环。如果脚本花费的时间超过超时时间,它将被终止,并且默认情况下将包含该匹配项。
重要注意事项
  • 监视器中的网络 slug 必须与有效的网络配置匹配。

  • 触发器 ID 必须与配置的触发器匹配。

  • EVM 和 Stellar 网络之间的表达式语法和可用变量不同。

  • 可以通过两种方式提供 ABI:

  • 对于 EVM 网络:通过使用标准 Ethereum ABI 格式的监视器配置

  • 对于 Stellar 网络:通过使用 SEP-48 格式的监视器配置,或者如果未提供,则自动从链中获取

  • 监视频率由网络的 cron_schedule 控制。

  • 每个监视器可以同时监视多个网络和地址。

  • 可以暂停监视器,而无需删除其配置。

运行监视器

本地执行

  1. 基本启动:
./openzeppelin-monitor
  1. 记录到文件:
./openzeppelin-monitor --log-file
  1. 启用指标:
./openzeppelin-monitor --metrics
  1. 验证配置而不启动:
./openzeppelin-monitor --check

Docker 执行

  1. 启动所有服务:
cargo make docker-compose-up
  1. 具有指标和监视 (Prometheus + Grafana):
## 在 .env 文件中设置 METRICS_ENABLED=true,然后:
docker compose --profile metrics up -d
  1. 查看日志:
docker compose logs -f monitor
  1. 停止服务:
cargo make docker-compose-down

命令行选项

选项 默认值 描述
--log-file false 将日志写入文件而不是 stdout
--log-level info 设置日志级别(trace、debug、info、warn、error)
--metrics false 在端口 8081 上启用指标服务器
--check false 仅验证配置文件
--help - 显示所有可用选项

测试你的配置

网络配置

validate_network_config.sh 脚本有助于确保你的网络配置已正确设置并可运行。脚本:

  • 测试所有配置的 RPC 端点的运行状况

  • 使用特定于网络的方法验证连接

  • 为每个端点提供清晰的可视反馈

## 测试默认网络目录 (/config/networks/)
./scripts/validate_network_config.sh

## 测试特定配置目录
./scripts/validate_network_config.sh -f /path/to/configs
在设置新网络、部署配置更改之前或在对连接问题进行故障排除时运行此脚本。
验证配置文件

在启动监视器服务之前,可以使用 --check 选项验证配置文件:

./openzeppelin-monitor --check

此命令将:

  • 解析和验证所有配置文件

  • 检查语法错误

  • 验证监视器、网络和触发器之间的引用

  • 报告任何问题而不启动服务

建议在对任何配置文件进行更改后运行此检查。

监视器配置

可以在两种模式下测试监视器:

1. 最新区块模式

此模式处理所有已配置网络上的最新区块。

./openzeppelin-monitor --monitor-path="config/monitors/evm_transfer_usdc.json"

它会执行以下操作:

  • 运行“USDC 代币的大额转账”监视器

  • 以配置中指定的所有网络为目标

  • 仅处理每个网络的最新区块

  • 为找到的每个匹配项向所有关联的通道发送通知

2. 特定区块模式

此模式允许你分析特定网络上的特定区块,这对于调试特定交易、验证已知事件的监视器行为以及测试历史数据的监视器性能非常有用。

./openzeppelin-monitor \
    --monitor-path="config/monitors/evm_transfer_usdc.json" \
    --network=ethereum_mainnet \
    --block=12345678

它会执行以下操作:

  • 运行“USDC 代币的大额转账”监视器

  • 仅以指定网络 ( ethereum_mainnet) 为目标

  • 仅处理指定的区块 ( 12345678)

  • 为找到的每个匹配项向所有关联的通道发送通知

特定区块模式需要两个参数:<br>- --network:要分析的网络<br> <br>- --block:要处理的区块号
数据持久性(可选)
  • LOG_MODE 设置为文件会将日志数据持久保存在主机上的 logs/ 中。要将其更改为其他目录,请使用 LOG_DATA_DIR

  • MONITOR_DATA_DIR 设置为你的主机系统上的特定目录,该目录将在容器重新启动之间持久保存数据。

错误处理

监视器实现了一个具有丰富上下文和跟踪功能的全面错误处理系统。有关错误处理的详细信息,请参阅错误处理指南

重要注意事项

性能注意事项

  • 监视器性能取决于网络拥塞和 RPC 端点可靠性。

  • 查看监视器发出的RPC 调用列表

  • max_past_blocks 配置至关重要:

  • 计算方式为:(cron_interval_ms/block_time_ms) + confirmation_blocks + 1(如果未指定,则默认为此计算)。

  • 1 分钟以太坊 cron 的示例:(60000/12000) + 12 + 1 = 18 个区块

  • 过低的设置可能会导致错过区块。

  • 触发器条件根据它们在触发器条件数组中的位置依次执行。正确的执行还取决于系统上可用的文件描述符的数量。为确保最佳性能,建议将打开文件描述符的限制增加到至少 2048 或更高。在基于 Unix 的系统上,你可以通过运行 ulimit -n 检查当前限制,并通过 ulimit -n 2048 暂时 增加它。

  • 由于脚本在启动时加载,因此对脚本文件的任何修改都需要重新启动监视器才能生效。

  • 请参阅有关自定义脚本的性能注意事项此处

通知注意事项

  • 模板变量是依赖于上下文的:

  • 事件触发的通知仅填充事件变量。

  • 函数触发的通知仅填充函数变量。

  • 混合上下文会导致空值。

  • 自定义脚本通知有其他注意事项:

  • 脚本接收监视器匹配数据和参数作为 JSON 输入

  • 脚本必须在其配置的 timeout_ms 内完成,否则它们将被终止

  • 脚本修改需要重新启动监视器才能生效

  • 支持的语言仅限于 Python、JavaScript 和 Bash

支持

如需支持或咨询,请在 Telegram 上联系我们。

有功能请求或想贡献?加入我们在 GitHub 上的社区

许可证

本项目根据 GNU Affero General Public License v3.0 获得许可 - 有关详细信息,请参阅 LICENSE 文件。

安全

有关安全问题,请参阅我们的 安全策略

快速入门 →

  • 原文链接: docs.openzeppelin.com/mo...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。