blueprint_manager/sources/
github.rs

1use crate::gadget::native::get_gadget_binary;
2use crate::sdk;
3use crate::sdk::utils::{
4    get_download_url, hash_bytes_to_hex, is_windows, msg_to_error, valid_file_exists,
5};
6use crate::sources::BinarySourceFetcher;
7use async_trait::async_trait;
8use color_eyre::eyre::OptionExt;
9use gadget_sdk::{error, info};
10use std::path::PathBuf;
11use tangle_subxt::tangle_testnet_runtime::api::runtime_types::tangle_primitives::services::GithubFetcher;
12use tokio::io::AsyncWriteExt;
13
14pub struct GithubBinaryFetcher {
15    pub fetcher: GithubFetcher,
16    pub blueprint_id: u64,
17    pub gadget_name: String,
18}
19
20#[async_trait]
21impl BinarySourceFetcher for GithubBinaryFetcher {
22    async fn get_binary(&self) -> color_eyre::Result<PathBuf> {
23        let relevant_binary = get_gadget_binary(&self.fetcher.binaries.0)
24            .ok_or_eyre("Unable to find matching binary")?;
25        let expected_hash = sdk::utils::slice_32_to_sha_hex_string(relevant_binary.sha256);
26        let current_dir = std::env::current_dir()?;
27        let mut binary_download_path =
28            format!("{}/protocol-{:?}", current_dir.display(), self.fetcher.tag);
29
30        if is_windows() {
31            binary_download_path += ".exe"
32        }
33
34        info!("Downloading to {binary_download_path}");
35
36        // Check if the binary exists, if not download it
37        let retrieved_hash = if !valid_file_exists(&binary_download_path, &expected_hash).await {
38            let url = get_download_url(relevant_binary, &self.fetcher);
39
40            let download = reqwest::get(&url)
41                .await
42                .map_err(|err| msg_to_error(err.to_string()))?
43                .bytes()
44                .await
45                .map_err(|err| msg_to_error(err.to_string()))?;
46            let retrieved_hash = hash_bytes_to_hex(&download);
47
48            // Write the binary to disk
49            let mut file = tokio::fs::File::create(&binary_download_path).await?;
50            file.write_all(&download).await?;
51            file.flush().await?;
52            Some(retrieved_hash)
53        } else {
54            None
55        };
56
57        if let Some(retrieved_hash) = retrieved_hash {
58            if retrieved_hash.trim() != expected_hash.trim() {
59                error!(
60                    "Binary hash {} mismatched expected hash of {} for protocol: {}",
61                    retrieved_hash, expected_hash, self.gadget_name
62                );
63                return Ok(PathBuf::from(binary_download_path));
64            }
65        }
66
67        Err(color_eyre::Report::msg(
68            "The hash of the downloaded binary did not match",
69        ))
70    }
71
72    fn blueprint_id(&self) -> u64 {
73        self.blueprint_id
74    }
75
76    fn name(&self) -> String {
77        self.gadget_name.clone()
78    }
79}