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
//! Extranonce prefix allocation for downstream SV2 channels.
//!
//! [`ExtranonceAllocator`] manages unique extranonce prefixes for both
//! standard and extended mining channels.
//!
//! # Extranonce layout
//!
//! | upstream_prefix | local_prefix | local_index | rollable |
//! |:---------------:|:------------:|:-----------:|:--------:|
//! | upstream node | caller (this node) | allocator | miner |
//!
//! - **upstream_prefix**: Bytes assigned by an upstream node. Empty for a
//! Pool / root allocator built with [`ExtranonceAllocator::new`].
//! - **local_prefix**: Caller-owned static bytes placed inside the
//! allocator's region. Typically used as a server/node identifier
//! (pool case) or as padding to shrink `rollable` to a specific target
//! size, or for embedding a tag/identifier. Can be empty.
//! - **local_index**: Per-channel dynamic bytes managed by the allocator —
//! each channel gets a unique value. The byte width is derived
//! automatically from `max_channels` via [`bytes_needed`].
//! - **rollable**: Remaining space for downstream rolling (extended
//! channels) or further allocation layers. Standard channels zero-pad
//! this portion.
//!
//! Both constructors, [`ExtranonceAllocator::new`] and
//! [`ExtranonceAllocator::from_upstream_prefix`], take `local_prefix_bytes`, so the
//! four-region layout is symmetric across pool and proxy/client cases.
//!
//! # Memory
//!
//! The allocator uses an internal bitmap of `max_channels` bits. Reference:
//!
//! | `max_channels` | `local_index` bytes | Bitmap memory |
//! |----------------|---------------------|---------------|
//! | 256 | 1 | 32 B |
//! | 65,536 | 2 | 8 KB |
//! | 16,777,216 | 3 | 2 MB |
//!
//! # Lifecycle
//!
//! Each call to [`ExtranonceAllocator::allocate_standard`] or
//! [`ExtranonceAllocator::allocate_extended`] returns an owning
//! [`AllocatedExtranoncePrefix`] — a type-level guarantee that the prefix
//! holds a reservation in the allocator's bitmap.
//!
//! **The allocation is released automatically when the
//! [`AllocatedExtranoncePrefix`] is dropped.** Server-side channel
//! constructors (e.g.
//! [`server::extended::ExtendedChannel::new_for_pool`](crate::server::extended::ExtendedChannel::new_for_pool))
//! take [`AllocatedExtranoncePrefix`] directly, so the allocator's
//! accounting always reflects the set of live server channels. Client-side
//! constructors take the wider [`ExtranoncePrefix`] (which an
//! [`AllocatedExtranoncePrefix`] converts into via [`Into`]), since
//! client-held prefixes legitimately come from both wire and allocator
//! sources. There is no manual release API — ownership enforces the
//! lifecycle.
//!
//! Internally this is implemented by having the allocator hold its bitmap
//! in an `Arc` and giving each outstanding prefix a `Weak` reference to it.
//! The prefix's `Drop` upgrades the `Weak`, clears its bit via an atomic
//! operation, and returns. If the allocator has already been dropped, the
//! upgrade fails and `Drop` becomes a silent no-op (safe: nothing to update).
//!
//! # Usage
//!
//! ## Pool (root node, no upstream)
//!
//! A pool creates the allocator with [`ExtranonceAllocator::new`], optionally
//! providing a `local_prefix_bytes` server identifier. It then calls
//! [`allocate_standard`](ExtranonceAllocator::allocate_standard) or
//! [`allocate_extended`](ExtranonceAllocator::allocate_extended) each time a
//! downstream opens a channel.
//!
//! ```
//! use channels_sv2::extranonce_manager::ExtranonceAllocator;
//!
//! // Pool: 20-byte extranonce, 2-byte server identifier, up to 65 536 channels.
//! // Layout: upstream(0) + local_prefix(2) + local_index(2) = 4;
//! // rollable = 20 − 4 = 16.
//! let mut allocator = ExtranonceAllocator::new(
//! vec![0x00, 0x01], // local_prefix_bytes (server identifier)
//! 20, // total_extranonce_len
//! 65_536, // max_channels
//! ).unwrap();
//!
//! let prefix = allocator.allocate_standard().unwrap();
//! // Pass `prefix` directly to the channel constructor — the channel
//! // takes ownership, and the allocation is released automatically when
//! // the channel (and thus the prefix) is dropped.
//! ```
//!
//! ## JDC / Translator / Proxies (receives upstream extranonce prefix)
//!
//! Proxies (JDC and Translator included) receive an `extranonce_prefix` from
//! their upstream node (via `OpenExtendedMiningChannelSuccess` or
//! `SetExtranoncePrefix`). They create the allocator with
//! [`ExtranonceAllocator::from_upstream_prefix`] and then subdivide the remaining
//! space for their own downstream channels. `local_prefix_bytes` can be used
//! to embed a tag or to absorb slack so `rollable` matches a specific target
//! size.
//!
//! ```
//! use channels_sv2::extranonce_manager::ExtranonceAllocator;
//!
//! let upstream_prefix = vec![0xAA, 0xBB, 0xCC, 0xDD];
//! let mut allocator = ExtranonceAllocator::from_upstream_prefix(
//! upstream_prefix,
//! Vec::new(), // local_prefix_bytes: none in this example
//! 20, // total_extranonce_len
//! 65_536, // max_channels
//! ).unwrap();
//!
//! // Layout: upstream(4) + local_prefix(0) + local_index(2) = 6;
//! // rollable = 20 − 6 = 14.
//! let prefix = allocator.allocate_extended(14).unwrap();
//! ```
//!
//! ## Translator: pinning `rollable` to a downstream-chosen size
//!
//! When a translator wants to grant its SV1 miner exactly `N` rollable bytes
//! regardless of how much space upstream left, it passes the slack as
//! `local_prefix_bytes`:
//!
//! ```
//! use channels_sv2::extranonce_manager::ExtranonceAllocator;
//!
//! let upstream_prefix = vec![0xAA, 0xBB, 0xCC, 0xDD];
//! let downstream_rollable: u8 = 4;
//! let total: u8 = 20;
//! let max_channels: u32 = 256;
//!
//! // upstream(4) + local_index(1) + rollable(4) = 9; slack = 11.
//! let local_prefix_bytes = vec![0x42; 11];
//! let mut allocator = ExtranonceAllocator::from_upstream_prefix(
//! upstream_prefix,
//! local_prefix_bytes,
//! total,
//! max_channels,
//! ).unwrap();
//!
//! assert_eq!(allocator.rollable_extranonce_size(), downstream_rollable);
//! ```
pub use ;
pub use ;
/// Maximum extranonce length in bytes (Sv2 spec).
pub const MAX_EXTRANONCE_LEN: u8 = 32;
/// Minimum bytes needed to represent `n` distinct values (`0..n`).
///
/// Always returns at least 1. Exposed so consumers can derive byte counts
/// from their `max_channels` value without hardcoding (and risking drift
/// from what [`ExtranonceAllocator`] uses internally).
pub const