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
//! Guest-side SDK for Odal Node Wasm sector plugins.
//!
//! A sector plugin author implements the [`DppSectorPlugin`](dpp_plugin_traits::DppSectorPlugin) trait from
//! `dpp-plugin-traits` and invokes [`export_plugin!`] once. The macro generates
//! the full low-level Wasm ABI (`alloc`, `dealloc`, `metadata`, `describe`,
//! `validate`, `calculate_metrics`, `generate_passport`) and wires each export
//! to the corresponding trait method. Plugins no longer hand-roll the ABI shim
//! or redefine their own output structs — they speak the shared contract.
//!
//! ## Why `describe()`
//!
//! The host calls `describe()` immediately after loading a plugin to read its
//! [`PluginCapabilities`](dpp_plugin_traits::PluginCapabilities) (ABI version, supported schema versions, feature
//! capabilities) and run `dpp_plugin_traits::check_compatibility` *before*
//! dispatching any work. This is what makes the version registry enforceable at
//! the Wasm boundary rather than aspirational.
//!
//! ## ABI summary
//!
//! | Export | Signature | Returns (JSON) |
//! |--------|-----------|----------------|
//! | `alloc` | `(len: u32) -> u32` | pointer to `len` bytes |
//! | `dealloc` | `(ptr: u32, len: u32)` | — |
//! | `metadata` | `() -> u64` | [`PluginMeta`](dpp_plugin_traits::PluginMeta) |
//! | `describe` | `() -> u64` | [`PluginCapabilities`](dpp_plugin_traits::PluginCapabilities) |
//! | `validate` | `(ptr: u32, len: u32) -> u64` | [`AbiResult`](dpp_plugin_traits::AbiResult) (`ok: null` / `error`) |
//! | `calculate_metrics` | `(ptr: u32, len: u32) -> u64` | [`AbiResult`](dpp_plugin_traits::AbiResult) (`ok: PluginResult`) |
//! | `generate_passport` | `(ptr: u32, len: u32) -> u64` | [`AbiResult`](dpp_plugin_traits::AbiResult) (`ok: payload`) |
//!
//! Every `-> u64` return packs the output buffer as `(out_ptr << 32) | out_len`.
//! The host reads the JSON, then frees the buffer via `dealloc`.
//!
//! ## Module layout
//!
//! - [`abi`] — the low-level linear-memory ABI (`alloc`/`dealloc`/buffer packing).
//! - `codec` — pure, host-testable JSON glue (`*_bytes` functions).
//! - `entry` — the `run_*` ABI entry-point wrappers `export_plugin!` calls.
//! - [`validate`] — shared field-validation helpers.
/// Re-export of the shared host/guest contract so plugins need only one
/// path dependency.
pub use dpp_plugin_traits as traits;
/// Re-export of the shared cross-field regulatory rules ([`dpp_rules`]), so a
/// plugin uses the same rule implementation as `dpp-domain` rather than
/// reimplementing it.
pub use dpp_rules as rules;
use ;
pub use ;
pub use ;
pub use ;
// ─── Export macro ─────────────────────────────────────────────────────────────
/// Generate the full Wasm ABI for a sector plugin.
///
/// `$plugin` must implement [`DppSectorPlugin`](dpp_plugin_traits::DppSectorPlugin)
/// and [`Default`] (plugins are deterministic and stateless, so the instance is
/// constructed per call). Invoke once at the crate root:
///
/// ```ignore
/// use dpp_plugin_sdk::{export_plugin, traits::*};
///
/// #[derive(Default)]
/// struct BatteryPlugin;
/// impl DppSectorPlugin for BatteryPlugin { /* ... */ }
///
/// export_plugin!(BatteryPlugin);
/// ```
/// Compile-checks this crate's README examples.
///
/// A README example is a public claim about the API, and nothing else in the
/// build compiles one. Without this, a README can advertise a function that
/// does not exist — which is exactly what happened before this harness landed.
;