Repository navigation
Expand file tree
/
Copy pathalerts.example.toml
More file actions
189 lines (162 loc) · 7.85 KB
/
Copy pathalerts.example.toml
File metadata and controls
189 lines (162 loc) · 7.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# Alerting for a rustock node.
#
# rustock --alerts-config /etc/rustock/alerts.toml ...
#
# One file, one mail transport, one section per kind of alert. Adding a kind
# means adding a section here, not a second file with a second credential that
# drifts out of date.
#
# Entirely optional, and entirely an observer. Nothing here is wired into
# execution or sync, so it cannot affect consensus or slow block processing.
# If the mail server is unreachable the node does not notice.
[alerts.email]
# Until this is true, alerts are logged and nothing is sent.
enabled = false
# Secrets do NOT belong in this file. Put them in a root-owned file with mode
# 0600 and refer to them below as ${NAME}; they are resolved at startup and
# never logged. The node refuses to start the watcher if this file is readable
# by group or others.
#
# /etc/rustock/alerts.env
# SMTP_USER=...
# SMTP_PASSWORD=...
#
# env_file = "/etc/rustock/alerts.env"
to = ["you@example.com"]
from = "rustock-node@example.com"
smtp_host = "smtp.example.com"
smtp_port = 587
# username = "${SMTP_USER}"
# password = "${SMTP_PASSWORD}"
# none | starttls | implicit
tls = "starttls"
# Prefix subjects with "[rustock peg-out] " so they filter easily.
subject_prefix_enabled = true
# ── Node health alarms ───────────────────────────────────────────────────────
#
# Three conditions, each of which means the node is not doing its job:
#
# 1. stalled -- the executed head has not moved
# 2. behind -- the best peer is more than `block_gap` blocks ahead
# 3. ahead -- this node is more than `block_gap` ahead of EVERY peer
#
# Condition 3 is not a humblebrag. Being ahead of the whole network means
# either that this node is following a chain the network rejected, or that all
# of its peers are stale and it is effectively blind. Both look like health
# from inside.
#
# Mail goes through the [alerts.email] transport above: one
# configuration, one credential.
[alerts.pegout]
enabled = true
# How often to look for newly executed blocks.
poll_interval_secs = 5
# Where to start. Omit to begin at the node's current executed head, which is
# almost always what you want — otherwise the first sweep replays the chain and
# alerts on history.
# start_block = 9200000
# Alert when a single peg-out request exceeds this many BTC.
pegout_alert_btc = 100.0
# Alert when any single non-change output of a peg-out Bitcoin transaction
# awaiting signature exceeds this many BTC.
output_alert_btc = 100.0
# Alert when the total value of peg-outs in transit exceeds this many BTC.
# "In transit" means built by the Bridge but not yet confirmed, so not yet
# handed to the signers.
in_transit_alert_btc = 200.0
# How often to log the total value of peg-outs in transit, in seconds.
# A report, not an alert: it fires whatever the total is, as long as it is
# non-zero, so money moving is visible rather than only threshold breaches.
# Silent when nothing is in transit. 0 disables it.
in_transit_report_secs = 600
# Confirmations a peg-out needs before it stops counting as in transit.
# Mainnet uses 4,000.
confirmations = 4000
# scriptPubKeys (hex) that are federation change rather than a transfer to an
# end user. Outputs paying these are excluded from the per-output check and from
# the in-transit total.
#
# Leave empty and EVERY output is treated as a user output. That over-alerts
# whenever the federation's change exceeds the threshold, but it can never hide
# a real transfer — the safe direction. Fill it in to silence change precisely.
federation_change_scripts = [
# "a914...87",
]
# ── Large transfers in and out of smart contracts ─────────────────────────
#
# Alert when rBTC above a threshold moves with a smart contract on either
# side of it: an account paying a contract, or a contract paying anyone.
#
# Unlike everything else here this watch cannot be answered by reading what
# the node already stored. A value transfer emits no log and writes nothing
# to the receipt, so a contract paying out is invisible from the outside.
# Finding one means **re-executing the block** with the call tracer, the way
# `trace_block` does, and reading the call tree.
#
# That is the cost, and it is why this is off by default. It still cannot
# affect consensus -- the replay writes nothing and runs on the watcher's own
# thread -- but it is not free the way reading a receipt is: about 24 ms a
# block at the median, measured on mainnet.
#
# Which is why it does not run on every block. The node already walks every
# account a block wrote, for the supply check, and so already knows the
# largest balance change in each block. `replay_filter_btc` below uses that
# to skip blocks in which nothing happened -- which, measured over 300
# consecutive mainnet blocks, is almost all of them.
#
# What it catches that a naive check would miss:
#
# - internal transfers, which is most of the point: a contract forwarding
# funds does not appear in any transaction's `to` or `value`
# - CREATE endowments and SELFDESTRUCT payouts
# - and it correctly ignores DELEGATECALL and CALLCODE, which carry a
# `value` but move no balance, and anything under a reverted frame
[alerts.contract_value]
enabled = false
# Alert above this many RBTC, per transfer. Independent of the peg-out
# thresholds above on purpose: a peg-out is the federation moving bitcoin,
# and a contract payment is a different event at a different scale.
transfer_alert_btc = 50.0
# Do not re-execute a block unless some account's balance moved more than
# this. Free, because the supply check computed those deltas already.
#
# Deliberately far below transfer_alert_btc, and it must stay below it --
# a value at or above it is refused. A balance delta is a NET figure, so a
# transfer hides from this filter only if the value came back to where it
# started inside the same block. A contract passing funds through is NOT
# that case and is not missed: the originator and the final recipient still
# show the movement. What is given up is a closed cycle -- realistically a
# flash loan, where a pool lends and is repaid in one transaction. At 0.1
# RBTC such a cycle has to leave every account within 0.1 RBTC of where it
# started, which is to say nobody profited.
#
# 0 re-executes every block: the behaviour before this filter existed, kept
# reachable as the control for measuring what the filter saves.
replay_filter_btc = 0.1
# How far behind the executed head this watch will still re-execute. A node
# that was stopped for a day comes back with thousands of blocks to sweep,
# and re-executing all of them would turn a backlog into an outage. Blocks
# further back than this are still swept for peg-outs, which is cheap, and
# the skipped range is logged -- a gap in this watch is never silent.
max_catchup_blocks = 128
[alerts.node_health]
enabled = false
# How often to take a reading. Must not exceed for_secs, or a condition could
# hold for its whole window between two readings and never be seen -- the
# config is rejected if it does.
poll_interval_secs = 30
# Blocks of difference before "behind" or "ahead" is true at all. The test is
# strictly greater, so a steady gap of exactly this many does not alert.
block_gap = 10
# How long a condition must hold CONTINUOUSLY before it is reported. Every one
# of these is momentarily true in normal operation -- a reorg stalls execution
# for seconds, a peer announces a block before this node has it -- so any
# reading that clears the condition restarts the timer.
for_secs = 600
# How long that alarm stays quiet after firing. NOT reset by recovery: a node
# that flaps is exactly the case where per-occurrence mail is useless, because
# the first message already said what is wrong.
#
# The three alarms are independent; a cooldown on one says nothing about the
# others.
cooldown_secs = 86400