|
1 | | -use binary_sv2::Seq064K; |
| 1 | +// NOTE: This is a simplified example that demonstrates UserIdentity creation. |
| 2 | +// For full TLV encoding/decoding examples with parsers_sv2, see the parsers_sv2 crate tests |
| 3 | +// or run integration tests from the workspace root. |
| 4 | +// |
| 5 | +// To run: cargo run --example worker_specific_hashrate_tracking --package extensions_sv2 |
| 6 | + |
2 | 7 | use extensions_sv2::{ |
3 | | - RequestExtensions, RequestExtensionsSuccess, UserIdentity, |
4 | | - EXTENSION_TYPE_WORKER_HASHRATE_TRACKING, TLV_FIELD_TYPE_USER_IDENTITY, |
| 8 | + UserIdentity, EXTENSION_TYPE_WORKER_HASHRATE_TRACKING, TLV_FIELD_TYPE_USER_IDENTITY, |
5 | 9 | }; |
6 | | -use mining_sv2::SubmitSharesExtended; |
7 | | -use parsers_sv2::{TlvField, TlvList, TLV_HEADER_SIZE}; |
8 | 10 |
|
9 | 11 | fn main() { |
10 | | - println!("=== Worker-Specific Hashrate Tracking Extension Example ===\n"); |
11 | | - |
12 | | - // Step 1: Client requests the Worker Hashrate Tracking extension |
13 | | - println!("1. Extension Negotiation:"); |
14 | | - println!(" Client requests Worker Hashrate Tracking extension (0x0002)"); |
| 12 | + println!("=== Worker-Specific Hashrate Tracking Extension (Simplified) ===\n"); |
15 | 13 |
|
16 | | - let request = RequestExtensions { |
17 | | - request_id: 1, |
18 | | - requested_extensions: Seq064K::new(vec![EXTENSION_TYPE_WORKER_HASHRATE_TRACKING]).unwrap(), |
19 | | - }; |
20 | | - println!(" {}", request); |
| 14 | + // Step 1: Extension Overview |
| 15 | + println!("1. Extension Overview:"); |
21 | 16 | println!( |
22 | | - " Extension type: 0x{:04x}\n", |
| 17 | + " Extension Type: 0x{:04x}", |
23 | 18 | EXTENSION_TYPE_WORKER_HASHRATE_TRACKING |
24 | 19 | ); |
25 | | - |
26 | | - // Step 2: Server accepts the extension |
27 | | - println!("2. Server Response:"); |
28 | | - let success = RequestExtensionsSuccess { |
29 | | - request_id: 1, |
30 | | - supported_extensions: Seq064K::new(vec![EXTENSION_TYPE_WORKER_HASHRATE_TRACKING]).unwrap(), |
31 | | - }; |
32 | | - println!(" {}", success); |
| 20 | + println!(" Purpose: Track individual worker hashrates within aggregated channels"); |
33 | 21 | println!( |
34 | | - " Extension 0x{:04x} is now active!\n", |
35 | | - EXTENSION_TYPE_WORKER_HASHRATE_TRACKING |
| 22 | + " TLV Field Type for user_identity: 0x{:02x}\n", |
| 23 | + TLV_FIELD_TYPE_USER_IDENTITY |
36 | 24 | ); |
37 | 25 |
|
38 | | - // Step 3: Demonstrate TLV field for user_identity |
39 | | - println!("3. TLV Field Structure:"); |
40 | | - println!(" When submitting shares, the client appends a TLV field:"); |
41 | | - println!( |
42 | | - " - Type (U16 | U8): Extension Type (0x{:04x}) | Field Type (0x{:02x})", |
43 | | - EXTENSION_TYPE_WORKER_HASHRATE_TRACKING, TLV_FIELD_TYPE_USER_IDENTITY |
44 | | - ); |
45 | | - println!(" - Length (U16): Actual byte length of worker name"); |
46 | | - println!(" - Value: UTF-8 encoded worker identifier\n"); |
47 | | - |
48 | | - // Step 4: Create UserIdentity TLV fields for different workers |
49 | | - println!("4. Example TLV Fields for Different Workers:\n"); |
| 26 | + // Step 2: Creating UserIdentity instances |
| 27 | + println!("2. Creating UserIdentity Instances:\n"); |
50 | 28 |
|
51 | | - let workers = vec!["Worker_001", "Miner_A", "Device_12345678901234567890123"]; |
| 29 | + let workers = vec![ |
| 30 | + "Worker_001", |
| 31 | + "Miner_A", |
| 32 | + "Device_12345678901234567890123", // 27 bytes (under 32 byte limit) |
| 33 | + ]; |
52 | 34 |
|
53 | 35 | for worker_name in workers { |
54 | | - let user_id = UserIdentity::new(worker_name).unwrap(); |
55 | | - |
56 | | - println!(" {}", user_id); |
57 | | - let tlv_bytes = TlvField::to_bytes(&user_id).unwrap(); |
58 | | - println!(" TLV Size: {} bytes", tlv_bytes.len()); |
59 | | - println!( |
60 | | - " TLV Format: [Type: 0x{:04x}|0x{:02x}] [Length: 0x{:04x}] [Value: \"{}\"]", |
61 | | - EXTENSION_TYPE_WORKER_HASHRATE_TRACKING, |
62 | | - TLV_FIELD_TYPE_USER_IDENTITY, |
63 | | - worker_name.len(), |
64 | | - worker_name |
65 | | - ); |
66 | | - |
67 | | - println!( |
68 | | - " Total TLV overhead: {} bytes + {} bytes (worker name) = {} bytes\n", |
69 | | - TLV_HEADER_SIZE, |
70 | | - worker_name.len(), |
71 | | - tlv_bytes.len() |
72 | | - ); |
73 | | - } |
74 | | - |
75 | | - // Step 5: Build a complete SubmitSharesExtended frame with TLV |
76 | | - println!("5. Building a SubmitSharesExtended Frame with Worker Identity:\n"); |
77 | | - |
78 | | - let submit_shares = SubmitSharesExtended { |
79 | | - channel_id: 1, |
80 | | - sequence_number: 42, |
81 | | - job_id: 1, |
82 | | - nonce: 0x12345678, |
83 | | - ntime: 0x87654321, |
84 | | - version: 0x20000000, |
85 | | - extranonce: vec![0x01, 0x02, 0x03, 0x04].try_into().unwrap(), |
86 | | - }; |
87 | | - |
88 | | - // Without worker identity (empty TLV list) |
89 | | - let tlv_list_empty = TlvList::from_slice(&[]).unwrap(); |
90 | | - let frame_bytes_without = tlv_list_empty |
91 | | - .build_frame_bytes_with_tlvs(parsers_sv2::Mining::SubmitSharesExtended( |
92 | | - submit_shares.clone(), |
93 | | - )) |
94 | | - .unwrap(); |
95 | | - println!( |
96 | | - " Frame bytes without worker identity: {} bytes", |
97 | | - frame_bytes_without.len() |
98 | | - ); |
99 | | - |
100 | | - // With worker identity (using UserIdentity::to_tlv()) |
101 | | - let user_identity = UserIdentity::new("Worker_001").unwrap(); |
102 | | - let tlv = user_identity.to_tlv().unwrap(); |
103 | | - let tlv_list_with = TlvList::from_slice(&[tlv]).unwrap(); |
104 | | - let frame_bytes_with = tlv_list_with |
105 | | - .build_frame_bytes_with_tlvs(parsers_sv2::Mining::SubmitSharesExtended(submit_shares)) |
106 | | - .unwrap(); |
107 | | - println!( |
108 | | - " Frame bytes with worker identity: {} bytes", |
109 | | - frame_bytes_with.len() |
110 | | - ); |
111 | | - println!( |
112 | | - " Difference: {} bytes (TLV overhead + worker name)\n", |
113 | | - frame_bytes_with.len() - frame_bytes_without.len() |
114 | | - ); |
115 | | - |
116 | | - // Step 6: Parsing TLV fields on the receiving side (Pool) |
117 | | - println!("6. Parsing TLV Fields from Received Messages:\n"); |
118 | | - println!(" When a pool receives SubmitSharesExtended with TLV data:"); |
119 | | - |
120 | | - // Simulate TLV data that would be appended to a message |
121 | | - let worker1_tlv = TlvField::to_bytes(&UserIdentity::new("Worker_001").unwrap()).unwrap(); |
122 | | - let worker2_tlv = TlvField::to_bytes(&UserIdentity::new("Miner_A").unwrap()).unwrap(); |
123 | | - |
124 | | - // Demo 1: Parse single TLV |
125 | | - println!("\n a) Parse single worker identity:"); |
126 | | - let tlv_list = TlvList::from_bytes(&worker1_tlv); |
127 | | - let negotiated = vec![EXTENSION_TYPE_WORKER_HASHRATE_TRACKING]; |
128 | | - let tlvs = tlv_list.for_extensions(&negotiated); |
129 | | - |
130 | | - // Find user_identity TLV |
131 | | - for tlv in &tlvs { |
132 | | - if tlv.r#type.extension_type == EXTENSION_TYPE_WORKER_HASHRATE_TRACKING |
133 | | - && tlv.r#type.field_type == TLV_FIELD_TYPE_USER_IDENTITY |
134 | | - { |
135 | | - if let Ok(user_identity) = UserIdentity::from_tlv(tlv) { |
136 | | - println!(" Found worker: {}", user_identity); |
| 36 | + match UserIdentity::new(worker_name) { |
| 37 | + Ok(user_id) => { |
| 38 | + println!(" Created: {}", user_id); |
| 39 | + println!(" - Name: {}", user_id.as_str().unwrap()); |
| 40 | + println!(" - Length: {} bytes", user_id.len()); |
| 41 | + println!(" - Raw bytes: {:?}\n", user_id.as_bytes()); |
137 | 42 | } |
138 | | - } |
139 | | - } |
140 | | - |
141 | | - // Demo 2: Parse with TlvList iterator API |
142 | | - println!("\n b) Using iterator API to process TLVs:"); |
143 | | - for result in tlv_list.iter() { |
144 | | - match result { |
145 | | - Ok(tlv) => { |
146 | | - println!( |
147 | | - " - Extension: 0x{:04x}, Field: 0x{:02x}, Length: {} bytes", |
148 | | - tlv.r#type.extension_type, tlv.r#type.field_type, tlv.length |
149 | | - ); |
150 | | - if let Ok(s) = std::str::from_utf8(&tlv.value) { |
151 | | - println!(" Value: \"{}\"", s); |
152 | | - } |
| 43 | + Err(e) => { |
| 44 | + println!(" Error creating worker '{}': {}\n", worker_name, e); |
153 | 45 | } |
154 | | - Err(e) => println!(" - Error parsing TLV: {:?}", e), |
155 | 46 | } |
156 | 47 | } |
157 | 48 |
|
158 | | - // Demo 3: Multiple TLVs (simulating batch processing) |
159 | | - println!("\n c) Processing multiple shares with different workers:"); |
160 | | - let shares_with_workers = vec![ |
161 | | - ("Worker_001", worker1_tlv.clone()), |
162 | | - ("Miner_A", worker2_tlv.clone()), |
163 | | - ]; |
| 49 | + // Step 3: Demonstrate length limits |
| 50 | + println!("3. Length Validation:\n"); |
164 | 51 |
|
165 | | - for (expected, tlv_data) in shares_with_workers { |
166 | | - let tlvs = TlvList::from_bytes(&tlv_data).for_extensions(&negotiated); |
167 | | - for tlv in &tlvs { |
168 | | - if tlv.r#type.extension_type == EXTENSION_TYPE_WORKER_HASHRATE_TRACKING |
169 | | - && tlv.r#type.field_type == TLV_FIELD_TYPE_USER_IDENTITY |
170 | | - { |
171 | | - if let Ok(worker) = UserIdentity::from_tlv(tlv) { |
172 | | - println!(" Share from: {} (expected: {})", worker, expected); |
173 | | - } |
174 | | - } |
| 52 | + // Valid: exactly 32 bytes |
| 53 | + let max_length_name = "x".repeat(32); |
| 54 | + match UserIdentity::new(&max_length_name) { |
| 55 | + Ok(user_id) => { |
| 56 | + println!(" ✓ 32-byte worker name accepted (max length)"); |
| 57 | + println!(" Length: {} bytes\n", user_id.len()); |
| 58 | + } |
| 59 | + Err(e) => { |
| 60 | + println!(" ✗ Unexpected error: {}\n", e); |
175 | 61 | } |
176 | 62 | } |
177 | 63 |
|
178 | | - // Demo 4: Using find() to search for specific TLV |
179 | | - println!("\n d) Find specific TLV field:"); |
180 | | - if let Some(tlv) = TlvList::from_bytes(&worker1_tlv).find( |
181 | | - EXTENSION_TYPE_WORKER_HASHRATE_TRACKING, |
182 | | - TLV_FIELD_TYPE_USER_IDENTITY, |
183 | | - ) { |
184 | | - println!( |
185 | | - " Found user_identity TLV with {} bytes", |
186 | | - tlv.value.len() |
187 | | - ); |
188 | | - if let Ok(s) = std::str::from_utf8(&tlv.value) { |
189 | | - println!(" Worker: \"{}\"", s); |
| 64 | + // Invalid: exceeds 32 bytes |
| 65 | + let too_long_name = "x".repeat(33); |
| 66 | + match UserIdentity::new(&too_long_name) { |
| 67 | + Ok(_) => { |
| 68 | + println!(" ✗ 33-byte worker name should have been rejected!\n"); |
| 69 | + } |
| 70 | + Err(e) => { |
| 71 | + println!(" ✓ 33-byte worker name rejected as expected"); |
| 72 | + println!(" Error: {}\n", e); |
190 | 73 | } |
191 | 74 | } |
192 | 75 |
|
193 | | - println!(); |
| 76 | + // Step 4: Empty worker identity |
| 77 | + println!("4. Edge Cases:\n"); |
194 | 78 |
|
195 | | - // Step 7: Convenience methods for applications |
196 | | - println!("7. Simplified API for Applications:\n"); |
| 79 | + let empty_identity = UserIdentity::new("").unwrap(); |
| 80 | + println!(" Empty identity:"); |
| 81 | + println!(" - Is empty: {}", empty_identity.is_empty()); |
| 82 | + println!(" - Length: {} bytes\n", empty_identity.len()); |
197 | 83 |
|
198 | | - println!(" a) Convert UserIdentity to TLV:"); |
199 | | - let user_identity = UserIdentity::new("Worker_001").unwrap(); |
200 | | - let tlv = TlvField::to_tlv(&user_identity).unwrap(); |
201 | | - println!( |
202 | | - " Created TLV: extension_type=0x{:04x}, field_type=0x{:02x}", |
203 | | - tlv.r#type.extension_type, tlv.r#type.field_type |
204 | | - ); |
| 84 | + // Step 5: Byte-level construction |
| 85 | + println!("5. Creating from Raw Bytes:\n"); |
205 | 86 |
|
206 | | - println!("\n b) Create Vec<Tlv> from UserIdentity:"); |
207 | | - let tlv_vec = vec![TlvField::to_tlv(&user_identity).unwrap()]; |
208 | | - println!(" Created Vec<Tlv> with {} element(s)", tlv_vec.len()); |
209 | | - println!( |
210 | | - " Usage: channel_sender.send((Mining::SubmitSharesExtended(msg), Some(tlv_vec)))?;" |
211 | | - ); |
| 87 | + let raw_bytes = b"CustomWorker"; |
| 88 | + let identity = UserIdentity::from_bytes(raw_bytes).unwrap(); |
| 89 | + println!(" From bytes: {:?}", raw_bytes); |
| 90 | + println!(" Result: {}", identity); |
| 91 | + println!(" As string: {}\n", identity.as_str().unwrap()); |
212 | 92 |
|
213 | | - println!("\n This simplifies the application code:"); |
214 | | - println!(" let user_identity = UserIdentity::new(worker_name)?;"); |
215 | | - println!(" let tlv = TlvField::to_tlv(&user_identity)?;"); |
216 | | - println!(" let tlv_vec = vec![tlv];"); |
| 93 | + // Step 6: Display method |
| 94 | + println!("6. Display Format:\n"); |
217 | 95 |
|
218 | | - println!(); |
| 96 | + let identity = UserIdentity::new("ExampleWorker").unwrap(); |
| 97 | + println!(" Display: {}", identity); |
| 98 | + println!(" Debug: {:?}\n", identity); |
| 99 | + |
| 100 | + // Step 7: Invalid UTF-8 handling |
| 101 | + println!("7. Invalid UTF-8 Handling:\n"); |
| 102 | + |
| 103 | + let invalid_utf8_bytes = vec![0xFF, 0xFE, 0xFD]; |
| 104 | + let invalid_identity = UserIdentity::from_bytes(&invalid_utf8_bytes).unwrap(); |
| 105 | + println!(" Bytes: {:?}", invalid_utf8_bytes); |
| 106 | + println!(" as_str(): {:?}", invalid_identity.as_str()); |
| 107 | + println!( |
| 108 | + " as_string_or_hex(): {}\n", |
| 109 | + invalid_identity.as_string_or_hex() |
| 110 | + ); |
219 | 111 |
|
220 | | - // Step 9: Use case example |
221 | | - println!("9. Use Case - Mining Farm with Multiple Workers:"); |
222 | | - println!(" A mining farm with 100 devices can use a single extended channel"); |
223 | | - println!(" and track per-device hashrate by including the device worker_name in each share."); |
224 | | - println!(" This allows the pool to monitor individual device performance while"); |
225 | | - println!(" reducing connection overhead compared to 100 separate channels.\n"); |
| 112 | + // Step 8: Usage context |
| 113 | + println!("8. Usage in Stratum V2:\n"); |
| 114 | + println!(" When the Worker-Specific Hashrate Tracking extension (0x0002) is negotiated,"); |
| 115 | + println!(" UserIdentity can be included as a TLV field in SubmitSharesExtended messages."); |
| 116 | + println!(" This allows pools to track per-worker hashrate within aggregated channels.\n"); |
| 117 | + println!(" For full TLV encoding/decoding examples, use the parsers_sv2 crate which"); |
| 118 | + println!(" implements the TlvField trait for UserIdentity.\n"); |
226 | 119 |
|
227 | 120 | println!("=== Example Complete ==="); |
228 | 121 | } |
0 commit comments