Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[fastx client & benchmark] Updated client wrapper to use objects. Updated readme commands #91

Merged
merged 20 commits into from
Dec 30, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 20 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ FastPay allows a set of distributed authorities, some of which are Byzantine, to
```bash
cargo build --release
cd target/release
rm -f *.json *.txt
rm -f *.json *.toml

# Create configuration files for 4 authorities with 4 shards each.
# * Private server states are stored in `server*.json`.
Expand All @@ -28,30 +28,35 @@ done

# Create configuration files for 1000 user accounts.
# * Private account states are stored in one local wallet `accounts.json`.
# * `initial_accounts.txt` is used to mint the corresponding initial balances at startup on the server side.
./client --committee committee.json --accounts accounts.json create_accounts 1000 --initial-funding 100 >> initial_accounts.txt

# * `initial_accounts.toml` is used to mint the corresponding initially randomly generated (for now) objects at startup on the server side.
./client --committee committee.json --accounts accounts.json create_accounts 1000 initial_accounts.toml
# Start servers
for I in 1 2 3 4
do
for J in $(seq 0 3)
do
./server --server server"$I".json run --shard "$J" --initial-accounts initial_accounts.txt --committee committee.json &
./server --server server"$I".json run --shard "$J" --initial-accounts initial_accounts.toml --committee committee.json &
done
done

# Query account addresses
./client --committee committee.json --accounts accounts.json query_accounts_addrs

# Query (locally cached) object info for first and last user account
ACCOUNT1=`./client --committee committee.json --accounts accounts.json query_accounts_addrs | head -n 1`
ACCOUNT2=`./client --committee committee.json --accounts accounts.json query_accounts_addrs | tail -n -1`
./client --committee committee.json --accounts accounts.json query_objects "$ACCOUNT1"
./client --committee committee.json --accounts accounts.json query_objects "$ACCOUNT2"

# Query (locally cached) balance for first and last user account
ACCOUNT1="`head -n 1 initial_accounts.txt | awk -F: '{ print $1 }'`"
ACCOUNT2="`tail -n -1 initial_accounts.txt | awk -F: '{ print $1 }'`"
./client --committee committee.json --accounts accounts.json query_balance "$ACCOUNT1"
./client --committee committee.json --accounts accounts.json query_balance "$ACCOUNT2"
# Get the first ObjectId for Account1
ACCOUNT1_OBJECT1=`./client --committee committee.json --accounts accounts.json query_objects "$ACCOUNT1" | head -n 1 | awk -F: '{ print $1 }'`

# Transfer 10 units
./client --committee committee.json --accounts accounts.json transfer 10 --from "$ACCOUNT1" --to "$ACCOUNT2"
# Transfer object by ObjectID
./client --committee committee.json --accounts accounts.json transfer "$ACCOUNT1_OBJECT1" --from "$ACCOUNT1" --to "$ACCOUNT2"

# Query balances again
./client --committee committee.json --accounts accounts.json query_balance "$ACCOUNT1"
./client --committee committee.json --accounts accounts.json query_balance "$ACCOUNT2"
# Query objects again again
oxade marked this conversation as resolved.
Show resolved Hide resolved
./client --committee committee.json --accounts accounts.json query_objects "$ACCOUNT1"
./client --committee committee.json --accounts accounts.json query_objects "$ACCOUNT2"

# Launch local benchmark using all user accounts
./client --committee committee.json --accounts accounts.json benchmark
Expand Down
2 changes: 2 additions & 0 deletions fastpay/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ serde_json = "1.0.57"
structopt = "0.3"
tempfile = "3.2.0"
tokio = { version = "0.2.22", features = ["full"] }
rand = "0.7.3"
toml = "0.5.8"

fastpay_core = { path = "../fastpay_core" }
fastx-types = { path = "../fastx_types" }
Expand Down
100 changes: 71 additions & 29 deletions fastpay/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ use fastx_types::{base_types::*, committee::Committee, messages::*, serialize::*
use bytes::Bytes;
use futures::stream::StreamExt;
use log::*;
use rand::Rng;
use std::{
collections::{HashMap, HashSet},
fmt::Write,
time::{Duration, Instant},
};
use structopt::StructOpt;
Expand Down Expand Up @@ -199,6 +201,18 @@ fn make_benchmark_certificates_from_votes(
certificates
}

/// Create randomly sized vectors (between 1 and 10 items) with random object IDs
fn create_random_object_ids() -> Vec<ObjectID> {
let mut rng = rand::thread_rng();
let num_ids: u8 = rng.gen();
oxade marked this conversation as resolved.
Show resolved Hide resolved

let mut object_ids = Vec::new();
for _ in 0..num_ids % 9 + 1 {
oxade marked this conversation as resolved.
Show resolved Hide resolved
object_ids.push(ObjectID::random());
}
object_ids
}

/// Broadcast a bulk of requests to each authority.
async fn mass_broadcast_orders(
phase: &'static str,
Expand Down Expand Up @@ -304,7 +318,7 @@ struct ClientOpt {
#[structopt(long, default_value = transport::DEFAULT_MAX_DATAGRAM_SIZE)]
buffer_size: usize,

/// Subcommands. Acceptable values are transfer, query_balance, benchmark, and create_accounts.
/// Subcommands. Acceptable values are transfer, query_objects, benchmark, and create_accounts.
#[structopt(subcommand)]
cmd: ClientCommands,
}
Expand All @@ -326,9 +340,13 @@ enum ClientCommands {
object_id: String,
},

/// Obtain the spendable balance
#[structopt(name = "query_balance")]
QueryBalance {
/// Obtain the Account Addresses
#[structopt(name = "query_accounts_addrs")]
QueryAccountAddresses {},

/// Obtain the Object Info
#[structopt(name = "query_objects")]
QueryObjects {
/// Address of the account
address: String,
},
Expand All @@ -349,15 +367,15 @@ enum ClientCommands {
server_configs: Option<Vec<String>>,
},

/// Create new user accounts and print the public keys
/// Create new user accounts with randomly generated object IDs
#[structopt(name = "create_accounts")]
CreateAccounts {
/// known initial balance of the account
#[structopt(long)]
initial_objects: Option<Vec<String>>,

/// Number of additional accounts to create
num: u32,

/// Initial state config file path
#[structopt(name = "init-state-cfg")]
initial_state_config_path: String,
},
}

Expand Down Expand Up @@ -427,7 +445,23 @@ fn main() {
});
}

ClientCommands::QueryBalance { address } => {
ClientCommands::QueryAccountAddresses {} => {
oxade marked this conversation as resolved.
Show resolved Hide resolved
let mut addr_text: String = String::new();

for addr in accounts_config.addresses() {
oxade marked this conversation as resolved.
Show resolved Hide resolved
let mut tmp = String::new();
write!(&mut tmp, "{:?}", addr).unwrap();
// Remove `=` symbol at the end
tmp = match tmp.strip_suffix('=') {
Some(x) => x.to_string(),
None => tmp,
};
writeln!(addr_text, "{}", tmp).unwrap();
}
print!("{}", addr_text);
oxade marked this conversation as resolved.
Show resolved Hide resolved
}

ClientCommands::QueryObjects { address } => {
let user_address = decode_address(&address).expect("Failed to decode address");

let mut rt = Runtime::new().unwrap();
Expand All @@ -440,10 +474,15 @@ fn main() {
send_timeout,
recv_timeout,
);
info!("Starting balance query");
info!("Starting object query");
let time_start = Instant::now();
let objects_ids = client_state.object_ids();
let time_total = time_start.elapsed().as_micros();
info!("Balance confirmed after {} us", time_total);
info!("ObjectIds confirmed after {} us", time_total);

for (obj_id, seq_num) in objects_ids {
println!("{:#x}: {:?}", obj_id, seq_num);
oxade marked this conversation as resolved.
Show resolved Hide resolved
}
accounts_config.update_from_state(&client_state);
accounts_config
.write(accounts_config_path)
Expand All @@ -461,7 +500,7 @@ fn main() {

let mut rt = Runtime::new().unwrap();
rt.block_on(async move {
warn!("Starting benchmark phase 1 (transfer orders)");
info!("Starting benchmark phase 1 (transfer orders)");
oxade marked this conversation as resolved.
Show resolved Hide resolved
let (orders, serialize_orders) =
make_benchmark_transfer_orders(&mut accounts_config, max_orders);
let responses = mass_broadcast_orders(
Expand All @@ -480,15 +519,15 @@ fn main() {
deserialize_response(&buf[..]).and_then(|info| info.pending_confirmation)
})
.collect();
warn!("Received {} valid votes.", votes.len());
info!("Received {} valid votes.", votes.len());

warn!("Starting benchmark phase 2 (confirmation orders)");
info!("Starting benchmark phase 2 (confirmation orders)");
let certificates = if let Some(files) = server_configs {
warn!("Using server configs provided by --server-configs");
let files = files.iter().map(AsRef::as_ref).collect();
make_benchmark_certificates_from_orders_and_server_configs(orders, files)
} else {
warn!("Using committee config");
info!("Using committee config");
make_benchmark_certificates_from_votes(&committee_config, votes)
};
let responses = mass_broadcast_orders(
Expand All @@ -512,13 +551,13 @@ fn main() {
}
None => acc,
});
warn!(
info!(
"Received {} valid confirmations for {} transfers.",
num_valid,
confirmed.len()
);

warn!("Updating local state of user accounts");
info!("Updating local state of user accounts");
// Make sure that the local balances are accurate so that future
// balance checks of the non-mass client pass.
mass_update_recipients(&mut accounts_config, certificates);
Expand All @@ -530,24 +569,27 @@ fn main() {
}

ClientCommands::CreateAccounts {
initial_objects,
oxade marked this conversation as resolved.
Show resolved Hide resolved
num,
initial_state_config_path,
} => {
let num_accounts: u32 = num;
let object_ids = match initial_objects {
Some(object_ids) => object_ids
.into_iter()
.map(|string| ObjectID::from_hex_literal(&string).unwrap())
.collect(),

None => Vec::new(),
};
let mut init_state_cfg: InitialStateConfig = InitialStateConfig::new();

for _ in 0..num_accounts {
let account = UserAccount::new(object_ids.clone());
println!("{}:{:?}", encode_address(&account.address), object_ids);
let obj_ids = create_random_object_ids();
let account = UserAccount::new(obj_ids.clone());

init_state_cfg.config.push(InitialStateConfigEntry {
address: account.address,
object_ids: obj_ids.clone(),
});

println!("{}:{:?}", encode_address(&account.address), obj_ids);
accounts_config.insert(account);
}
init_state_cfg
.write(initial_state_config_path.as_str())
.expect("Unable to write to initial state config file");
accounts_config
.write(accounts_config_path)
.expect("Unable to write user accounts");
Expand Down
51 changes: 29 additions & 22 deletions fastpay/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use fastx_types::{
use serde::{Deserialize, Serialize};
use std::{
collections::BTreeMap,
fs::{self, File, OpenOptions},
io::{BufRead, BufReader, BufWriter, Write},
fs::{self, read_to_string, File, OpenOptions},
io::{BufReader, BufWriter, Write},
};

#[derive(Clone, Debug, Serialize, Deserialize)]
Expand Down Expand Up @@ -140,6 +140,10 @@ impl AccountsConfig {
self.accounts.values_mut()
}

pub fn addresses(&mut self) -> impl Iterator<Item = &FastPayAddress> {
self.accounts.keys()
}

pub fn update_from_state<A>(&mut self, state: &ClientState<A>) {
let account = self
.accounts
Expand Down Expand Up @@ -197,34 +201,37 @@ impl AccountsConfig {
}
}

#[derive(Serialize, Deserialize)]
pub struct InitialStateConfigEntry {
pub address: FastPayAddress,
pub object_ids: Vec<ObjectID>,
}
#[derive(Serialize, Deserialize)]
pub struct InitialStateConfig {
pub accounts: Vec<(FastPayAddress, ObjectID)>,
pub config: Vec<InitialStateConfigEntry>,
}

impl InitialStateConfig {
pub fn new() -> Self {
Self { config: Vec::new() }
}

pub fn read(path: &str) -> Result<Self, anyhow::Error> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let mut accounts = Vec::new();
for line in reader.lines() {
let line = line?;
let elements = line.split(':').collect::<Vec<_>>();
if elements.len() != 2 {
anyhow::bail!("expecting two columns separated with ':'")
}
let address = decode_address(elements[0])?;
let object_id = ObjectID::from_hex_literal(elements[1])?;
accounts.push((address, object_id));
}
Ok(Self { accounts })
let raw_data: String = read_to_string(path)?.parse()?;

Ok(toml::from_str(&raw_data)?)
}

pub fn write(&self, path: &str) -> Result<(), std::io::Error> {
let file = OpenOptions::new().create(true).write(true).open(path)?;
let mut writer = BufWriter::new(file);
for (address, object_id) in &self.accounts {
writeln!(writer, "{}:{}", encode_address(address), object_id,)?;
}
let config = toml::to_string(self).unwrap();
oxade marked this conversation as resolved.
Show resolved Hide resolved

fs::write(path, config).expect("Unable to write to initial config file");
Ok(())
}
}

impl Default for InitialStateConfig {
fn default() -> Self {
Self::new()
}
}
21 changes: 13 additions & 8 deletions fastpay/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ fn make_shard_server(
let committee = Committee::new(committee_config.voting_rights());
let num_shards = server_config.authority.num_shards;

let state = AuthorityState::new_shard(
let mut state = AuthorityState::new_shard(
committee,
server_config.authority.address,
server_config.key.copy(),
Expand All @@ -40,14 +40,19 @@ fn make_shard_server(
);

// Load initial states
for (address, object_id) in &initial_accounts_config.accounts {
if AuthorityState::get_shard(num_shards, object_id) != shard {
continue;
for initial_state_cfg_entry in &initial_accounts_config.config {
let address = &initial_state_cfg_entry.address;

for object_id in initial_state_cfg_entry
.object_ids
.iter()
.filter(|oid| AuthorityState::get_shard(num_shards, oid) == shard)
{
let object = Object::with_id_owner_for_testing(*object_id, *address);

state.init_order_lock(object.to_object_reference());
state.insert_object(object);
}

let mut client = Object::with_id_for_testing(*object_id);
client.transfer(*address);
state.insert_object(client);
}

network::Server::new(
Expand Down