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

Feature/support move calls from client #157

Merged
merged 26 commits into from
Jan 15, 2022
Merged
Show file tree
Hide file tree
Changes from 25 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
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ do
./server --server server"$I".json generate --host 127.0.0.1 --port 9"$I"00 --database-path ./db"$I" >> committee.json
done

# Create configuration files for 100 user accounts, with 4 gas objects per account and 200 value each.
# Create configuration files for 100 user accounts, with 10 gas objects per account and 2000000 value each.
# * Private account states are stored in one local wallet `accounts.json`.
# * `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 --num 100 \
--gas-objs-per-account 4 --value-per-per-obj 200 initial_accounts.toml
--gas-objs-per-account 10 --value-per-per-obj 2000000 initial_accounts.toml
# Start servers
for I in 1 2 3 4
do
Expand All @@ -45,18 +45,18 @@ done
# 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"
./client --committee committee.json --accounts accounts.json query-objects --address "$ACCOUNT1"
./client --committee committee.json --accounts accounts.json query-objects --address "$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 }'`

ACCOUNT1_OBJECT1=`./client --committee committee.json --accounts accounts.json query-objects --address "$ACCOUNT1" | head -n 1 | awk -F: '{ print $1 }'`
# Pick the last item as gas object for Account1
ACCOUNT1_GAS_OBJECT=`./client --committee committee.json --accounts accounts.json query-objects --address "$ACCOUNT1" | tail -n -1 | awk -F: '{ print $1 }'`
# Transfer object by ObjectID
./client --committee committee.json --accounts accounts.json transfer "$ACCOUNT1_OBJECT1" --from "$ACCOUNT1" --to "$ACCOUNT2"

./client --committee committee.json --accounts accounts.json transfer "$ACCOUNT1_OBJECT1" "$ACCOUNT1_GAS_OBJECT" --to "$ACCOUNT2"
# Query objects again
./client --committee committee.json --accounts accounts.json query-objects "$ACCOUNT1"
./client --committee committee.json --accounts accounts.json query-objects "$ACCOUNT2"
./client --committee committee.json --accounts accounts.json query-objects --address "$ACCOUNT1"
./client --committee committee.json --accounts accounts.json query-objects --address "$ACCOUNT2"

# Launch local benchmark using all user accounts
./client --committee committee.json --accounts accounts.json benchmark
Expand Down
8 changes: 8 additions & 0 deletions fastpay/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,20 @@ toml = "0.5.8"
strum = "0.23.0"
strum_macros = "0.23.1"
num_cpus = "1.13.1"
base64 = "0.13.0"
oxade marked this conversation as resolved.
Show resolved Hide resolved
ed25519-dalek = { version = "1.0.1", features = ["batch", "serde"] }
rocksdb = "0.17.0"
hex = "0.4.3"

bcs = "0.1.3"
fastpay_core = { path = "../fastpay_core" }
fastx-adapter = { path = "../fastx_programmability/adapter" }
fastx-types = { path = "../fastx_types" }

move-package = { git = "https://github.com/diem/move", rev="98ed299a7e3a9223019c9bdf4dd92fea9faef860" }
move-core-types = { git = "https://github.com/diem/move", rev="98ed299a7e3a9223019c9bdf4dd92fea9faef860" }
move-bytecode-verifier = { git = "https://github.com/diem/move", rev="98ed299a7e3a9223019c9bdf4dd92fea9faef860" }

[[bin]]
name = "client"
path = "src/client.rs"
Expand Down
191 changes: 169 additions & 22 deletions fastpay/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use fastpay::{config::*, network, transport};
use fastpay_core::client::*;
use fastx_types::{base_types::*, committee::Committee, messages::*, serialize::*};
use move_core_types::transaction_argument::convert_txn_args;

use bytes::Bytes;
use futures::stream::StreamExt;
Expand Down Expand Up @@ -283,6 +284,33 @@ fn deserialize_response(response: &[u8]) -> Option<ObjectInfoResponse> {
}
}
}
fn find_cached_owner_by_object_id(
account_config: &AccountsConfig,
object_id: ObjectID,
) -> Option<&PublicKeyBytes> {
account_config
.find_account(&object_id)
.map(|acc| &acc.address)
}

fn show_object_effects(order_effects: OrderEffects) {
if !order_effects.mutated.is_empty() {
println!("Mutated Objects:");
for obj in order_effects.mutated {
println!("{:?} {:?} {:?}", obj.0, obj.1, obj.2);
}
}
if !order_effects.deleted.is_empty() {
println!("Deleted Objects:");
for obj in order_effects.deleted {
println!("{:?} {:?} {:?}", obj.0, obj.1, obj.2);
}
}
}

fn parse_public_key_bytes(src: &str) -> Result<PublicKeyBytes, hex::FromHexError> {
decode_address_hex(src)
}

#[derive(StructOpt)]
#[structopt(
Expand Down Expand Up @@ -319,22 +347,30 @@ struct ClientOpt {
#[derive(StructOpt)]
#[structopt(rename_all = "kebab-case")]
enum ClientCommands {
/// Get obj info
#[structopt(name = "get-obj-info")]
GetObjInfo { obj_id: ObjectID },

/// Call Move
#[structopt(name = "call")]
Call { path: String },

/// Transfer funds
#[structopt(name = "transfer")]
Transfer {
/// Sending address (must be one of our accounts)
#[structopt(long)]
from: String,
#[structopt(long, parse(try_from_str = parse_public_key_bytes))]
from: PublicKeyBytes,

/// Recipient address
#[structopt(long)]
to: String,
#[structopt(long, parse(try_from_str = parse_public_key_bytes))]
to: PublicKeyBytes,

/// Object to transfer, in 20 bytes Hex string
object_id: String,
object_id: ObjectID,

/// ID of the gas object for gas payment, in 20 bytes Hex string
gas_object_id: String,
gas_object_id: ObjectID,
},

/// Obtain the Account Addresses
Expand All @@ -345,7 +381,8 @@ enum ClientCommands {
#[structopt(name = "query-objects")]
QueryObjects {
/// Address of the account
address: String,
#[structopt(long, parse(try_from_str = parse_public_key_bytes))]
address: PublicKeyBytes,
},

/// Send one transfer per account in bulk mode
Expand Down Expand Up @@ -389,7 +426,6 @@ enum ClientCommands {
fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
let options = ClientOpt::from_args();

let send_timeout = Duration::from_micros(options.send_timeout);
let recv_timeout = Duration::from_micros(options.recv_timeout);
let accounts_config_path = &options.accounts;
Expand All @@ -402,31 +438,144 @@ fn main() {
CommitteeConfig::read(committee_config_path).expect("Unable to read committee config file");

match options.cmd {
ClientCommands::GetObjInfo { obj_id } => {
// Pick the first (or any) account for use in finding obj info
let account = accounts_config
.nth_account(0)
oxade marked this conversation as resolved.
Show resolved Hide resolved
.expect("Account config is invalid")
.address;
// Fetch the object ref
let mut client_state = make_client_state(
&accounts_config,
&committee_config,
account,
buffer_size,
send_timeout,
recv_timeout,
);
let rt = Runtime::new().unwrap();
rt.block_on(async move {
// Fetch the object info for the object
let obj_info_req = ObjectInfoRequest {
object_id: obj_id,
request_sequence_number: None,
request_received_transfers_excluding_first_nth: None,
};
let obj_info = client_state.get_object_info(obj_info_req).await.unwrap();
println!("Owner: {:#?}", obj_info.object.owner);
println!("Version: {:#?}", obj_info.object.version().value());
println!("ID: {:#?}", obj_info.object.id());
println!("Readonly: {:#?}", obj_info.object.is_read_only());
println!(
"Type: {:#?}",
obj_info
.object
.data
.type_()
.map_or("Type Unwrap Failed".to_owned(), |type_| type_
.module
.as_ident_str()
.to_string())
);
});
}

ClientCommands::Call { path } => {
let config = MoveCallConfig::read(&path).unwrap();
// Find owner of gas object
let owner = find_cached_owner_by_object_id(&accounts_config, config.gas_object_id)
.expect("Cannot find owner for gas object");
oxade marked this conversation as resolved.
Show resolved Hide resolved

let mut client_state = make_client_state(
&accounts_config,
&committee_config,
*owner,
buffer_size,
send_timeout,
recv_timeout,
);

let rt = Runtime::new().unwrap();
rt.block_on(async move {
// Fetch the object info for the package
let package_obj_info_req = ObjectInfoRequest {
object_id: config.package_obj_id,
request_sequence_number: None,
request_received_transfers_excluding_first_nth: None,
};
let package_obj_info = client_state
.get_object_info(package_obj_info_req)
.await
.unwrap();
let package_obj_ref = package_obj_info.object.to_object_reference();

// Fetch the object info for the gas obj
let gas_obj_info_req = ObjectInfoRequest {
object_id: config.gas_object_id,
request_sequence_number: None,
request_received_transfers_excluding_first_nth: None,
};

let gas_obj_info = client_state
.get_object_info(gas_obj_info_req)
.await
.unwrap();
let gas_obj_ref = gas_obj_info.object.to_object_reference();

// Fetch the objects for the object args
let mut object_args_refs = Vec::new();
for obj_id in config.object_args_ids {
// Fetch the obj ref
let obj_info_req = ObjectInfoRequest {
object_id: obj_id,
request_sequence_number: None,
request_received_transfers_excluding_first_nth: None,
};

let obj_info = client_state.get_object_info(obj_info_req).await.unwrap();
object_args_refs.push(obj_info.object.to_object_reference());
}

let pure_args = convert_txn_args(&config.pure_args);

let call_ret = client_state
.move_call(
package_obj_ref,
config.module,
config.function,
config.type_args,
gas_obj_ref,
object_args_refs,
pure_args,
config.gas_budget,
)
.await
.unwrap();
println!("Cert: {:?}", call_ret.0);
show_object_effects(call_ret.1);
});
}

ClientCommands::Transfer {
from,
to,
object_id,
gas_object_id,
} => {
let sender = decode_address(&from).expect("Failed to decode sender's address");
let recipient = decode_address(&to).expect("Failed to decode recipient's address");
let object_id = ObjectID::from_hex_literal(&object_id).unwrap();
let gas_object_id = ObjectID::from_hex_literal(&gas_object_id).unwrap();

let rt = Runtime::new().unwrap();
rt.block_on(async move {
let mut client_state = make_client_state(
&accounts_config,
&committee_config,
sender,
from,
buffer_size,
send_timeout,
recv_timeout,
);
info!("Starting transfer");
let time_start = Instant::now();
let cert = client_state
.transfer_object(object_id, gas_object_id, recipient)
.transfer_object(object_id, gas_object_id, to)
.await
.unwrap();
let time_total = time_start.elapsed().as_micros();
Expand All @@ -437,7 +586,7 @@ fn main() {
let mut recipient_client_state = make_client_state(
&accounts_config,
&committee_config,
recipient,
to,
buffer_size,
send_timeout,
recv_timeout,
Expand All @@ -455,21 +604,19 @@ fn main() {
let addr_strings: Vec<_> = accounts_config
.addresses()
.into_iter()
.map(|addr| format!("{:?}", addr).trim_end_matches('=').to_string())
.map(|addr| format!("{:X}", addr).trim_end_matches('=').to_string())
.collect();
let addr_text = addr_strings.join("\n");
println!("{}", addr_text);
}

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

let rt = Runtime::new().unwrap();
rt.block_on(async move {
let client_state = make_client_state(
&accounts_config,
&committee_config,
user_address,
address,
buffer_size,
send_timeout,
recv_timeout,
Expand All @@ -483,7 +630,7 @@ fn main() {
.expect("Unable to write user accounts");

for (obj_id, seq_num) in objects_ids {
println!("{:#x}: {:?}", obj_id, seq_num);
println!("{}: {:?}", obj_id, seq_num);
}
});
}
Expand Down Expand Up @@ -543,7 +690,7 @@ fn main() {
.iter()
.fold(0, |acc, buf| match deserialize_response(&buf[..]) {
Some(info) => {
confirmed.insert(info.object_id);
confirmed.insert(info.object.id());
acc + 1
}
None => acc,
Expand Down
Loading