dusk_wallet/
rusk.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7use std::io::{self, Write};
8
9use reqwest::{Body, Response};
10use rkyv::Archive;
11
12use crate::Error;
13
14/// Supported Rusk version
15const REQUIRED_RUSK_VERSION: &str = "0.7.0";
16
17#[derive(Debug)]
18/// RuskRequesst according to the rusk event system
19pub struct RuskRequest {
20    topic: String,
21    data: Vec<u8>,
22}
23
24impl RuskRequest {
25    /// New RuskRequesst from topic and data
26    pub fn new(topic: &str, data: Vec<u8>) -> Self {
27        let topic = topic.to_string();
28        Self { data, topic }
29    }
30
31    /// Return the binary representation of the RuskRequesst
32    pub fn to_bytes(&self) -> io::Result<Vec<u8>> {
33        let mut buffer = vec![];
34        buffer.write_all(&(self.topic.len() as u32).to_le_bytes())?;
35        buffer.write_all(self.topic.as_bytes())?;
36        buffer.write_all(&self.data)?;
37
38        Ok(buffer)
39    }
40}
41#[derive(Clone)]
42/// Rusk HTTP Binary Client
43pub struct RuskHttpClient {
44    uri: String,
45}
46
47impl RuskHttpClient {
48    /// Create a new HTTP Client
49    pub fn new(uri: String) -> Self {
50        Self { uri }
51    }
52
53    /// Utility for querying the rusk VM
54    pub async fn contract_query<I, const N: usize>(
55        &self,
56        contract: &str,
57        method: &str,
58        value: &I,
59    ) -> Result<Vec<u8>, Error>
60    where
61        I: Archive,
62        I: rkyv::Serialize<rkyv::ser::serializers::AllocSerializer<N>>,
63    {
64        let data = rkyv::to_bytes(value).map_err(|_| Error::Rkyv)?.to_vec();
65        let request = RuskRequest::new(method, data);
66
67        let response = self.call_raw(1, contract, &request, false).await?;
68
69        Ok(response.bytes().await?.to_vec())
70    }
71
72    /// Check rusk connection
73    pub async fn check_connection(&self) -> Result<(), reqwest::Error> {
74        reqwest::Client::new().post(&self.uri).send().await?;
75        Ok(())
76    }
77
78    /// Send a RuskRequest to a specific target.
79    ///
80    /// The response is interpreted as Binary
81    pub async fn call(
82        &self,
83        target_type: u8,
84        target: &str,
85        request: &RuskRequest,
86    ) -> Result<Vec<u8>, Error> {
87        let response =
88            self.call_raw(target_type, target, request, false).await?;
89        let data = response.bytes().await?;
90        Ok(data.to_vec())
91    }
92    /// Send a RuskRequest to a specific target without parsing the response
93    pub async fn call_raw(
94        &self,
95        target_type: u8,
96        target: &str,
97        request: &RuskRequest,
98        feed: bool,
99    ) -> Result<Response, Error> {
100        let uri = &self.uri;
101        let client = reqwest::Client::new();
102        let mut request = client
103            .post(format!("{uri}/{target_type}/{target}"))
104            .body(Body::from(request.to_bytes()?))
105            .header("Content-Type", "application/octet-stream")
106            .header("rusk-version", REQUIRED_RUSK_VERSION);
107
108        if feed {
109            request = request.header("Rusk-Feeder", "1");
110        }
111        let response = request.send().await?;
112
113        let status = response.status();
114        if status.is_client_error() || status.is_server_error() {
115            let error = &response.bytes().await?;
116
117            let error = String::from_utf8(error.to_vec())
118                .unwrap_or("unparsable error".into());
119
120            let msg = format!("{status}: {error}");
121
122            Err(Error::Rusk(msg))
123        } else {
124            Ok(response)
125        }
126    }
127}