From dc5f26debb76c75f83359ba80c2bdd010d13c719 Mon Sep 17 00:00:00 2001 From: Willilam <1873880+willrnch@users.noreply.github.com> Date: Tue, 11 Apr 2023 21:53:50 +0200 Subject: [PATCH] Add mining test (#1238) * fix smoke-tests * Add mining and validator onboarding smoke tests --------- Co-authored-by: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> --- Cargo.lock | 5 + ol/smoke-tests/Cargo.toml | 6 +- ol/smoke-tests/src/tests.rs | 319 +++++++++++++++++++- ol/tower/src/backlog.rs | 187 +++++++----- ol/txs/src/tx_params.rs | 46 +-- testsuite/forge/Cargo.toml | 1 + testsuite/forge/src/interface/chain_info.rs | 61 ++++ 7 files changed, 515 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 04658ef4cc..020e481ddf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3756,6 +3756,7 @@ dependencies = [ "itertools", "k8s-openapi", "kube", + "ol-types", "rand 0.8.4", "rand_core 0.6.3", "rayon", @@ -6747,6 +6748,7 @@ dependencies = [ "diem-types", "diem-validator-interface", "diem-vault-client", + "diem-wallet", "diem-workspace-hack 0.1.0", "diem-writeset-generator", "forge", @@ -6756,11 +6758,14 @@ dependencies = [ "move-command-line-common", "move-ir-compiler", "move-stdlib", + "ol-types", "once_cell", "proptest", "rand 0.8.4", "regex", "reqwest 0.11.10", + "serde 1.0.137", + "serde_json", "serde_yaml", "smoke-test", "tokio 1.18.2", diff --git a/ol/smoke-tests/Cargo.toml b/ol/smoke-tests/Cargo.toml index a40939cf76..4c238d4431 100644 --- a/ol/smoke-tests/Cargo.toml +++ b/ol/smoke-tests/Cargo.toml @@ -46,6 +46,8 @@ base64 = "0.13.0" hex = "0.4.3" once_cell = "1.7.2" regex = "1.4.3" +serde = { version = "1.0.124", features = ["derive"] } +serde_json = "1.0.64" serde_yaml = "0.8.17" futures = "0.3.12" backup-cli = { path = "../../storage/backup/backup-cli" } @@ -59,6 +61,8 @@ diem-operational-tool = {path = "../../config/management/operational", features diem-time-service = { path = "../../crates/diem-time-service", features = ["testing"] } diem-vault-client = { path = "../../secure/storage/vault", features = ["fuzzing"] } diem-validator-interface = { path = "../../diem-move/diem-validator-interface" } +diem-wallet = { path = "../../crates/diem-wallet", version = "0.1.0" } diem-writeset-generator = { path = "../../diem-move/writeset-transaction-generator" } diem-transaction-builder = { path = "../../sdk/transaction-builder" } -smoke-test = { path = "../../testsuite/smoke-test", features = ["ol-smoke-test"] } \ No newline at end of file +smoke-test = { path = "../../testsuite/smoke-test", features = ["ol-smoke-test"] } +ol-types = { path = "../../ol/types"} \ No newline at end of file diff --git a/ol/smoke-tests/src/tests.rs b/ol/smoke-tests/src/tests.rs index cd1b85b923..38c488d032 100644 --- a/ol/smoke-tests/src/tests.rs +++ b/ol/smoke-tests/src/tests.rs @@ -1,16 +1,163 @@ +use anyhow::{Context, Result}; use smoke_test::{ smoke_test_environment::new_local_swarm, test_utils::{assert_balance, create_and_fund_account}, operational_tooling::launch_swarm_with_op_tool_and_backend, }; -use diem_global_constants::{OWNER_ACCOUNT, OWNER_KEY}; -use diem_types::account_address::AccountAddress; -use diem_sdk::types::LocalAccount; -use forge::{NodeExt, Swarm}; -use std::time::{Duration, Instant}; +use diem_global_constants::{OWNER_ACCOUNT, OWNER_KEY, CONFIG_FILE}; +use diem_rest_client::dpn::diem_root_address; +use diem_types::{ + account_address::AccountAddress, + chain_id::NamedChain, + account_state_blob::AccountStateBlob, + on_chain_config::OnChainConfig, +}; +use diem_sdk::types::{LocalAccount, AccountKey}; +use forge::{NodeExt, Swarm, self}; +use std::{time::{Duration, Instant}, convert::TryFrom}; use diem_secure_storage::CryptoStorage; use diem_secure_storage::KVStorage; + +use std::{ + path::PathBuf, + process::Command, +}; +use serde::{Deserialize, Serialize}; +use diem_wallet::{Mnemonic, WalletLibrary}; +use ol_types::fixtures::{ + get_persona_mnem, + get_persona_block_one, + get_persona_block_zero, + get_persona_account_json, +}; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +struct ValidatorUniverse { + validators: Vec, +} + +impl OnChainConfig for ValidatorUniverse { + const IDENTIFIER: &'static str = "ValidatorUniverse"; +} + +#[derive(Debug, Deserialize)] +struct Metadata { + pub workspace_root: PathBuf, +} + +fn metadata() -> Result { + let output = Command::new("cargo") + .args(&[ + "metadata", + "--no-deps", + "--format-version=1", + ]) + .output() + .context("Failed to query cargo metadata")?; + + serde_json::from_slice(&output.stdout).map_err(Into::into) +} + +struct TestEnvionement { + metadata: Metadata, + swarm: forge::LocalSwarm, + // op_tool: diem_operational_tool::test_helper::OperationalTool, + // backend: diem_config::config::SecureBackend, + storage: diem_secure_storage::Storage, + env: String, +} + +impl TestEnvionement { + async fn new() -> TestEnvionement { + let env = if std::env::var("NODE_ENV") == Ok("test".to_string()) { "test" } else { "stage" }; + let metadata = metadata().unwrap(); + let ( + swarm, + _op_tool, + _backend, + storage + ) = launch_swarm_with_op_tool_and_backend(1).await; + + TestEnvionement { + metadata, + swarm, + // op_tool, + // backend, + storage, + env: env.to_string(), + } + } + + fn get_persona_wallet(&self, persona: &str) -> WalletLibrary { + let mnemonic = get_persona_mnem(persona); + let mnem = Mnemonic::from(&mnemonic).unwrap(); + + let mut wallet = WalletLibrary::new_from_mnemonic(mnem); + wallet.generate_addresses(1).unwrap(); + + wallet + } + + async fn fund_account(&mut self, recipient: &LocalAccount) { + let owner_account = self.storage + .get::(OWNER_ACCOUNT) + .unwrap() + .value; + + let keys = self.storage.export_private_key(OWNER_KEY).unwrap(); + let sending_account = LocalAccount::new( + owner_account, + keys, + 0, + ); + + self.swarm.chain_info().ol_create_account_by_coin(sending_account, &recipient).await.unwrap(); + } + + async fn get_validator_universe(&mut self) -> Result { + let validator = self.swarm.validators().next().unwrap(); + let rpc_client = validator.json_rpc_client(); + + let account_state = rpc_client + .get_account_state_with_proof( + diem_root_address(), + None, + None, + )? + .into_inner(); + + let blob = account_state + .blob.unwrap(); + let account_state_blob = AccountStateBlob::from(bcs::from_bytes::>(&blob)?); + + let account_state = diem_types::account_state::AccountState::try_from(&account_state_blob)?; + let validator_universe = account_state.get_config::()?; + + Ok(validator_universe.unwrap()) + } + + async fn create_validator_account(&mut self, persona: &str) -> Result<()> { + let new_account = get_persona_account_json(persona); + let new_account = serde_json::from_str(&new_account.0).expect("invalid account file"); + + let owner_account = self.storage.get::(OWNER_ACCOUNT).unwrap().value; + + let keys = self.storage.export_private_key(OWNER_KEY)?; + let local_acct = LocalAccount::new(owner_account, keys, 0); + self.swarm + .chain_info() + .create_validator( + local_acct, + new_account + ) + .await?; + + Ok(()) + } +} + + #[tokio::test] async fn ol_test_demo() { let (mut swarm, _op_tool, _backend, storage) = launch_swarm_with_op_tool_and_backend(1).await; @@ -65,6 +212,164 @@ async fn ol_test_basic_restartability() { dbg!("validator healthy"); let client = validator.rest_client(); swarm.chain_info().ol_send_demo_tx_root(Some(client)).await.expect("could not send tx"); - dbg!("tx sent"); - + dbg!("tx sent"); +} + +/// Tests mining by sending proofs +/// - Initialize a local chain +/// - Create the miner's account (alice) +/// - Send the proofs (ol/fixtures/vdf_proofs/test/alice) +#[tokio::test] +async fn ol_test_mining() { + let mut test_environment = TestEnvionement::new().await; + + let persona = "alice"; + + let mut wallet = test_environment.get_persona_wallet(persona); + wallet.generate_addresses(1).unwrap(); + let address = wallet.get_addresses().unwrap().into_iter().next().unwrap(); + let private_key = wallet.get_private_key(&address).unwrap(); + let account_key = AccountKey::from_private_key(private_key); + let new_account = LocalAccount::new(address, account_key, 0); + + test_environment.fund_account(&new_account).await; + + let validator = test_environment.swarm.validators().next().unwrap(); + let rpc_client = validator.json_rpc_client(); + + match rpc_client.get_miner_state(address) { + Err(err) => { + let err = err.json_rpc_error().unwrap(); + assert_eq!(err.message, "Server error: could not get tower state"); + }, + _ => { + panic!("miner state for new account shouldn't exists"); + }, + } + + // Proof #0 + let proof = get_persona_block_zero(persona, &test_environment.env); + test_environment.swarm.chain_info().ol_commit_proof(new_account, proof).await.unwrap(); + + let miner_state = rpc_client.get_miner_state(address).unwrap(); + let miner_state = miner_state.inner().as_ref().unwrap(); + + assert_eq!(miner_state.verified_tower_height, 0); + + // Proof #1 + let private_key = wallet.get_private_key(&address).unwrap(); + let account_key = AccountKey::from_private_key(private_key); + let new_account = LocalAccount::new(address, account_key, 1); + + let proof = get_persona_block_one(persona, &test_environment.env); + test_environment.swarm.chain_info().ol_commit_proof(new_account, proof).await.unwrap(); + + let miner_state = rpc_client.get_miner_state(address).unwrap(); + let miner_state = miner_state.inner().as_ref().unwrap(); + + assert_eq!(miner_state.verified_tower_height, 1); +} + +/// Tests the mining capacity through the tower binary. +/// - Initialize a local chain +/// - Create the miner's account (alice) +/// - Send the proofs with tower (ol/fixtures/vdf_proofs/test/alice) +#[tokio::test] +async fn ol_test_tower_mining() { + let mut test_environment = TestEnvionement::new().await; + + let validator = test_environment.swarm.validators().next().unwrap(); + let rpc_client = validator.json_rpc_client(); + + // 01. CREATE MINER ACCOUNT + let persona = "alice"; + let mut wallet = test_environment.get_persona_wallet(persona); + wallet.generate_addresses(1).unwrap(); + let address = wallet.get_addresses().unwrap().into_iter().next().unwrap(); + + let private_key = wallet.get_private_key(&address).unwrap(); + let account_key = AccountKey::from_private_key(private_key); + let new_account = LocalAccount::new(address, account_key, 0); + + test_environment.fund_account(&new_account).await; + + // 02. FORGE TOWER CONFIG + let mut rpc_url = rpc_client.url(); + rpc_url.set_path(""); + + let waypoint = rpc_client.get_waypoint().unwrap(); + let waypoint = waypoint.inner().as_ref().unwrap(); + + let mut config = ol_types::config::AppCfg::default(); + + config.workspace.block_dir = test_environment.metadata.workspace_root.join( + format!( + "ol/fixtures/vdf_proofs/{}/{}", + test_environment.env, + persona + ) + ).to_string_lossy().to_string(); + + config.profile.account = address; + + config.chain_info.base_waypoint = Some(waypoint.waypoint); + config.chain_info.chain_id = NamedChain::from_chain_id(&test_environment.swarm.chain_id()).unwrap(); + + let mut rpc_url = rpc_client.url(); + rpc_url.set_path(""); + config.profile.upstream_nodes = vec![rpc_url]; + + let node_home = test_environment.swarm.dir().join("tower"); + config.workspace.node_home = node_home.to_owned(); + + config.save_file().unwrap(); + + let tower_config_path = node_home.join(CONFIG_FILE).into_os_string().into_string().unwrap(); + + // 03. SEND PROOFS + let mnemonic = get_persona_mnem(persona); + let mut process = Command::new("cargo") + .env("TEST", "y") + .env("MNEM", mnemonic.clone()) + .args(&[ + "run", + "-p", + "tower", + "--", + "--config", + &tower_config_path, + "backlog", + "-s", + ]) + .spawn() + .expect("failed to execute process"); + + process.wait().unwrap(); + + let miner_state = rpc_client.get_miner_state(address).unwrap(); + let miner_state = miner_state.inner().as_ref().unwrap(); + + assert_eq!(miner_state.verified_tower_height, 1); +} + +/// Tests the validator onboarding +/// - Initialize a local chain +/// - Create the validator's account (alice) by calling AccountScripts::create_acc_val +/// - Check that the validator is in the validator set by checking the validator universe +#[tokio::test] +async fn ol_test_validator_onboarding() { + let mut test_environment = TestEnvionement::new().await; + + let persona = "alice"; + + let wallet = test_environment.get_persona_wallet(persona); + let address = wallet.get_addresses().unwrap().into_iter().next().unwrap(); + + let validator_universe = test_environment.get_validator_universe().await.unwrap(); + assert_eq!(validator_universe.validators.contains(&address), false); + + test_environment.create_validator_account(persona).await.unwrap(); + + let validator_universe = test_environment.get_validator_universe().await.unwrap(); + assert_eq!(validator_universe.validators.contains(&address), true); } \ No newline at end of file diff --git a/ol/tower/src/backlog.rs b/ol/tower/src/backlog.rs index 09675584f2..c71bef809e 100644 --- a/ol/tower/src/backlog.rs +++ b/ol/tower/src/backlog.rs @@ -20,12 +20,6 @@ use crate::{tower_errors, EPOCH_MINING_THRES_UPPER}; /// Submit a backlog of blocks that may have been mined while network is offline. /// Likely not more than 1. pub fn process_backlog(config: &AppCfg, tx_params: &TxParams) -> Result<(), TxError> { - // Getting remote miner state - // there may not be any onchain state. - let (remote_height, proofs_in_epoch) = get_remote_tower_height(tx_params)?; - - info!("Remote tower height: {}", remote_height); - info!("Proofs already submitted in epoch: {}", proofs_in_epoch); // Getting local state height let mut blocks_dir = config.workspace.node_home.clone(); blocks_dir.push(&config.workspace.block_dir); @@ -33,66 +27,76 @@ pub fn process_backlog(config: &AppCfg, tx_params: &TxParams) -> Result<(), TxEr let (current_local_proof, _current_block_path) = get_highest_block(&blocks_dir)?; let current_proof_number = current_local_proof.height; - // if let Some(current_proof_number) = current_local_proof { + info!("Local tower height: {:?}", current_proof_number); - if remote_height < 0 || current_proof_number > remote_height as u64 { - let mut i = remote_height as u64 + 1; - - // use i64 for safety - if !(proofs_in_epoch < EPOCH_MINING_THRES_UPPER as i64) { - info!( - "Backlog: Maximum number of proofs sent this epoch {}, exiting.", - EPOCH_MINING_THRES_UPPER - ); - return Err(anyhow!( - "cannot submit more proofs than allowed in epoch, aborting backlog." - ) - .into()); + + + let mut i = 0; + let mut remaining_in_epoch = EPOCH_MINING_THRES_UPPER; + + // Getting remote miner state + // there may not be any onchain state. + if let Some((remote_height, proofs_in_epoch)) = get_remote_tower_height(tx_params)? { + info!("Remote tower height: {}", remote_height); + info!("Proofs already submitted in epoch: {}", proofs_in_epoch); + + if remote_height < 0 || current_proof_number > remote_height as u64 { + i = remote_height as u64 + 1; + + // use i64 for safety + if !(proofs_in_epoch < EPOCH_MINING_THRES_UPPER as i64) { + info!( + "Backlog: Maximum number of proofs sent this epoch {}, exiting.", + EPOCH_MINING_THRES_UPPER + ); + return Err(anyhow!( + "cannot submit more proofs than allowed in epoch, aborting backlog." + ) + .into()); + } + + if proofs_in_epoch > 0 { + remaining_in_epoch = EPOCH_MINING_THRES_UPPER - proofs_in_epoch as u64 + } } + } - let remaining_in_epoch = if proofs_in_epoch > 0 { - EPOCH_MINING_THRES_UPPER - proofs_in_epoch as u64 - } else { - EPOCH_MINING_THRES_UPPER - }; - let mut submitted_now = 1u64; - - info!("Backlog: resubmitting missing proofs. Remaining in epoch: {}, already submitted in this backlog: {}", remaining_in_epoch, submitted_now); - - while i <= current_proof_number && submitted_now <= remaining_in_epoch { - info!("submitting proof {}, in this backlog: {}", i, submitted_now); - - let path = PathBuf::from(format!("{}/{}_{}.json", blocks_dir.display(), FILENAME, i)); - - let file = File::open(&path).map_err(|e| { - anyhow!("failed to open file: {:?}, message, {}", &path.to_str(), e.to_string()) - })?; - - let reader = BufReader::new(file); - let block: VDFProof = serde_json::from_reader(reader).map_err(|e| Error::from(e))?; - - let view = commit_proof_tx(&tx_params, block.clone())?; - match eval_tx_status(view) { - Ok(_) => {} - Err(e) => { - warn!( - "WARN: could not fetch TX status, aborting. Message: {:?} ", - &e - ); - // evaluate type of error and maybe garbage collect - match tower_errors::parse_error(&e) { - tower_errors::TowerError::WrongDifficulty => gc_failed_proof(config, path)?, - tower_errors::TowerError::Discontinuity => gc_failed_proof(config, path)?, - tower_errors::TowerError::Invalid => gc_failed_proof(config, path)?, - _ => {} - } - return Err(e); + let mut submitted_now = 1u64; + + info!("Backlog: resubmitting missing proofs. Remaining in epoch: {}, already submitted in this backlog: {}", remaining_in_epoch, submitted_now); + + while i <= current_proof_number && submitted_now <= remaining_in_epoch { + info!("submitting proof {}, in this backlog: {}", i, submitted_now); + + let path = PathBuf::from(format!("{}/{}_{}.json", blocks_dir.display(), FILENAME, i)); + + let file = File::open(&path).map_err(|e| { + anyhow!("failed to open file: {:?}, message, {}", &path.to_str(), e.to_string()) + })?; + + let reader = BufReader::new(file); + let block: VDFProof = serde_json::from_reader(reader).map_err(|e| Error::from(e))?; + + let view = commit_proof_tx(&tx_params, block.clone())?; + match eval_tx_status(view) { + Ok(_) => {} + Err(e) => { + warn!( + "WARN: could not fetch TX status, aborting. Message: {:?} ", + &e + ); + // evaluate type of error and maybe garbage collect + match tower_errors::parse_error(&e) { + tower_errors::TowerError::WrongDifficulty => gc_failed_proof(config, path)?, + tower_errors::TowerError::Discontinuity => gc_failed_proof(config, path)?, + tower_errors::TowerError::Invalid => gc_failed_proof(config, path)?, + _ => {} } - }; - i = i + 1; - submitted_now = submitted_now + 1; - } - // } + return Err(e); + } + }; + i = i + 1; + submitted_now = submitted_now + 1; } Ok(()) } @@ -105,9 +109,28 @@ pub fn submit_proof_by_number( ) -> Result<(), TxError> { // Getting remote miner state // there may not be any onchain state. - let (remote_height, _proofs_in_epoch) = get_remote_tower_height(tx_params)?; + match get_remote_tower_height(tx_params)? { + Some((remote_height, _proofs_in_epoch)) => { + info!("Remote tower height: {}", remote_height); + + if remote_height > 0 && proof_to_submit <= remote_height as u64 { + warn!( + "unable to submit proof - remote tower height is higher than or equal to {:?}", + proof_to_submit + ); + return Ok(()); + } + }, + None => { + if proof_to_submit != 0 { + warn!( + "unable to submit proof - remote tower state is not initiliazed. Sent proof 0 first" + ); + return Ok(()); + } + } + } - info!("Remote tower height: {}", remote_height); // Getting local state height let mut blocks_dir = config.workspace.node_home.clone(); blocks_dir.push(&config.workspace.block_dir); @@ -124,14 +147,6 @@ pub fn submit_proof_by_number( return Ok(()); } - if remote_height > 0 && proof_to_submit <= remote_height as u64 { - warn!( - "unable to submit proof - remote tower height is higher than or equal to {:?}", - proof_to_submit - ); - return Ok(()); - } - info!("Backlog: submitting proof {:?}", proof_to_submit); let path = PathBuf::from(format!( @@ -163,9 +178,15 @@ pub fn submit_proof_by_number( pub fn show_backlog(config: &AppCfg, tx_params: &TxParams) -> Result<(), TxError> { // Getting remote miner state // there may not be any onchain state. - let (remote_height, _proofs_in_epoch) = get_remote_tower_height(tx_params)?; + match get_remote_tower_height(tx_params)? { + Some((remote_height, _proofs_in_epoch)) => { + println!("Remote tower height: {}", remote_height); + }, + None => { + println!("Remote tower state no initialized"); + }, + } - println!("Remote tower height: {}", remote_height); // Getting local state height let mut blocks_dir = config.workspace.node_home.clone(); blocks_dir.push(&config.workspace.block_dir); @@ -179,7 +200,7 @@ pub fn show_backlog(config: &AppCfg, tx_params: &TxParams) -> Result<(), TxError } /// returns remote tower height and current proofs in epoch -pub fn get_remote_tower_height(tx_params: &TxParams) -> Result<(i64, i64), Error> { +pub fn get_remote_tower_height(tx_params: &TxParams) -> Result, Error> { let client = DiemClient::new(tx_params.url.clone()); info!( "Fetching remote tower height: {}, {}", @@ -210,19 +231,25 @@ pub fn get_remote_tower_height(tx_params: &TxParams) -> Result<(i64, i64), Error s.actual_count_proofs_in_epoch ); - return Ok(( + return Ok(Some(( s.verified_tower_height as i64, s.actual_count_proofs_in_epoch as i64, - )); + ))); } None => bail!("ERROR: user has no tower state on chain"), }, - Err(e) => { + Err(error) => { + if let Some(rpc_error) = error.json_rpc_error() { + if rpc_error.message == "Server error: could not get tower state" { + return Ok(None); + } + } + println!( "ERROR: unable to get tower height from chain, message: {:?}", - e + error ); - return Err(anyhow!(e)); + return Err(anyhow!(error)); } } } diff --git a/ol/txs/src/tx_params.rs b/ol/txs/src/tx_params.rs index b15c151511..98b35f6eef 100644 --- a/ol/txs/src/tx_params.rs +++ b/ol/txs/src/tx_params.rs @@ -20,7 +20,7 @@ use ol::node::client::find_a_remote_jsonrpc; use ol_keys::{scheme::KeyScheme, wallet}; use ol_types::{ self, - config::{TxCost, TxType}, + config::{TxCost, TxType, IS_TEST}, fixtures, }; @@ -111,30 +111,32 @@ impl TxParams { } }; - println!("OPTIONAL: If you have changed your account's authkey \ - then input the old address below, enter to skip."); let mut account_address: Option = None; - let mut input = String::new(); - loop { - match io::stdin().read_line(&mut input) { - Ok(_) => { - if let Some('\n') = input.chars().next_back() { - input.pop(); + if *IS_TEST == false { + println!("OPTIONAL: If you have changed your account's authkey \ + then input the old address below, enter to skip."); + let mut input = String::new(); + loop { + match io::stdin().read_line(&mut input) { + Ok(_) => { + if let Some('\n') = input.chars().next_back() { + input.pop(); + } + if let Some('\r') = input.chars().next_back() { + input.pop(); + } + if input.len() == 0 { + break; + } + if let Ok(address) = AccountAddress::from_hex_literal(&format!("0x{}", input)) { + account_address = Some(address); + break; + }; + println!("Invalid address. Try again!"); + input.clear(); } - if let Some('\r') = input.chars().next_back() { - input.pop(); - } - if input.len() == 0 { - break; - } - if let Ok(address) = AccountAddress::from_hex_literal(&format!("0x{}", input)) { - account_address = Some(address); - break; - }; - println!("Invalid address. Try again!"); - input.clear(); + Err(error) => println!("{}", error) } - Err(error) => println!("{}", error) } } if let Some(address) = account_address { diff --git a/testsuite/forge/Cargo.toml b/testsuite/forge/Cargo.toml index f6dd35fdd7..530ae11858 100644 --- a/testsuite/forge/Cargo.toml +++ b/testsuite/forge/Cargo.toml @@ -49,3 +49,4 @@ diem-secure-storage = { path = "../../secure/storage" } diem-transaction-builder = { path = "../../sdk/transaction-builder" } diem-workspace-hack = { version = "0.1", path = "../../crates/diem-workspace-hack" } transaction-emitter = { path = "../../crates/transaction-emitter" } +ol-types = { path = "../../ol/types"} diff --git a/testsuite/forge/src/interface/chain_info.rs b/testsuite/forge/src/interface/chain_info.rs index 0e638aa56d..68a4cc44cf 100644 --- a/testsuite/forge/src/interface/chain_info.rs +++ b/testsuite/forge/src/interface/chain_info.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{Coffer, NFTPublicInfo, PublicInfo, Result}; +use anyhow::bail; use diem_rest_client::Client as RestClient; use diem_sdk::{ client::BlockingClient, @@ -219,4 +220,64 @@ impl<'t> ChainInfo<'t> { pub fn into_nft_public_info(self) -> NFTPublicInfo<'t> { NFTPublicInfo::new(self.chain_id, self.rest_api_url.clone(), self.root_account) } + + /// Commit miner proof + pub async fn ol_commit_proof( + &mut self, + mut account: LocalAccount, + block: ol_types::block::VDFProof + ) -> Result<()> { + let factory = self.transaction_factory(); + let client = self.rest_client(); + + let txn = account + .sign_with_transaction_builder( + factory.payload( + transaction_builder::stdlib::encode_minerstate_commit_script_function( + block.preimage.clone(), + block.proof.clone(), + block.difficulty(), + block.security(), + ) + ) + ); + client.submit_and_wait(&txn).await?; + Ok(()) + } + + pub async fn create_validator( + &mut self, + mut validator: LocalAccount, + new_validator: ol_types::account::ValConfigs, + ) -> Result<()> { + let factory = self.transaction_factory(); + let client = self.rest_client(); + + let block = match new_validator.block_zero { + Some(b) => b, + None => bail!("no block zero found in account.json"), + }; + let txn = validator + .sign_with_transaction_builder( + factory.payload( + transaction_builder::stdlib::encode_create_acc_val_script_function( + block.preimage.clone(), + block.proof.clone(), + block.difficulty(), + block.security(), + new_validator.ow_human_name.to_string().as_bytes().to_vec(), + new_validator.op_address.parse().unwrap(), + new_validator.op_auth_key_prefix, + new_validator.op_consensus_pubkey, + new_validator.op_validator_network_addresses, + new_validator.op_fullnode_network_addresses, + new_validator.op_human_name.as_bytes().to_vec(), + ) + ) + ); + + client.submit_and_wait(&txn).await?; + + Ok(()) + } }