OpenZeppelin Monitor - OpenZeppelin 文档

本文档介绍了 OpenZeppelin Monitor,它是一种区块链监控服务,可以实时监控链上活动,并根据配置的条件触发通知。

OpenZeppelin Monitor

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

概述

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

主要功能

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

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

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

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

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

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

支持的网络

  • EVM 兼容网络

  • Stellar

通知渠道

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

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

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

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

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

  • 自定义脚本 - 执行 Python、JavaScript 或 Bash 脚本

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

安装

前提条件

  • Rust 2021 版本或更高版本

  • 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.json, stellar_testnet.json

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

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

  • 示例:usdc_transfer_monitor.json, dai_liquidation_monitor.json

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

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

  • 示例:slack_notifications.json, email_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"],
    ...
}
确保所有引用的 slug 和触发器键存在于各自的配置文件中。如果监控器无法解析这些引用,则启动将失败。
安全协议指南

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

网络协议

####### RPC URLs

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

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

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

通知协议

####### Webhook 通知

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

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

  • X-API-Key 标头

  • Authorization 标头

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

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

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

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

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

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

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

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

####### Slack 通知

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

  • 警告:非 HTTPS URL 将触发安全警告

####### Discord 通知

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

  • 警告:非 HTTPS URL 将触发安全警告

####### 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 系统)

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

  • 推荐的权限:对脚本文件使用 644rw-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 设置日志级别(trace、debug、info、warn、error)
--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 格式作为模板变量。 将数据展平为点表示法以供模板使用。

常用变量
变量 描述
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})"
      }```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 price,单位为 wei (legacy transactions)
max_fee_per_gas uint256 EIP-1559 最大 gas fee
max_priority_fee_per_gas uint256 EIP-1559 优先级 fee
gas_limit uint256 交易的 Gas limit
nonce uint256 发送者 nonce
input string Hex 编码的输入数据 (例如,"0xa9059cbb…​")
gas_used uint256 实际使用的 gas (来自 receipt)
transaction_index uint64 在区块中的位置
可用的交易字段 (Stellar)
字段 类型 描述
hash string 交易哈希
ledger i64 包含该交易的 Ledger 序列号
value i64 第一个相关操作关联的 value (例如,付款金额)。 如果未找到相关操作或 value,则默认为 0。
from address 第一个相关操作的源账户地址(例如,付款发送者)。不区分大小写比较。
to address 第一个相关操作的目标账户地址(例如,付款接收者或调用的合约)。不区分大小写比较。
匹配规则
  • 如果未指定任何条件,则匹配所有交易

  • 对于多个条件类型:

  • 首先检查交易条件

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

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

表达式

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

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

类型 描述 示例运算符 注释
Numeric (uint/int variants) 整数值 (例如,42-100) 或小数值 (例如,3.14-0.5)。 >, >=, <, , ==, != 如果存在小数点,则数字必须在小数点前后都有数字 (例如,.55. 不是有效的独立数字)。
Address 区块链地址。 ==, != 比较 (例如,from == '0xABC…​') 通常对地址 value 本身的十六进制字符不区分大小写。
String 文本 value。 可以用单引号引起来 (例如,'hello'),或者在比较的右侧不加引号 (例如,active)。 ==, !=, starts_with, ends_with, contains 带引号的字符串支持 \' 来转义单引号,\\ 来转义反斜杠。 所有字符串比较操作 (例如,name == 'Alice'description contains 'error') 在评估期间都以不区分大小写的方式执行。 有关更多示例和详细信息,请参阅专门的“字符串操作”部分。
Boolean True 或 false value。 ==, != 表示为 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,通常是 config 中的 JSON 字符串 (例如,'["a", {"id":1}]')。 对于 EVM,通常从 ABI 参数解码。 contains, ==, !=, [index] 详细操作,包括索引访问和 contains 的行为,因网络而异。 请参阅下面的“复杂类型上的操作”。
Object/Map (Stellar) 键值对,通常在 config 中表示为 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 属性)。

字符串操作:

有多个运算符可用于匹配模式和比较字符串 value。 这些对于 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、比较符号如 ==>) 以不区分大小写的方式解析。 例如,CONTAINS 的处理方式与 contains 相同,TRUEtrue 相同。

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

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

  • 模式匹配 ( 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") / Map ( kind: "Map") 操作

  • object_param.key == 'value' 检查对象或 map 是否具有名为 key 且 value 为 'value' 的键。

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

  • object_param == '{"raw_json_object_string"}' 检查对象或 map 是否与提供的 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 (对'==' 和 '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 (对'==' 和 '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") / Map ( 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'" // 不区分大小写的包含
"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 Token 的大额转帐”监视器

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

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

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

2. 特定区块模式

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

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

这会做什么:

  • 运行“USDC Token 的大额转帐”监视器

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

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

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

“特定区块模式”需要两个参数:<br>- --network:要分析的网络<br> <br>- --block:要处理的区块号
数据持久性(可选)
  • LOG_MODE 设置为 file 会将日志数据持久保存在主机上的 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 增加它。

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

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

通知注意事项

  • 模板变量是上下文相关的:

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

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

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

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

  • 脚本接收监视器匹配数据和参数作为 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
江湖只有他的大名,没有他的介绍。