Skip to content

Commit

Permalink
Add mining test (#1238)
Browse files Browse the repository at this point in the history
* fix smoke-tests

* Add mining and validator onboarding smoke tests

---------

Co-authored-by: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com>
  • Loading branch information
willrnch and 0o-de-lally committed Apr 11, 2023
1 parent cd2bac0 commit dc5f26d
Show file tree
Hide file tree
Showing 7 changed files with 515 additions and 110 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion ol/smoke-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand All @@ -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"] }
smoke-test = { path = "../../testsuite/smoke-test", features = ["ol-smoke-test"] }
ol-types = { path = "../../ol/types"}
319 changes: 312 additions & 7 deletions ol/smoke-tests/src/tests.rs
Original file line number Diff line number Diff line change
@@ -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<AccountAddress>,
}

impl OnChainConfig for ValidatorUniverse {
const IDENTIFIER: &'static str = "ValidatorUniverse";
}

#[derive(Debug, Deserialize)]
struct Metadata {
pub workspace_root: PathBuf,
}

fn metadata() -> Result<Metadata> {
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::<AccountAddress>(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<ValidatorUniverse> {
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::<Vec<u8>>(&blob)?);

let account_state = diem_types::account_state::AccountState::try_from(&account_state_blob)?;
let validator_universe = account_state.get_config::<ValidatorUniverse>()?;

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::<AccountAddress>(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;
Expand Down Expand Up @@ -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);
}

0 comments on commit dc5f26d

Please sign in to comment.