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
//! Strongly typed helper functions for communicating with the Node's
//! RPC endpoint.

use crate::strip_0x_prefix;
use anyhow::anyhow;
use jsonrpsee::{core::client::ClientT, http_client::HttpClient, rpc_params};
use parity_scale_codec::{Decode, Encode};
use sp_core::H256;
use tuxedo_core::{
    types::{OpaqueBlock, Output, OutputRef},
    TuxedoMetadata, Verifier,
};

/// Get the node's metadata
pub async fn node_get_metadata(client: &HttpClient) -> anyhow::Result<TuxedoMetadata> {
    // Don't provide a block height to use the best height.
    let params = rpc_params![Option::<u32>::None];
    let rpc_response: Option<String> = client.request("state_getMetadata", params).await?;
    let metadata = metadata_from_string(&rpc_response.expect("metadata should be available."))?;
    Ok(metadata)
}

/// Parse a string into a Tuxedo Metadata
pub(crate) fn metadata_from_string(s: &str) -> anyhow::Result<TuxedoMetadata> {
    let s = strip_0x_prefix(s);
    let bytes = hex::decode(s)?;
    Ok(TuxedoMetadata::decode(&mut &bytes[..])?)
}

/// Typed helper to get the Node's block hash at a particular height
pub async fn node_get_block_hash(height: u32, client: &HttpClient) -> anyhow::Result<Option<H256>> {
    let params = rpc_params![Some(height)];
    let rpc_response: Option<String> = client.request("chain_getBlockHash", params).await?;
    let maybe_hash = rpc_response.map(|s| crate::h256_from_string(&s).unwrap());
    Ok(maybe_hash)
}

/// Get the node's full opaque block at a particular hash
pub async fn node_get_block(
    hash: H256,
    client: &HttpClient,
) -> anyhow::Result<Option<OpaqueBlock>> {
    let s = hex::encode(hash.0);
    let params = rpc_params![s];

    let maybe_rpc_response: Option<serde_json::Value> =
        client.request("chain_getBlock", params).await?;
    let rpc_response = maybe_rpc_response.unwrap();

    let json_opaque_block = rpc_response.get("block").cloned().unwrap();
    let opaque_block: OpaqueBlock = serde_json::from_value(json_opaque_block).unwrap();

    Ok(Some(opaque_block))
}

/// Fetch an output from chain storage given an OutputRef
pub async fn fetch_storage<V: Verifier>(
    output_ref: &OutputRef,
    client: &HttpClient,
) -> anyhow::Result<Output<V>> {
    let ref_hex = hex::encode(output_ref.encode());
    let params = rpc_params![ref_hex];
    let rpc_response: Result<Option<String>, _> = client.request("state_getStorage", params).await;

    let response_hex = rpc_response?.ok_or(anyhow!("Data cannot be retrieved from storage"))?;
    let response_hex = strip_0x_prefix(&response_hex);
    let response_bytes = hex::decode(response_hex)?;
    let utxo = Output::decode(&mut &response_bytes[..])?;

    Ok(utxo)
}