use anyhow::Context;
use clap::AppSettings;
use concordium_base::updates::ValidatorScoreParameters;
use concordium_rust_sdk::{
common::types::TransactionTime,
types::{
chain_parameters::ChainParameters,
transactions::{update, BlockItem, Payload},
TransactionStatus, UpdateKeyPair, UpdatePayload,
},
v2::{self, BlockIdentifier},
};
use std::path::PathBuf;
use structopt::StructOpt;
use tokio_stream::StreamExt;
#[derive(StructOpt)]
struct App {
#[structopt(
long = "node",
help = "GRPC interface of the node.",
default_value = "http://localhost:20000"
)]
endpoint: v2::Endpoint,
#[structopt(long = "key", help = "Path to update keys to use.")]
keys: Vec<PathBuf>,
}
#[tokio::main(flavor = "multi_thread")]
async fn main() -> anyhow::Result<()> {
let app = {
let app = App::clap().global_setting(AppSettings::ColoredHelp);
let matches = app.get_matches();
App::from_clap(&matches)
};
let kps: Vec<UpdateKeyPair> = app
.keys
.iter()
.map(|p| {
serde_json::from_reader(std::fs::File::open(p).context("Could not open file.")?)
.context("Could not read keys from file.")
})
.collect::<anyhow::Result<_>>()?;
let mut client = v2::Client::new(app.endpoint).await?;
let summary: ChainParameters = client
.get_block_chain_parameters(BlockIdentifier::LastFinal)
.await
.context("Could not obtain last finalized block's chain parameters")?
.response;
let keys = summary
.keys
.level_2_keys
.context("No level 2 keys in chain parameters.")?;
let signer = keys
.construct_update_signer(
keys.micro_ccd_per_euro
.as_ref()
.context("Missing micro CCD per Euro update keys.")?,
kps,
)
.context("Invalid keys supplied.")?;
let seq_number = client
.get_next_update_sequence_numbers(BlockIdentifier::LastFinal)
.await?
.response;
let seq_number = seq_number.validator_score_parameters;
let now = chrono::offset::Utc::now().timestamp() as u64;
let effective_time = TransactionTime::from_seconds(now + 300); let timeout = TransactionTime::from_seconds(now + 60); let payload = UpdatePayload::ValidatorScoreParametersCPV3(ValidatorScoreParameters {
max_missed_rounds: 10,
});
let block_item: BlockItem<Payload> =
update::update(&signer, seq_number, effective_time, timeout, payload).into();
let submission_id = client
.send_block_item(&block_item)
.await
.context("Could not send the update instruction.")?;
println!("Submitted update with hash {}", submission_id);
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(1));
loop {
interval.tick().await;
match client
.get_block_item_status(&submission_id)
.await
.context("Could not query submission status.")?
{
TransactionStatus::Finalized(blocks) => {
println!(
"Submission is finalized in blocks {:?}",
blocks.keys().collect::<Vec<_>>()
);
break;
}
TransactionStatus::Committed(blocks) => {
println!(
"Submission is committed to blocks {:?}",
blocks.keys().collect::<Vec<_>>()
);
}
TransactionStatus::Received => {
println!("Submission is received.")
}
}
}
let mut pending_updates = client
.get_block_pending_updates(BlockIdentifier::LastFinal)
.await?
.response;
while let Some(update) = pending_updates.next().await.transpose()? {
#[cfg(feature = "serde_deprecated")]
println!("Pending update: {}", serde_json::to_string_pretty(&update)?);
#[cfg(not(feature = "serde_deprecated"))]
println!("Pending update: {:?}", &update);
}
Ok(())
}