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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// Copyright 2025 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause
pub const WASM_LIMITS: WasmLimits = WasmLimits ;
/// Maximum Wasmer metering points a single template invocation may consume. Enforced by the
/// metering middleware compiled into the engine (see `tari_engine::wasm::module::create_engine`):
/// exceeding it traps the call with an out-of-gas error.
pub const MAX_WASM_POINTS_PER_CALL: u64 = 100_000_000;
/// Maximum Wasmer metering points a whole transaction may consume, summed across every template
/// invocation it makes (top-level instructions and nested cross-template calls). Each invocation
/// otherwise gets a fresh per-call budget, so without this a transaction could multiply its
/// execution time by stacking instructions or recursing to `ENGINE_LIMITS.max_call_depth`. Enforced
/// in `WasmProcess::invoke` by capping each call's allowance to the budget remaining for the
/// transaction. Kept equal to the per-call cap: a transaction gets one compute budget, shared across
/// all its calls. The aggregate across a *block* still needs a separate per-block budget.
pub const MAX_WASM_POINTS_PER_TRANSACTION: u64 = 100_000_000;
/// Maximum native-verification points (priced by [`NativeExecutionPoints`]) a whole transaction may consume. The
/// native counterpart of [`MAX_WASM_POINTS_PER_TRANSACTION`], enforced in `StateTracker::charge_native_execution`.
///
/// Both per-transaction ceilings exist so the block execution budget has a bounded overshoot: a leader only learns
/// a transaction's cost after executing it, so an honest block may exceed the propose budget by one transaction's
/// worth, and `max_block_validation_execution_points` must leave room for it. Without this cap the native half of
/// that overshoot is bounded only by the structural limits, which permit ~2.2e9 points of stealth verification —
/// and, for `ClaimBurn`, only by transaction weight, which permits ~2.9e9. Either would exceed the headroom and
/// get honest blocks rejected.
///
/// Sized just above the most expensive statement set the structural caps allow ([`STEALTH_LIMITS`]: 64 transfers,
/// 256 outputs each carrying the view-key surcharge, 1024 inputs ≈ 2.23e9), so no transaction the other limits
/// admit can trip this one. Tightening it means tightening those caps first.
pub const MAX_NATIVE_POINTS_PER_TRANSACTION: u64 = 2_400_000_000;
/// Execution metering points a transaction may consume *before* its fee payments cover them. A
/// transaction sources its fee in the fee intent (withdraw, claim-burn, AMM swap to TARI, stealth
/// transfer, …) and only then calls `pay_fee`, so it must be allowed to run some compute on credit;
/// this bounds that credit. Beyond it, each WASM call's metering allowance is capped to the points
/// the fees paid so far can cover (`WasmProcess::invoke`), and native verification pre-charges its
/// point cost against the same allowance, so a transaction that does not pay traps out-of-gas here
/// rather than consuming the full [`MAX_WASM_POINTS_PER_TRANSACTION`] (or unmetered native crypto)
/// for free. This is the bound on total free compute — WASM and native — a non-paying transaction
/// can extract from a validator. Payments raise the allowance above this value proportionally to
/// the WASM fee rate.
///
/// The credit applies to the fee intent only. Sourcing a fee is the whole reason a transaction may
/// run anything before paying, so once the fee checkpoint is taken the credit ends and the remaining
/// instructions are funded by the payment alone (`StateTracker::wasm_point_allowance`). Extending it
/// past the checkpoint would hand every transaction this many points of compute it never pays for,
/// on top of what it bought.
///
/// Sized at ~3x the most expensive legitimate fee-sourcing flow: paying a fee from stealth UTXOs
/// (one transfer: fixed cost + 1 stealth change output + up to 64 dust inputs ≈ 10.8M points at
/// the calibrated native prices below — fees are TARI, which has no view key, so the flow prices
/// at the base output rate). The other fee-sourcing flows are far cheaper: a burn claim is
/// [`NativeExecutionPoints::PER_CLAIM_BURN`] and an AMM swap to TARI is ~143k WASM points
/// (guarded by `tari_engine`'s `complex_fee_payment` test). Re-derive with
/// `cargo run -p tari_engine --example native_points_calibrate --release`.
pub const FREE_COMPUTE_GRACE_POINTS: u64 = 32_000_000;
/// Metering-point prices for native (non-WASM) verification work, charged against the same
/// payment-funded allowance as WASM execution ([`FREE_COMPUTE_GRACE_POINTS`] of credit, then
/// payments fund the rest). Native crypto runs outside the Wasmer meter, so these price it by
/// wall-clock equivalence: measured milliseconds × the measured points-per-millisecond rate of
/// real metered WASM on the same hardware. Both sides are CPU-bound, so the ratio holds across
/// validator classes. Values from `cargo run -p tari_engine --example native_points_calibrate
/// --release` (~8.4M points/ms), rounded up.
;
pub const ENGINE_LIMITS: EngineLimits = EngineLimits ;
pub const MAX_DIVISIBILITY: u8 = 18;
pub const MAX_TOKEN_SYMBOL_LEN: usize = 10;
/// Maximum number of `PublishTemplate` instructions a single transaction may contain.
///
/// Publishing a template registers a new global substate and carries a WASM binary up to
/// [`ENGINE_LIMITS`]`.max_template_binary_size_bytes`. Capping at one keeps each publishing transaction to a single,
/// bounded template registration; multiple publishes would stack several large binaries and their validation/storage
/// cost into one transaction with no benefit a caller cannot get from separate transactions. The engine enforces this
/// during execution — a consensus rule applied uniformly by every validator — and the mempool mirrors it to reject
/// such transactions at ingress.
pub const MAX_PUBLISH_TEMPLATES_PER_TRANSACTION: usize = 1;
/// Verifying a stealth transfer is native work dominated by the per-output bulletproof range proof and ElGamal
/// viewable-balance proof (~1ms per output on x86-class hardware). It is priced in metering points by
/// [`NativeExecutionPoints`] and counted toward the per-block execution budget, so the block-level bound is the
/// budget rather than these caps. The per-transfer limits bound one statement and the per-transaction limits bound
/// the aggregate, capping how much verification a single transaction can stack — which keeps any one transaction
/// from consuming a whole block's budget by itself. The per-transaction caps are a consensus-relevant execution
/// rule enforced uniformly during execution, not just a mempool heuristic.
pub const STEALTH_LIMITS: StealthLimits = StealthLimits ;
/// Spending confidential outputs is native, unmetered work: each input commitment is a separate substate that must be
/// locked and read plus folded into the balance-proof point aggregation, and each withdraw verifies a bulletproof range
/// proof over its (at most two) outputs. The per-withdraw limit bounds one proof; the per-transaction limits bound the
/// aggregate so a single transaction cannot stack enough native verification and substate access to stall the proposing
/// leader. These are consensus-relevant execution rules enforced uniformly during execution, not mempool heuristics.
pub const CONFIDENTIAL_LIMITS: ConfidentialLimits = ConfidentialLimits ;