Skip to content

Commit

Permalink
Feature/support move calls from client (#157)
Browse files Browse the repository at this point in the history
* Support move Calls from client and added object fetching from authorities
  • Loading branch information
oxade authored Jan 15, 2022
1 parent f97e0a8 commit 7eb0760
Show file tree
Hide file tree
Showing 13 changed files with 562 additions and 71 deletions.
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"
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)
.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");

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

1 comment on commit 7eb0760

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bench results

�[0m�[0m�[1m�[32m Finished�[0m release [optimized + debuginfo] target(s) in 3.18s
�[0m�[0m�[1m�[32m Running�[0m target/release/bench
[2022-01-15T21:12:07Z INFO bench] Starting benchmark: OrdersAndCerts
[2022-01-15T21:12:07Z INFO bench] Preparing accounts.
[2022-01-15T21:12:07Z INFO bench] Open database on path: "/tmp/DB_90308A1BEAC04DB27A4766BD51BCF2ED"
[2022-01-15T21:12:11Z INFO bench] Preparing transactions.
[2022-01-15T21:12:21Z INFO fastpay::network] Listening to Tcp traffic on 127.0.0.1:9555
[2022-01-15T21:12:22Z INFO bench] Number of TCP connections: 2
[2022-01-15T21:12:22Z INFO bench] Set max_in_flight to 500
[2022-01-15T21:12:22Z INFO bench] Sending requests.
[2022-01-15T21:12:22Z INFO fastpay::network] Sending Tcp requests to 127.0.0.1:9555
[2022-01-15T21:12:23Z INFO fastpay::network] 127.0.0.1:9555 has processed 5000 packets
[2022-01-15T21:12:24Z INFO fastpay::network] In flight 500 Remaining 35000
[2022-01-15T21:12:24Z INFO fastpay::network] 127.0.0.1:9555 has processed 10000 packets
[2022-01-15T21:12:25Z INFO fastpay::network] 127.0.0.1:9555 has processed 15000 packets
[2022-01-15T21:12:26Z INFO fastpay::network] In flight 500 Remaining 30000
[2022-01-15T21:12:26Z INFO fastpay::network] 127.0.0.1:9555 has processed 20000 packets
[2022-01-15T21:12:27Z INFO fastpay::network] 127.0.0.1:9555 has processed 25000 packets
[2022-01-15T21:12:27Z INFO fastpay::network] In flight 500 Remaining 25000
[2022-01-15T21:12:27Z INFO fastpay::network] 127.0.0.1:9555 has processed 30000 packets
[2022-01-15T21:12:28Z INFO fastpay::network] 127.0.0.1:9555 has processed 35000 packets
[2022-01-15T21:12:29Z INFO fastpay::network] In flight 500 Remaining 20000
[2022-01-15T21:12:29Z INFO fastpay::network] 127.0.0.1:9555 has processed 40000 packets
[2022-01-15T21:12:30Z INFO fastpay::network] 127.0.0.1:9555 has processed 45000 packets
[2022-01-15T21:12:31Z INFO fastpay::network] In flight 500 Remaining 15000
[2022-01-15T21:12:31Z INFO fastpay::network] 127.0.0.1:9555 has processed 50000 packets
[2022-01-15T21:12:32Z INFO fastpay::network] 127.0.0.1:9555 has processed 55000 packets
[2022-01-15T21:12:33Z INFO fastpay::network] In flight 500 Remaining 10000
[2022-01-15T21:12:33Z INFO fastpay::network] 127.0.0.1:9555 has processed 60000 packets
[2022-01-15T21:12:34Z INFO fastpay::network] 127.0.0.1:9555 has processed 65000 packets
[2022-01-15T21:12:35Z INFO fastpay::network] In flight 500 Remaining 5000
[2022-01-15T21:12:35Z INFO fastpay::network] 127.0.0.1:9555 has processed 70000 packets
[2022-01-15T21:12:36Z INFO fastpay::network] 127.0.0.1:9555 has processed 75000 packets
[2022-01-15T21:12:37Z INFO fastpay::network] Done sending Tcp requests to 127.0.0.1:9555
[2022-01-15T21:12:37Z INFO fastpay::network] 127.0.0.1:9555 has processed 80000 packets
[2022-01-15T21:12:37Z INFO bench] Received 80000 responses.
[2022-01-15T21:12:37Z WARN bench] Completed benchmark for OrdersAndCerts
Total time: 14638859us, items: 40000, tx/sec: 2732.453396811869

Please sign in to comment.