View on GitHub
File Changes
mod programs;
#[macro_use]
pub mod scenario;
+
mod graphql;
mod slog;
pub mod style;
mod wallet;

                      
    introduction(&context);

                      
-
    scenario_2(context.derive());
+
    // FIXME: Can be abstracted as a macro? Maybe a procedural one (attributes?)
+
    let tests = vec![
+
        Test {
+
            func: Box::new(move |context| explorer_get_transaction(context)),
+
            name: "test explorer transaction".to_owned(),
+
            ignore: false,
+
        },
+
        Test {
+
            func: Box::new(move |context| scenario_1(context)),
+
            name: "scenario 1".to_owned(),
+
            ignore: false,
+
        },
+
        Test {
+
            func: Box::new(move |context| scenario_2(context)),
+
            name: "scenario 2".to_owned(),
+
            ignore: true,
+
        },
+
    ];
+

                      
+
    for test in tests.iter().filter(|t| !t.ignore) {
+
        let context = context.derive();
+
        match std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || (test.func)(context))) {
+
            Ok(()) => println!("{} PASSED", test.name),
+
            Err(e) => {
+
                println!("{} FAILED", test.name);
+

                      
+
                let msg = e
+
                    .downcast_ref::<String>()
+
                    .map(|e| &**e)
+
                    .or_else(|| e.downcast_ref::<&'static str>().map(|e| *e))
+
                    .unwrap_or("Unknown");
+

                      
+
                println!("Cause: {}", msg);
+

                      
+
                // Return a non 0 exit status
+
                std::process::exit(1);
+
            }
+
        }
+
    }
}

                      
fn introduction<R: rand_core::RngCore>(context: &Context<R>) {

                      
    controller.finalize();
}
+

                      
+
pub fn explorer_get_transaction(mut context: Context<ChaChaRng>) {
+
    let scenario_settings = prepare_scenario! {
+
        "Testing the explorer",
+
        &mut context,
+
        topology [
+
            "Leader1",
+
        ]
+
        blockchain {
+
            consensus = GenesisPraos,
+
            number_of_slots_per_epoch = 60,
+
            slot_duration = 1,
+
            leaders = [ "Leader1" ],
+
            initials = [
+
                account "unassigned1" with   500_000_000,
+
                account "unassigned2" with   100_000_000,
+
                account "delegated1" with  2_000_000_000 delegates to "Leader1",
+
            ],
+
        }
+
    };
+

                      
+
    let mut controller = scenario_settings.build(context).unwrap();
+

                      
+
    let leader1 = controller.spawn_node("Leader1", true).unwrap();
+
    thread::sleep(Duration::from_secs(1));
+

                      
+
    controller.monitor_nodes();
+

                      
+
    let mut wallet1 = controller.wallet("unassigned1").unwrap();
+
    let wallet2 = controller.wallet("delegated1").unwrap();
+

                      
+
    let check = controller
+
        .wallet_send_to(&mut wallet1, &wallet2, &leader1, 5_000.into())
+
        .unwrap();
+

                      
+
    let id = check.fragment_id;
+

                      
+
    thread::sleep(Duration::from_secs(1));
+

                      
+
    let leader_status = leader1.wait_fragment(Duration::from_secs(2), check);
+

                      
+
    if let Ok(status) = leader_status {
+
        if status.is_in_a_block() {
+
            wallet1.confirm_transaction();
+
            assert!(
+
                leader1.get_explorer_transaction(id).is_ok(),
+
                "transaction not found in explorer"
+
            );
+
        }
+
    }
+

                      
+
    thread::sleep(Duration::from_secs(1));
+

                      
+
    leader1.shutdown().unwrap();
+

                      
+
    controller.finalize();
+
}
+

                      
+
pub struct Test {
+
    pub func: Box<dyn Fn(Context<ChaChaRng>)>,
+
    pub name: String,
+
    pub ignore: bool,
+
}
+
use crate::graphql::{ExplorerTransaction, GraphQLQuery, GraphQLResponse};
use crate::{scenario::settings::NodeSetting, style, Context, NodeAlias};
use bawawa::{Control, Process};
use chain_impl_mockchain::{
    block::{Block, HeaderHash},
    fragment::{Fragment, FragmentId},
};
use indicatif::ProgressBar;
+
use jormungandr_lib::crypto::hash::Hash;
use jormungandr_lib::interfaces::{FragmentLog, FragmentStatus};
use rand_core::RngCore;
+
use std::convert::TryFrom;
use std::{
    collections::HashMap,
    path::PathBuf,
        Io(std::io::Error);
        Reqwest(reqwest::Error);
        BlockFormatError(chain_core::mempack::ReadError);
+
        JsonError(serde_json::error::Error);
    }

                      
    errors {
}

                      
pub struct MemPoolCheck {
-
    fragment_id: FragmentId,
+
    // FIXME: Probably this shouldn't be pub, maybe as_ref?
+
    pub fragment_id: FragmentId,
}

                      
pub enum NodeBlock0 {
        self.status() == Status::Running
    }

                      
-
    fn post(&self, path: &str, body: Vec<u8>) -> Result<reqwest::Response> {
+
    fn post(&self, path: &str, body: impl Into<reqwest::Body>) -> Result<reqwest::Response> {
        self.progress_bar.log_info(format!("POST '{}'", path));

                      
        let client = reqwest::Client::new();
        }
    }

                      
+
    fn query_explorer(&self, query: GraphQLQuery) -> Result<reqwest::Response> {
+
        let client = reqwest::Client::new();
+
        let res = client
+
            .post(&format!("{}", self.base_explorer_url()))
+
            .json(&query)
+
            .send();
+

                      
+
        match res {
+
            Err(err) => {
+
                self.progress_bar
+
                    .log_err(format!("Failed to send request {}", &err));
+
                Err(err.into())
+
            }
+
            Ok(r) => Ok(r),
+
        }
+
    }
+

                      
    fn get(&self, path: &str) -> Result<reqwest::Response> {
        self.progress_bar.log_info(format!("GET '{}'", path));

                      
        format!("http://{}/api/v0", self.settings.config.rest.listen.clone())
    }

                      
+
    fn base_explorer_url(&self) -> String {
+
        format!(
+
            "http://{}/explorer/graphql",
+
            self.settings.config.rest.listen.clone()
+
        )
+
    }
+

                      
    pub fn send_fragment(&self, fragment: Fragment) -> Result<MemPoolCheck> {
        use chain_core::property::Fragment as _;
        use chain_core::property::Serialize as _;
        }
    }

                      
+
    pub fn get_explorer_transaction(&self, fragment_id: FragmentId) -> Result<ExplorerTransaction> {
+
        let query = ExplorerTransaction::build_query(fragment_id);
+

                      
+
        let response = self.query_explorer(query);
+

                      
+
        let response: GraphQLResponse = serde_json::from_str(&response?.text()?)?;
+

                      
+
        ExplorerTransaction::try_from(response).map_err(|e| e.into())
+
    }
+

                      
    pub fn shutdown(&self) -> Result<bool> {
        let result = self.get("shutdown")?.text()?;

                      
    pub rest: Rest,

                      
    pub p2p: P2pConfig,
+

                      
+
    pub explorer: Explorer,
}

                      
#[derive(Debug, Clone, Serialize, Deserialize)]
    pub trusted_peers: Vec<poldercast::Address>,
}

                      
+
#[derive(Debug, Clone, Serialize, Deserialize)]
+
pub struct Explorer {
+
    pub enabled: bool,
+
}
+

                      
/// Node Secret(s)
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NodeSecret {
        NodeConfig {
            rest: Rest::prepare(context),
            p2p: P2pConfig::prepare(context),
+
            explorer: Explorer { enabled: true },
        }
    }
}