Skip to main content

casper_rust_wasm_sdk/sdk/binary_port/
mod.rs

1use crate::{types::sdk_error::SdkError, SDK};
2use casper_binary_port::{
3    ConsensusStatus, ConsensusValidatorChanges, GlobalStateQueryResult, LastProgress, NetworkName,
4    NodeStatus, ReactorStateName, RecordId, RewardResponse, SpeculativeExecutionResult,
5    TransactionWithExecutionInfo, Uptime,
6};
7use casper_binary_port_access::{
8    available_block_range, block_header_by_hash, block_header_by_height, block_synchronizer_status,
9    block_with_signatures_by_hash, block_with_signatures_by_height, chainspec_raw_bytes,
10    consensus_status, consensus_validator_changes, delegator_reward_by_block_hash,
11    delegator_reward_by_block_height, delegator_reward_by_era, global_state_item,
12    global_state_item_by_block_hash, global_state_item_by_block_height,
13    global_state_item_by_state_root_hash, last_progress, latest_block_header,
14    latest_block_with_signatures, latest_switch_block_header, network_name, next_upgrade,
15    node_status, peers, protocol_version, reactor_state, read_record, transaction_by_hash,
16    try_accept_transaction, try_speculative_execution, uptime, validator_reward_by_block_hash,
17    validator_reward_by_block_height, validator_reward_by_era,
18};
19use casper_types::{
20    AvailableBlockRange, BlockHash, BlockHeader, BlockSynchronizerStatus, BlockWithSignatures,
21    ChainspecRawBytes, Digest, EraId, Key, NextUpgrade, Peers, ProtocolVersion, PublicKey,
22    Transaction, TransactionHash,
23};
24
25#[cfg(feature = "js")]
26pub mod wasm32;
27
28impl SDK {
29    /// Returns the latest switch block header.
30    pub async fn get_binary_latest_switch_block_header(
31        &self,
32        node_address: Option<String>,
33    ) -> Result<Option<BlockHeader>, SdkError> {
34        let node_address = self.get_node_address(node_address);
35        match latest_switch_block_header(&node_address).await {
36            Ok(block_header) => Ok(block_header),
37            Err(err) => Err(SdkError::CustomError {
38                context: "Failed to retrieve latest switch block header",
39                error: err.to_string(),
40            }),
41        }
42    }
43
44    /// Returns the latest block header.
45    pub async fn get_binary_latest_block_header(
46        &self,
47        node_address: Option<String>,
48    ) -> Result<Option<BlockHeader>, SdkError> {
49        let node_address = self.get_node_address(node_address);
50        match latest_block_header(&node_address).await {
51            Ok(block_header) => Ok(block_header),
52            Err(err) => Err(SdkError::CustomError {
53                context: "Failed to retrieve latest block header",
54                error: err.to_string(),
55            }),
56        }
57    }
58
59    /// Returns the block header at the given height.
60    pub async fn get_binary_block_header_by_height(
61        &self,
62        node_address: Option<String>,
63        height: u64,
64    ) -> Result<Option<BlockHeader>, SdkError> {
65        let node_address = self.get_node_address(node_address);
66        match block_header_by_height(&node_address, height).await {
67            Ok(block_header) => Ok(block_header),
68            Err(err) => Err(SdkError::CustomError {
69                context: "Failed to retrieve block header by height",
70                error: err.to_string(),
71            }),
72        }
73    }
74
75    /// Returns the block header with the given hash.
76    pub async fn get_binary_block_header_by_hash(
77        &self,
78        node_address: Option<String>,
79        block_hash: BlockHash,
80    ) -> Result<Option<BlockHeader>, SdkError> {
81        let node_address = self.get_node_address(node_address);
82        match block_header_by_hash(&node_address, block_hash).await {
83            Ok(block_header) => Ok(block_header),
84            Err(err) => Err(SdkError::CustomError {
85                context: "Failed to retrieve block header by block_hash",
86                error: err.to_string(),
87            }),
88        }
89    }
90
91    /// Returns the latest signed block along with signatures.
92    pub async fn get_binary_latest_block_with_signatures(
93        &self,
94        node_address: Option<String>,
95    ) -> Result<Option<BlockWithSignatures>, SdkError> {
96        let node_address = self.get_node_address(node_address);
97        match latest_block_with_signatures(&node_address).await {
98            Ok(signed_block) => Ok(signed_block),
99            Err(err) => Err(SdkError::CustomError {
100                context: "Failed to retrieve latest signed block",
101                error: err.to_string(),
102            }),
103        }
104    }
105
106    /// Returns the signed block at the given height.
107    pub async fn get_binary_block_with_signatures_by_height(
108        &self,
109        node_address: Option<String>,
110        height: u64,
111    ) -> Result<Option<BlockWithSignatures>, SdkError> {
112        let node_address = self.get_node_address(node_address);
113        match block_with_signatures_by_height(&node_address, height).await {
114            Ok(signed_block) => Ok(signed_block),
115            Err(err) => Err(SdkError::CustomError {
116                context: "Failed to retrieve signed block by height",
117                error: err.to_string(),
118            }),
119        }
120    }
121
122    /// Returns the signed block with the given hash.
123    pub async fn get_binary_block_with_signatures_by_hash(
124        &self,
125        node_address: Option<String>,
126        block_hash: BlockHash,
127    ) -> Result<Option<BlockWithSignatures>, SdkError> {
128        let node_address = self.get_node_address(node_address);
129        match block_with_signatures_by_hash(&node_address, block_hash).await {
130            Ok(signed_block) => Ok(signed_block),
131            Err(err) => Err(SdkError::CustomError {
132                context: "Failed to retrieve signed block by block_hash",
133                error: err.to_string(),
134            }),
135        }
136    }
137
138    /// Returns the transaction by its hash.
139    pub async fn get_binary_transaction_by_hash(
140        &self,
141        node_address: Option<String>,
142        hash: TransactionHash,
143        with_finalized_approvals: bool,
144    ) -> Result<Option<TransactionWithExecutionInfo>, SdkError> {
145        let node_address = self.get_node_address(node_address);
146        match transaction_by_hash(&node_address, hash, with_finalized_approvals).await {
147            Ok(transaction) => Ok(transaction),
148            Err(err) => Err(SdkError::CustomError {
149                context: "Failed to retrieve transaction by hash",
150                error: err.to_string(),
151            }),
152        }
153    }
154
155    /// Returns the peer list.
156    pub async fn get_binary_peers(&self, node_address: Option<String>) -> Result<Peers, SdkError> {
157        let node_address = self.get_node_address(node_address);
158        match peers(&node_address).await {
159            Ok(peers) => Ok(peers),
160            Err(err) => Err(SdkError::CustomError {
161                context: "Failed to retrieve peer list",
162                error: err.to_string(),
163            }),
164        }
165    }
166
167    /// Returns the node uptime.
168    pub async fn get_binary_uptime(
169        &self,
170        node_address: Option<String>,
171    ) -> Result<Uptime, SdkError> {
172        let node_address = self.get_node_address(node_address);
173        match uptime(&node_address).await {
174            Ok(uptime) => Ok(uptime),
175            Err(err) => Err(SdkError::CustomError {
176                context: "Failed to retrieve node uptime",
177                error: err.to_string(),
178            }),
179        }
180    }
181
182    /// Returns the last progress recorded by the node.
183    pub async fn get_binary_last_progress(
184        &self,
185        node_address: Option<String>,
186    ) -> Result<LastProgress, SdkError> {
187        let node_address = self.get_node_address(node_address);
188        match last_progress(&node_address).await {
189            Ok(progress) => Ok(progress),
190            Err(err) => Err(SdkError::CustomError {
191                context: "Failed to retrieve last progress",
192                error: err.to_string(),
193            }),
194        }
195    }
196
197    /// Returns the current reactor state.
198    pub async fn get_binary_reactor_state(
199        &self,
200        node_address: Option<String>,
201    ) -> Result<ReactorStateName, SdkError> {
202        let node_address = self.get_node_address(node_address);
203        match reactor_state(&node_address).await {
204            Ok(state) => Ok(state),
205            Err(err) => Err(SdkError::CustomError {
206                context: "Failed to retrieve reactor state",
207                error: err.to_string(),
208            }),
209        }
210    }
211
212    /// Returns the network name.
213    pub async fn get_binary_network_name(
214        &self,
215        node_address: Option<String>,
216    ) -> Result<NetworkName, SdkError> {
217        let node_address = self.get_node_address(node_address);
218        match network_name(&node_address).await {
219            Ok(network_name) => Ok(network_name),
220            Err(err) => Err(SdkError::CustomError {
221                context: "Failed to retrieve network name",
222                error: err.to_string(),
223            }),
224        }
225    }
226
227    /// Returns the last consensus validator changes.
228    pub async fn get_binary_consensus_validator_changes(
229        &self,
230        node_address: Option<String>,
231    ) -> Result<ConsensusValidatorChanges, SdkError> {
232        let node_address = self.get_node_address(node_address);
233        match consensus_validator_changes(&node_address).await {
234            Ok(changes) => Ok(changes),
235            Err(err) => Err(SdkError::CustomError {
236                context: "Failed to retrieve consensus validator changes",
237                error: err.to_string(),
238            }),
239        }
240    }
241
242    /// Returns the block synchronizer status.
243    pub async fn get_binary_block_synchronizer_status(
244        &self,
245        node_address: Option<String>,
246    ) -> Result<BlockSynchronizerStatus, SdkError> {
247        let node_address = self.get_node_address(node_address);
248        match block_synchronizer_status(&node_address).await {
249            Ok(status) => Ok(status),
250            Err(err) => Err(SdkError::CustomError {
251                context: "Failed to retrieve block synchronizer status",
252                error: err.to_string(),
253            }),
254        }
255    }
256
257    /// Returns the available block range.
258    pub async fn get_binary_available_block_range(
259        &self,
260        node_address: Option<String>,
261    ) -> Result<AvailableBlockRange, SdkError> {
262        let node_address = self.get_node_address(node_address);
263        match available_block_range(&node_address).await {
264            Ok(block_range) => Ok(block_range),
265            Err(err) => Err(SdkError::CustomError {
266                context: "Failed to retrieve available block range",
267                error: err.to_string(),
268            }),
269        }
270    }
271
272    /// Returns information about the next upgrade point.
273    pub async fn get_binary_next_upgrade(
274        &self,
275        node_address: Option<String>,
276    ) -> Result<Option<NextUpgrade>, SdkError> {
277        let node_address = self.get_node_address(node_address);
278        match next_upgrade(&node_address).await {
279            Ok(upgrade) => Ok(upgrade),
280            Err(err) => Err(SdkError::CustomError {
281                context: "Failed to retrieve next upgrade",
282                error: err.to_string(),
283            }),
284        }
285    }
286
287    /// Returns the consensus status.
288    pub async fn get_binary_consensus_status(
289        &self,
290        node_address: Option<String>,
291    ) -> Result<ConsensusStatus, SdkError> {
292        let node_address = self.get_node_address(node_address);
293        match consensus_status(&node_address).await {
294            Ok(status) => Ok(status),
295            Err(err) => Err(SdkError::CustomError {
296                context: "Failed to retrieve consensus status",
297                error: err.to_string(),
298            }),
299        }
300    }
301
302    /// Returns the raw chainspec bytes and additional configuration.
303    pub async fn get_binary_chainspec_raw_bytes(
304        &self,
305        node_address: Option<String>,
306    ) -> Result<ChainspecRawBytes, SdkError> {
307        let node_address = self.get_node_address(node_address);
308        match chainspec_raw_bytes(&node_address).await {
309            Ok(bytes) => Ok(bytes),
310            Err(err) => Err(SdkError::CustomError {
311                context: "Failed to retrieve chainspec raw bytes",
312                error: err.to_string(),
313            }),
314        }
315    }
316
317    /// Retrieves the node status.
318    pub async fn get_binary_node_status(
319        &self,
320        node_address: Option<String>,
321    ) -> Result<NodeStatus, SdkError> {
322        // Get the node address, falling back to self's default if not provided
323        let node_address = self.get_node_address(node_address);
324
325        match node_status(&node_address).await {
326            Ok(status) => Ok(status),
327            Err(err) => Err(SdkError::CustomError {
328                context: "Failed to retrieve node status",
329                error: err.to_string(),
330            }),
331        }
332    }
333
334    /// Retrieves the reward for the given validator at the given era.
335    pub async fn get_binary_validator_reward_by_era(
336        &self,
337        node_address: Option<String>,
338        validator_key: PublicKey,
339        era: EraId,
340    ) -> Result<Option<RewardResponse>, SdkError> {
341        let node_address = self.get_node_address(node_address);
342
343        match validator_reward_by_era(&node_address, validator_key, era).await {
344            Ok(reward) => Ok(reward),
345            Err(err) => Err(SdkError::CustomError {
346                context: "Failed to retrieve validator reward by era",
347                error: err.to_string(),
348            }),
349        }
350    }
351
352    /// Retrieves the reward for the given validator at the era containing the block at given height.
353    pub async fn get_binary_validator_reward_by_block_height(
354        &self,
355        node_address: Option<String>,
356        validator_key: PublicKey,
357        block_height: u64,
358    ) -> Result<Option<RewardResponse>, SdkError> {
359        let node_address = self.get_node_address(node_address);
360
361        match validator_reward_by_block_height(&node_address, validator_key, block_height).await {
362            Ok(reward) => Ok(reward),
363            Err(err) => Err(SdkError::CustomError {
364                context: "Failed to retrieve validator reward by block height",
365                error: err.to_string(),
366            }),
367        }
368    }
369
370    /// Retrieves the reward for the given validator at the era containing the block with given hash.
371    pub async fn get_binary_validator_reward_by_block_hash(
372        &self,
373        node_address: Option<String>,
374        validator_key: PublicKey,
375        block_hash: BlockHash,
376    ) -> Result<Option<RewardResponse>, SdkError> {
377        let node_address = self.get_node_address(node_address);
378
379        match validator_reward_by_block_hash(&node_address, validator_key, block_hash).await {
380            Ok(reward) => Ok(reward),
381            Err(err) => Err(SdkError::CustomError {
382                context: "Failed to retrieve validator reward by block hash",
383                error: err.to_string(),
384            }),
385        }
386    }
387
388    /// Retrieves the reward for the given delegator at the given era.
389    pub async fn get_binary_delegator_reward_by_era(
390        &self,
391        node_address: Option<String>,
392        validator_key: PublicKey,
393        delegator_key: PublicKey,
394        era: EraId,
395    ) -> Result<Option<RewardResponse>, SdkError> {
396        let node_address = self.get_node_address(node_address);
397
398        match delegator_reward_by_era(&node_address, validator_key, delegator_key, era).await {
399            Ok(reward) => Ok(reward),
400            Err(err) => Err(SdkError::CustomError {
401                context: "Failed to retrieve delegator reward by era",
402                error: err.to_string(),
403            }),
404        }
405    }
406
407    /// Retrieves the reward for the given delegator at the era containing the block at given height.
408    pub async fn get_binary_delegator_reward_by_block_height(
409        &self,
410        node_address: Option<String>,
411        validator_key: PublicKey,
412        delegator_key: PublicKey,
413        block_height: u64,
414    ) -> Result<Option<RewardResponse>, SdkError> {
415        let node_address = self.get_node_address(node_address);
416
417        match delegator_reward_by_block_height(
418            &node_address,
419            validator_key,
420            delegator_key,
421            block_height,
422        )
423        .await
424        {
425            Ok(reward) => Ok(reward),
426            Err(err) => Err(SdkError::CustomError {
427                context: "Failed to retrieve delegator reward by block height",
428                error: err.to_string(),
429            }),
430        }
431    }
432
433    /// Retrieves the reward for the given delegator at the era containing the block with given hash.
434    pub async fn get_binary_delegator_reward_by_block_hash(
435        &self,
436        node_address: Option<String>,
437        validator_key: PublicKey,
438        delegator_key: PublicKey,
439        block_hash: BlockHash,
440    ) -> Result<Option<RewardResponse>, SdkError> {
441        let node_address = self.get_node_address(node_address);
442
443        match delegator_reward_by_block_hash(
444            &node_address,
445            validator_key,
446            delegator_key,
447            block_hash,
448        )
449        .await
450        {
451            Ok(reward) => Ok(reward),
452            Err(err) => Err(SdkError::CustomError {
453                context: "Failed to retrieve delegator reward by block hash",
454                error: err.to_string(),
455            }),
456        }
457    }
458
459    /// Reads a record from the node.
460    pub async fn get_binary_read_record(
461        &self,
462        node_address: Option<String>,
463        record_id: RecordId,
464        key: &[u8],
465    ) -> Result<Vec<u8>, SdkError> {
466        // Check if the key is empty and return an error if it is
467        if key.is_empty() {
468            return Err(SdkError::CustomError {
469                context: "Failed to read record",
470                error: "Key cannot be an empty array".to_string(),
471            });
472        }
473
474        let node_address = self.get_node_address(node_address);
475
476        match read_record(&node_address, record_id, key).await {
477            Ok(record) => Ok(record),
478            Err(err) => Err(SdkError::CustomError {
479                context: "Failed to read record",
480                error: err.to_string(),
481            }),
482        }
483    }
484
485    /// Retrieves an item from the global state using the most recent state root hash.
486    pub async fn get_binary_global_state_item(
487        &self,
488        node_address: Option<String>,
489        key: Key,
490        path: Vec<String>,
491    ) -> Result<Option<GlobalStateQueryResult>, SdkError> {
492        let node_address = self.get_node_address(node_address);
493        match global_state_item(&node_address, key, path).await {
494            Ok(item) => Ok(item),
495            Err(err) => Err(SdkError::CustomError {
496                context: "Failed to retrieve global state item",
497                error: err.to_string(),
498            }),
499        }
500    }
501
502    /// Retrieves an item from the global state using a specific state root hash.
503    pub async fn get_binary_global_state_item_by_state_root_hash(
504        &self,
505        node_address: Option<String>,
506        state_root_hash: Digest,
507        key: Key,
508        path: Vec<String>,
509    ) -> Result<Option<GlobalStateQueryResult>, SdkError> {
510        let node_address = self.get_node_address(node_address);
511        match global_state_item_by_state_root_hash(&node_address, state_root_hash, key, path).await
512        {
513            Ok(item) => Ok(item),
514            Err(err) => Err(SdkError::CustomError {
515                context: "Failed to retrieve global state item by state root hash",
516                error: err.to_string(),
517            }),
518        }
519    }
520
521    /// Retrieves an item from the global state using a block hash.
522    pub async fn get_binary_global_state_item_by_block_hash(
523        &self,
524        node_address: Option<String>,
525        block_hash: BlockHash,
526        key: Key,
527        path: Vec<String>,
528    ) -> Result<Option<GlobalStateQueryResult>, SdkError> {
529        let node_address = self.get_node_address(node_address);
530        match global_state_item_by_block_hash(&node_address, block_hash, key, path).await {
531            Ok(item) => Ok(item),
532            Err(err) => Err(SdkError::CustomError {
533                context: "Failed to retrieve global state item by block hash",
534                error: err.to_string(),
535            }),
536        }
537    }
538
539    /// Retrieves an item from the global state using a block height.
540    pub async fn get_binary_global_state_item_by_block_height(
541        &self,
542        node_address: Option<String>,
543        block_height: u64,
544        key: Key,
545        path: Vec<String>,
546    ) -> Result<Option<GlobalStateQueryResult>, SdkError> {
547        let node_address = self.get_node_address(node_address);
548        match global_state_item_by_block_height(&node_address, block_height, key, path).await {
549            Ok(item) => Ok(item),
550            Err(err) => Err(SdkError::CustomError {
551                context: "Failed to retrieve global state item by block height",
552                error: err.to_string(),
553            }),
554        }
555    }
556
557    /// Attempts to send a transaction to the node for inclusion.
558    pub async fn get_binary_try_accept_transaction(
559        &self,
560        node_address: Option<String>,
561        transaction: Transaction,
562    ) -> Result<(), SdkError> {
563        let node_address = self.get_node_address(node_address);
564        match try_accept_transaction(&node_address, transaction).await {
565            Ok(_) => Ok(()),
566            Err(err) => Err(SdkError::CustomError {
567                context: "Failed to accept transaction",
568                error: err.to_string(),
569            }),
570        }
571    }
572
573    /// Attempts to send a transaction to the node for speculative execution.
574    pub async fn get_binary_try_speculative_execution(
575        &self,
576        node_address: Option<String>,
577        transaction: Transaction,
578    ) -> Result<SpeculativeExecutionResult, SdkError> {
579        let node_address = self.get_node_address(node_address);
580        match try_speculative_execution(&node_address, transaction).await {
581            Ok(result) => Ok(result),
582            Err(err) => Err(SdkError::CustomError {
583                context: "Failed to perform speculative execution",
584                error: err.to_string(),
585            }),
586        }
587    }
588
589    /// Retrieves the protocol version from the node.
590    pub async fn get_binary_protocol_version(
591        &self,
592        node_address: Option<String>,
593    ) -> Result<ProtocolVersion, SdkError> {
594        let node_address = self.get_node_address(node_address);
595        match protocol_version(&node_address).await {
596            Ok(version) => Ok(version),
597            Err(err) => Err(SdkError::CustomError {
598                context: "Failed to retrieve protocol version",
599                error: err.to_string(),
600            }),
601        }
602    }
603}
604
605#[cfg(test)]
606mod tests {
607    use crate::{
608        helpers::{public_key_from_secret_key, secret_key_from_pem},
609        types::transaction_params::transaction_str_params::TransactionStrParams,
610    };
611
612    use super::*;
613    use casper_types::bytesrepr::ToBytes;
614    use sdk_tests::{
615        config::TRANSFER_AMOUNT,
616        tests::helpers::{
617            get_network_constants, get_node_validator_secret_key, get_user_secret_key,
618        },
619    };
620
621    /// Public key of the node we talk to over binary port (no PEM required; CI-safe).
622    async fn node_validator_public_key(sdk: &SDK, node_address: String) -> PublicKey {
623        let status = sdk
624            .get_binary_consensus_status(Some(node_address))
625            .await
626            .expect("consensus status");
627        status.validator_public_key().clone()
628    }
629
630    /// Assert reward RPC succeeded. `None` is valid on short-lived NCTL before rewards accrue.
631    fn assert_reward_rpc_ok(reward: Option<RewardResponse>) {
632        if let Some(reward) = reward {
633            let debug = format!("{reward:?}");
634            assert!(
635                !debug.is_empty(),
636                "reward response should serialize when present"
637            );
638        }
639    }
640
641    #[tokio::test]
642    async fn test_get_binary_latest_switch_block_header_success() {
643        let sdk = SDK::new(None, None, None);
644        let (_, _, _, node_address, _) = get_network_constants();
645
646        let result = sdk
647            .get_binary_latest_switch_block_header(Some(node_address))
648            .await;
649
650        let block_header = result.unwrap();
651        assert!(block_header.is_some());
652        let block_header = block_header.unwrap();
653
654        assert!(!block_header.body_hash().to_string().is_empty());
655        assert!(block_header.height() > 0);
656    }
657
658    #[tokio::test]
659    async fn test_get_binary_latest_block_header_success() {
660        let sdk = SDK::new(None, None, None);
661        let (_, _, _, node_address, _) = get_network_constants();
662
663        let result = sdk.get_binary_latest_block_header(Some(node_address)).await;
664
665        let block_header = result.unwrap();
666        assert!(block_header.is_some());
667        let block_header = block_header.unwrap();
668
669        assert!(!block_header.body_hash().to_string().is_empty());
670        assert!(block_header.height() > 0);
671    }
672
673    #[tokio::test]
674    async fn test_get_binary_block_header_by_height_success() {
675        let sdk = SDK::new(None, None, None);
676        let (_, _, _, node_address, _) = get_network_constants();
677        let block_height = 1; // Sample block height
678
679        let result = sdk
680            .get_binary_block_header_by_height(Some(node_address), block_height)
681            .await;
682
683        let block_header = result.unwrap();
684        assert!(block_header.is_some());
685        let block_header = block_header.unwrap();
686
687        assert!(!block_header.body_hash().to_string().is_empty());
688        assert_eq!(block_header.height(), block_height);
689    }
690
691    #[tokio::test]
692    async fn test_get_binary_block_header_by_hash_success() {
693        let sdk = SDK::new(None, None, None);
694        let (_, _, _, node_address, _) = get_network_constants();
695        let block_height = 1; // Sample block height
696
697        let result = sdk
698            .get_binary_block_header_by_height(Some(node_address.clone()), block_height)
699            .await;
700
701        let block_header = result.unwrap();
702        assert!(block_header.is_some());
703        let block_header = block_header.unwrap();
704        let block_hash = block_header.block_hash();
705
706        assert!(!block_hash.to_string().is_empty());
707
708        let result = sdk
709            .get_binary_block_header_by_hash(Some(node_address), block_hash)
710            .await;
711
712        let block_header = result.unwrap();
713        assert!(block_header.is_some());
714        let block_header = block_header.unwrap();
715
716        assert_eq!(
717            block_header.block_hash().to_hex_string(),
718            block_hash.to_hex_string()
719        );
720    }
721
722    #[tokio::test]
723    async fn test_get_binary_latest_block_with_signatures_success() {
724        let sdk = SDK::new(None, None, None);
725        let (_, _, _, node_address, _) = get_network_constants();
726
727        let result = sdk
728            .get_binary_latest_block_with_signatures(Some(node_address))
729            .await;
730        let signed_block = result.unwrap();
731        assert!(signed_block.is_some());
732        let signed_block = signed_block.unwrap();
733        assert!(!signed_block.block().hash().to_string().is_empty());
734        assert!(signed_block.block().height() > 0);
735    }
736
737    #[tokio::test]
738    async fn test_get_binary_block_with_signatures_by_height_success() {
739        let sdk = SDK::new(None, None, None);
740        let (_, _, _, node_address, _) = get_network_constants();
741        let block_height = 1;
742
743        let result = sdk
744            .get_binary_block_with_signatures_by_height(Some(node_address), block_height)
745            .await;
746        let signed_block = result.unwrap();
747        assert!(signed_block.is_some());
748        let signed_block = signed_block.unwrap();
749        assert_eq!(signed_block.block().height(), block_height);
750        assert!(!signed_block.block().hash().to_string().is_empty());
751    }
752
753    #[tokio::test]
754    async fn test_get_binary_block_with_signatures_by_hash_success() {
755        let sdk = SDK::new(None, None, None);
756        let (_, _, _, node_address, _) = get_network_constants();
757        let block_height = 1;
758
759        let result = sdk
760            .get_binary_block_with_signatures_by_height(Some(node_address.clone()), block_height)
761            .await;
762        let signed_block = result.unwrap();
763        assert!(signed_block.is_some());
764        let signed_block = signed_block.unwrap();
765        assert_eq!(signed_block.block().height(), block_height);
766        let block_hash = signed_block.block().hash();
767        assert!(!block_hash.to_string().is_empty());
768
769        let result = sdk
770            .get_binary_block_with_signatures_by_hash(Some(node_address), *block_hash)
771            .await;
772
773        let signed_block = result.unwrap();
774        assert!(signed_block.is_some());
775        let signed_block = signed_block.unwrap();
776        assert_eq!(
777            signed_block.block().hash().to_hex_string(),
778            block_hash.to_hex_string()
779        );
780    }
781
782    #[tokio::test]
783    async fn test_get_binary_transaction_by_hash_success() {
784        let sdk = SDK::new(None, None, None);
785        let (rpc_address, _, _, node_address, chain_name) = get_network_constants();
786
787        let secret_key = get_user_secret_key(None).unwrap();
788        let initiator_addr = public_key_from_secret_key(&secret_key).unwrap();
789
790        let transaction_params = TransactionStrParams::default();
791        transaction_params.set_secret_key(&secret_key);
792        transaction_params.set_chain_name(&chain_name);
793        transaction_params.set_payment_amount(TRANSFER_AMOUNT);
794
795        let transfer = sdk
796            .transfer_transaction(
797                None,
798                &initiator_addr, // self transfer
799                TRANSFER_AMOUNT,
800                transaction_params,
801                None,
802                None,
803                Some(rpc_address.clone()),
804            )
805            .await
806            .unwrap();
807        let transaction_hash = transfer.result.transaction_hash;
808        assert!(!transaction_hash.to_string().is_empty());
809
810        let result = sdk
811            .get_binary_transaction_by_hash(Some(node_address), transaction_hash, false)
812            .await;
813
814        let transaction = result.unwrap();
815        assert!(transaction.is_some());
816        let transaction = transaction.unwrap();
817        assert_eq!(
818            transaction.into_inner().0.hash().to_hex_string(),
819            transaction_hash.to_hex_string()
820        );
821    }
822
823    #[tokio::test]
824    async fn test_get_binary_peers_success() {
825        let sdk = SDK::new(None, None, None);
826        let (_, _, _, node_address, _) = get_network_constants();
827
828        let result = sdk.get_binary_peers(Some(node_address)).await;
829        let peers = result.unwrap();
830        assert!(!peers.into_inner().is_empty());
831    }
832
833    #[tokio::test]
834    async fn test_get_binary_uptime_success() {
835        let sdk = SDK::new(None, None, None);
836        let (_, _, _, node_address, _) = get_network_constants();
837
838        let result = sdk.get_binary_uptime(Some(node_address)).await;
839        let uptime = result.unwrap();
840        assert!(!uptime.into_inner().to_string().is_empty());
841    }
842
843    #[tokio::test]
844    async fn test_get_binary_last_progress_success() {
845        let sdk = SDK::new(None, None, None);
846        let (_, _, _, node_address, _) = get_network_constants();
847
848        let result = sdk.get_binary_last_progress(Some(node_address)).await;
849        let progress = result.unwrap();
850        assert!(!progress.into_inner().to_string().is_empty());
851    }
852
853    #[tokio::test]
854    async fn test_get_binary_reactor_state_success() {
855        let sdk = SDK::new(None, None, None);
856        let (_, _, _, node_address, _) = get_network_constants();
857
858        let result = sdk.get_binary_reactor_state(Some(node_address)).await;
859        let state = result.unwrap();
860        assert!(!state.into_inner().to_string().is_empty());
861    }
862
863    #[tokio::test]
864    async fn test_get_binary_network_name_success() {
865        let sdk = SDK::new(None, None, None);
866        let (_, _, _, node_address, _) = get_network_constants();
867
868        let result = sdk.get_binary_network_name(Some(node_address)).await;
869        let network_name = result.unwrap();
870        assert!(!network_name.into_inner().to_string().is_empty());
871    }
872
873    #[tokio::test]
874    async fn test_get_binary_consensus_validator_changes_success() {
875        let sdk = SDK::new(None, None, None);
876        let (_, _, _, node_address, _) = get_network_constants();
877
878        let result = sdk
879            .get_binary_consensus_validator_changes(Some(node_address))
880            .await;
881        let changes = result.unwrap();
882        // Fresh NCTL has no validator churn yet.
883        assert!(changes.into_inner().is_empty());
884    }
885
886    #[tokio::test]
887    async fn test_get_binary_block_synchronizer_status_success() {
888        let sdk = SDK::new(None, None, None);
889        let (_, _, _, node_address, _) = get_network_constants();
890
891        let result = sdk
892            .get_binary_block_synchronizer_status(Some(node_address))
893            .await;
894        let status = result.unwrap();
895        assert!(!status.to_bytes().unwrap().is_empty());
896    }
897
898    #[tokio::test]
899    async fn test_get_binary_available_block_range_success() {
900        let sdk = SDK::new(None, None, None);
901        let (_, _, _, node_address, _) = get_network_constants();
902
903        let result = sdk
904            .get_binary_available_block_range(Some(node_address))
905            .await;
906        let block_range = result.unwrap();
907        assert!(block_range.low() <= block_range.high());
908    }
909
910    #[tokio::test]
911    async fn test_get_binary_next_upgrade_success() {
912        let sdk = SDK::new(None, None, None);
913        let (_, _, _, node_address, _) = get_network_constants();
914
915        let result = sdk.get_binary_next_upgrade(Some(node_address)).await;
916        let upgrade = result.unwrap();
917        // Fresh NCTL is not mid-upgrade.
918        assert!(upgrade.is_none());
919    }
920
921    #[tokio::test]
922    async fn test_get_binary_consensus_status_success() {
923        let sdk = SDK::new(None, None, None);
924        let (_, _, _, node_address, _) = get_network_constants();
925
926        let result = sdk.get_binary_consensus_status(Some(node_address)).await;
927        let status = result.unwrap();
928        assert!(!status.validator_public_key().to_string().is_empty());
929        assert!(status.round_length().is_some())
930    }
931
932    #[tokio::test]
933    async fn test_get_binary_chainspec_raw_bytes_success() {
934        let sdk = SDK::new(None, None, None);
935        let (_, _, _, node_address, _) = get_network_constants();
936
937        let result = sdk.get_binary_chainspec_raw_bytes(Some(node_address)).await;
938        let chainspec = result.unwrap();
939        assert!(!chainspec.chainspec_bytes().is_empty());
940    }
941
942    #[tokio::test]
943    async fn test_get_binary_node_status_success() {
944        let sdk = SDK::new(None, None, None);
945        let (_, _, _, node_address, _) = get_network_constants();
946
947        let get_binary_node_status = sdk.get_binary_node_status(Some(node_address)).await;
948
949        let get_binary_node_status = get_binary_node_status.unwrap();
950        assert!(!get_binary_node_status
951            .protocol_version
952            .to_string()
953            .is_empty());
954        assert!(!get_binary_node_status.chainspec_name.to_string().is_empty());
955    }
956
957    #[tokio::test]
958    async fn test_get_binary_validator_reward_by_era_success() {
959        let sdk = SDK::new(None, None, None);
960        let (_, _, _, node_address, _) = get_network_constants();
961        let validator_key = node_validator_public_key(&sdk, node_address.clone()).await;
962        let era = EraId::new(1);
963
964        let result = sdk
965            .get_binary_validator_reward_by_era(Some(node_address), validator_key, era)
966            .await;
967
968        assert_reward_rpc_ok(result.unwrap());
969    }
970
971    #[tokio::test]
972    async fn test_get_binary_validator_reward_by_block_height_success() {
973        let sdk = SDK::new(None, None, None);
974        let (_, _, _, node_address, _) = get_network_constants();
975        let validator_key = node_validator_public_key(&sdk, node_address.clone()).await;
976        let block_height = 1;
977
978        let result = sdk
979            .get_binary_validator_reward_by_block_height(
980                Some(node_address),
981                validator_key,
982                block_height,
983            )
984            .await;
985
986        assert_reward_rpc_ok(result.unwrap());
987    }
988
989    #[tokio::test]
990    async fn test_get_binary_validator_reward_by_block_hash_success() {
991        let sdk = SDK::new(None, None, None);
992        let (_, _, _, node_address, _) = get_network_constants();
993        let validator_key = node_validator_public_key(&sdk, node_address.clone()).await;
994
995        let block_height = 1;
996
997        let result = sdk
998            .get_binary_block_with_signatures_by_height(Some(node_address.clone()), block_height)
999            .await;
1000        let signed_block = result.unwrap();
1001        assert!(signed_block.is_some());
1002        let signed_block = signed_block.unwrap();
1003        assert_eq!(signed_block.block().height(), block_height);
1004        let block_hash = signed_block.block().hash();
1005        assert!(!block_hash.to_string().is_empty());
1006
1007        let result = sdk
1008            .get_binary_validator_reward_by_block_hash(
1009                Some(node_address),
1010                validator_key,
1011                *block_hash,
1012            )
1013            .await;
1014
1015        assert_reward_rpc_ok(result.unwrap());
1016    }
1017
1018    #[tokio::test]
1019    async fn test_get_binary_delegator_reward_by_era_success() {
1020        let sdk = SDK::new(None, None, None);
1021        let (_, _, _, node_address, _) = get_network_constants();
1022        let validator_key = node_validator_public_key(&sdk, node_address.clone()).await;
1023
1024        let secret_key = get_user_secret_key(Some("user-2")).unwrap();
1025        let delegator_secret_key_from_pem = secret_key_from_pem(&secret_key).unwrap();
1026        let delegator_key = PublicKey::from(&delegator_secret_key_from_pem);
1027
1028        let era = EraId::new(1);
1029
1030        let result = sdk
1031            .get_binary_delegator_reward_by_era(
1032                Some(node_address),
1033                validator_key,
1034                delegator_key,
1035                era,
1036            )
1037            .await;
1038
1039        assert_reward_rpc_ok(result.unwrap());
1040    }
1041
1042    #[tokio::test]
1043    async fn test_get_binary_delegator_reward_by_block_height_success() {
1044        let sdk = SDK::new(None, None, None);
1045        let (_, _, _, node_address, _) = get_network_constants();
1046        let validator_key = node_validator_public_key(&sdk, node_address.clone()).await;
1047
1048        let secret_key = get_user_secret_key(Some("user-2")).unwrap();
1049        let delegator_secret_key_from_pem = secret_key_from_pem(&secret_key).unwrap();
1050        let delegator_key = PublicKey::from(&delegator_secret_key_from_pem);
1051        let block_height = 1;
1052
1053        let result = sdk
1054            .get_binary_delegator_reward_by_block_height(
1055                Some(node_address),
1056                validator_key,
1057                delegator_key,
1058                block_height,
1059            )
1060            .await;
1061
1062        assert_reward_rpc_ok(result.unwrap());
1063    }
1064
1065    #[tokio::test]
1066    async fn test_get_binary_delegator_reward_by_block_hash_success() {
1067        let sdk = SDK::new(None, None, None);
1068        let (_, _, _, node_address, _) = get_network_constants();
1069        let validator_key = node_validator_public_key(&sdk, node_address.clone()).await;
1070
1071        let secret_key = get_user_secret_key(Some("user-2")).unwrap();
1072        let delegator_secret_key_from_pem = secret_key_from_pem(&secret_key).unwrap();
1073        let delegator_key = PublicKey::from(&delegator_secret_key_from_pem);
1074
1075        let block_height = 1;
1076
1077        let result = sdk
1078            .get_binary_block_with_signatures_by_height(Some(node_address.clone()), block_height)
1079            .await;
1080        let signed_block = result.unwrap();
1081        assert!(signed_block.is_some());
1082        let signed_block = signed_block.unwrap();
1083        assert_eq!(signed_block.block().height(), block_height);
1084        let block_hash = signed_block.block().hash();
1085        assert!(!block_hash.to_string().is_empty());
1086
1087        let result = sdk
1088            .get_binary_delegator_reward_by_block_hash(
1089                Some(node_address),
1090                validator_key,
1091                delegator_key,
1092                *block_hash,
1093            )
1094            .await;
1095
1096        assert_reward_rpc_ok(result.unwrap());
1097    }
1098
1099    #[tokio::test]
1100    async fn test_node_validator_pem_matches_consensus_when_available() {
1101        let sdk = SDK::new(None, None, None);
1102        let (_, _, _, node_address, _) = get_network_constants();
1103        let from_status = node_validator_public_key(&sdk, node_address).await;
1104
1105        // CI mounts users only; local NCTL may expose node PEMs.
1106        if let Ok(pem) = get_node_validator_secret_key(Some("node-1")) {
1107            let from_pem = PublicKey::from(&secret_key_from_pem(&pem).unwrap());
1108            assert_eq!(from_pem.to_string(), from_status.to_string());
1109        }
1110    }
1111
1112    #[tokio::test]
1113    async fn test_get_binary_read_record_success() {
1114        let sdk = SDK::new(None, None, None);
1115        let (_, _, _, node_address, _) = get_network_constants();
1116        // Negative: garbage key yields empty bytes for BlockHeader record id.
1117        let record_id = RecordId::BlockHeader;
1118        let key = b"record_key";
1119
1120        let result = sdk
1121            .get_binary_read_record(Some(node_address.clone()), record_id, key)
1122            .await;
1123
1124        let record = result.unwrap();
1125        assert!(record.is_empty());
1126
1127        // Positive: block hash bytes for height 1 should return a header record.
1128        let header = sdk
1129            .get_binary_block_header_by_height(Some(node_address.clone()), 1)
1130            .await
1131            .unwrap()
1132            .expect("block header at height 1");
1133        let hash = header.block_hash();
1134        let key = hash.as_ref();
1135        let result = sdk
1136            .get_binary_read_record(Some(node_address), RecordId::BlockHeader, key)
1137            .await;
1138        let record = result.unwrap();
1139        assert!(
1140            !record.is_empty(),
1141            "BlockHeader record for a known block hash should be non-empty"
1142        );
1143    }
1144
1145    #[tokio::test]
1146    async fn test_get_binary_global_state_item_success() {
1147        let sdk = SDK::new(None, None, None);
1148        let (_, _, _, node_address, _) = get_network_constants();
1149
1150        let secret_key = get_user_secret_key(None).unwrap();
1151        let validator_secret_key_from_pem = secret_key_from_pem(&secret_key).unwrap();
1152        let validator_key = PublicKey::from(&validator_secret_key_from_pem);
1153
1154        let key = Key::Account(validator_key.to_account_hash());
1155
1156        let path = vec![];
1157
1158        let result = sdk
1159            .get_binary_global_state_item(Some(node_address), key, path)
1160            .await;
1161        let item = result.unwrap();
1162        assert!(item.is_some());
1163    }
1164
1165    #[tokio::test]
1166    async fn test_get_binary_global_state_item_by_state_root_hash_success() {
1167        let sdk = SDK::new(None, None, None);
1168        let (_, _, _, node_address, _) = get_network_constants();
1169
1170        let secret_key = get_user_secret_key(None).unwrap();
1171        let validator_secret_key_from_pem = secret_key_from_pem(&secret_key).unwrap();
1172        let validator_key = PublicKey::from(&validator_secret_key_from_pem);
1173
1174        let key = Key::Account(validator_key.to_account_hash());
1175        let path = vec![];
1176        let height = 1;
1177
1178        let result = sdk
1179            .get_binary_block_header_by_height(Some(node_address.clone()), height)
1180            .await;
1181
1182        let block_header = result.unwrap();
1183        assert!(block_header.is_some());
1184        let block_header = block_header.unwrap();
1185        let state_root_hash = block_header.state_root_hash();
1186
1187        assert!(!state_root_hash.to_string().is_empty());
1188
1189        let result = sdk
1190            .get_binary_global_state_item_by_state_root_hash(
1191                Some(node_address),
1192                *state_root_hash,
1193                key,
1194                path,
1195            )
1196            .await;
1197        let item = result.unwrap();
1198        assert!(item.is_some());
1199    }
1200
1201    #[tokio::test]
1202    async fn test_get_binary_global_state_item_by_block_hash_success() {
1203        let sdk = SDK::new(None, None, None);
1204        let (_, _, _, node_address, _) = get_network_constants();
1205        let secret_key = get_user_secret_key(None).unwrap();
1206        let validator_secret_key_from_pem = secret_key_from_pem(&secret_key).unwrap();
1207        let validator_key = PublicKey::from(&validator_secret_key_from_pem);
1208
1209        let key = Key::Account(validator_key.to_account_hash());
1210        let path = vec![];
1211
1212        let block_height = 1;
1213
1214        let result = sdk
1215            .get_binary_block_header_by_height(Some(node_address.clone()), block_height)
1216            .await;
1217
1218        let block_header = result.unwrap();
1219        assert!(block_header.is_some());
1220        let block_header = block_header.unwrap();
1221        let block_hash = block_header.block_hash();
1222
1223        assert!(!block_hash.to_string().is_empty());
1224
1225        let result = sdk
1226            .get_binary_global_state_item_by_block_hash(Some(node_address), block_hash, key, path)
1227            .await;
1228        let item = result.unwrap();
1229        assert!(item.is_some());
1230    }
1231
1232    #[tokio::test]
1233    async fn test_get_binary_global_state_item_by_block_height_success() {
1234        let sdk = SDK::new(None, None, None);
1235        let (_, _, _, node_address, _) = get_network_constants();
1236        let secret_key = get_user_secret_key(None).unwrap();
1237        let validator_secret_key_from_pem = secret_key_from_pem(&secret_key).unwrap();
1238        let validator_key = PublicKey::from(&validator_secret_key_from_pem);
1239
1240        let key = Key::Account(validator_key.to_account_hash());
1241        let path = vec![];
1242
1243        let block_height = 1;
1244
1245        let result = sdk
1246            .get_binary_global_state_item_by_block_height(
1247                Some(node_address),
1248                block_height,
1249                key,
1250                path,
1251            )
1252            .await;
1253        let item = result.unwrap();
1254        assert!(item.is_some());
1255    }
1256
1257    #[tokio::test]
1258    async fn test_get_binary_try_accept_transaction_success() {
1259        let sdk = SDK::new(None, None, None);
1260        let (_, _, _, node_address, chain_name) = get_network_constants();
1261
1262        let secret_key = get_user_secret_key(None).unwrap();
1263        let initiator_addr = public_key_from_secret_key(&secret_key).unwrap();
1264
1265        let transaction_params = TransactionStrParams::default();
1266        transaction_params.set_secret_key(&secret_key);
1267        transaction_params.set_chain_name(&chain_name);
1268        transaction_params.set_payment_amount(TRANSFER_AMOUNT);
1269
1270        let make_transfer_transaction = sdk
1271            .make_transfer_transaction(
1272                None,
1273                &initiator_addr, // self transfer
1274                TRANSFER_AMOUNT,
1275                transaction_params,
1276                None,
1277            )
1278            .unwrap();
1279        assert!(!make_transfer_transaction.hash().to_string().is_empty());
1280
1281        let result = sdk
1282            .get_binary_try_accept_transaction(Some(node_address), make_transfer_transaction.into())
1283            .await;
1284
1285        assert!(result.is_ok());
1286    }
1287
1288    #[tokio::test]
1289    async fn test_get_binary_try_speculative_execution_rejects_v1_transfer() {
1290        let sdk = SDK::new(None, None, None);
1291        let (_, _, _, node_address, chain_name) = get_network_constants();
1292
1293        let secret_key = get_user_secret_key(None).unwrap();
1294        let initiator_addr = public_key_from_secret_key(&secret_key).unwrap();
1295
1296        let transaction_params = TransactionStrParams::default();
1297        transaction_params.set_secret_key(&secret_key);
1298        transaction_params.set_chain_name(&chain_name);
1299        transaction_params.set_payment_amount(TRANSFER_AMOUNT);
1300
1301        let make_transfer_transaction = sdk
1302            .make_transfer_transaction(
1303                None,
1304                &initiator_addr, // self transfer
1305                TRANSFER_AMOUNT,
1306                transaction_params,
1307                None,
1308            )
1309            .unwrap();
1310        assert!(!make_transfer_transaction.hash().to_string().is_empty());
1311
1312        let result = sdk
1313            .get_binary_try_speculative_execution(
1314                Some(node_address),
1315                make_transfer_transaction.into(),
1316            )
1317            .await;
1318
1319        // Binary speculative exec on current NCTL rejects TransactionV1 payloads.
1320        let err = result.expect_err("expected V1 speculative rejection");
1321        let err = err.to_string();
1322        assert!(
1323            err.contains("v1 transaction") || err.contains("speculative"),
1324            "unexpected speculative error: {err}"
1325        );
1326    }
1327
1328    #[tokio::test]
1329    async fn test_get_binary_protocol_version_success() {
1330        let sdk = SDK::new(None, None, None);
1331        let (_, _, _, node_address, _) = get_network_constants();
1332
1333        let result = sdk.get_binary_protocol_version(Some(node_address)).await;
1334        let protocol_version = result.unwrap();
1335        assert!(!protocol_version.value().to_string().is_empty());
1336    }
1337}