Skip to content

Commit

Permalink
Feature/client manage multi addresses: Part 1 (#428)
Browse files Browse the repository at this point in the history
* Add address manager
  • Loading branch information
oxade authored Feb 11, 2022
1 parent 346105c commit 13ccdd2
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 112 deletions.
2 changes: 1 addition & 1 deletion sui/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ async fn main() -> Result<(), anyhow::Error> {
.iter()
.map(|info| info.address)
.collect::<Vec<_>>();
let mut context = WalletContext::new(config);
let mut context = WalletContext::new(config)?;

// Sync all accounts on start up.
for address in addresses {
Expand Down
117 changes: 51 additions & 66 deletions sui/src/wallet_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::config::{AccountInfo, AuthorityInfo, WalletConfig};
use sui_core::authority_client::AuthorityClient;
use sui_core::client::{Client, ClientState};
use sui_core::client::{Client, ClientAddressManager, ClientState};
use sui_network::network::NetworkClient;
use sui_types::base_types::{
decode_address_hex, encode_address_hex, get_key_pair, AuthorityName, ObjectID, PublicKeyBytes,
Expand All @@ -18,6 +18,7 @@ use move_core_types::parser::{parse_transaction_argument, parse_type_tag};
use move_core_types::transaction_argument::{convert_txn_args, TransactionArgument};
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::{Duration, Instant};
use structopt::clap::AppSettings;
use structopt::StructOpt;
Expand All @@ -35,6 +36,10 @@ pub enum WalletCommands {
/// Get obj info
#[structopt(name = "object")]
Object {
/// Owner address
#[structopt(long, parse(try_from_str = decode_address_hex))]
owner: PublicKeyBytes,

/// Object ID of the object to fetch
#[structopt(long)]
id: ObjectID,
Expand All @@ -47,6 +52,10 @@ pub enum WalletCommands {
/// Publish Move modules
#[structopt(name = "publish")]
Publish {
/// Sender address
#[structopt(long, parse(try_from_str = decode_address_hex))]
sender: PublicKeyBytes,

/// Path to directory containing a Move package
#[structopt(long)]
path: String,
Expand All @@ -63,6 +72,9 @@ pub enum WalletCommands {
/// Call Move
#[structopt(name = "call")]
Call {
/// Sender address
#[structopt(long, parse(try_from_str = decode_address_hex))]
sender: PublicKeyBytes,
/// Object ID of the package, which contains the module
#[structopt(long)]
package: ObjectID,
Expand Down Expand Up @@ -95,6 +107,10 @@ pub enum WalletCommands {
/// Transfer funds
#[structopt(name = "transfer")]
Transfer {
/// Sender address
#[structopt(long, parse(try_from_str = decode_address_hex))]
from: PublicKeyBytes,

/// Recipient address
#[structopt(long, parse(try_from_str = decode_address_hex))]
to: PublicKeyBytes,
Expand Down Expand Up @@ -135,21 +151,19 @@ impl WalletCommands {
pub async fn execute(&mut self, context: &mut WalletContext) -> Result<(), anyhow::Error> {
match self {
WalletCommands::Publish {
sender,
path,
gas,
gas_budget,
} => {
// Find owner of gas object
let owner = context.find_owner(gas)?;
let client_state = context.get_or_create_client_state(&owner)?;
let client_state = context.get_or_create_client_state(sender)?;
publish(client_state, path.clone(), *gas, *gas_budget).await;
}

WalletCommands::Object { id, deep } => {
// Pick the first (or any) account for use in finding obj info
let account = context.find_owner(id)?;
WalletCommands::Object { id, deep, owner } => {
// Fetch the object ref
let client_state = context.get_or_create_client_state(&account)?;
let client_state = context.get_or_create_client_state(owner)?;
let object_read = client_state.get_object_info(*id).await?;
let object = object_read.object()?;
println!("{}", object);
Expand All @@ -158,6 +172,7 @@ impl WalletCommands {
}
}
WalletCommands::Call {
sender,
package,
module,
function,
Expand All @@ -167,9 +182,7 @@ impl WalletCommands {
gas,
gas_budget,
} => {
// Find owner of gas object
let sender = context.find_owner(gas)?;
let client_state = context.get_or_create_client_state(&sender)?;
let client_state = context.get_or_create_client_state(sender)?;

let package_obj_info = client_state.get_object_info(*package).await?;
let package_obj_ref = package_obj_info.object().unwrap().to_object_reference();
Expand Down Expand Up @@ -203,10 +216,13 @@ impl WalletCommands {
show_object_effects(effects);
}

WalletCommands::Transfer { to, object_id, gas } => {
let owner = context.find_owner(gas)?;

let client_state = context.get_or_create_client_state(&owner)?;
WalletCommands::Transfer {
to,
object_id,
gas,
from,
} => {
let client_state = context.get_or_create_client_state(from)?;
info!("Starting transfer");
let time_start = Instant::now();
let cert = client_state
Expand All @@ -219,14 +235,15 @@ impl WalletCommands {
}

WalletCommands::Addresses => {
let addr_strings: Vec<_> = context
.config
.accounts
let addr_strings = context
.address_manager
.get_managed_address_states()
.iter()
.map(|account| encode_address_hex(&account.address))
.collect();
.map(|(a, _)| encode_address_hex(a))
.collect::<Vec<_>>();

let addr_text = addr_strings.join("\n");
println!("Showing {} results.", context.config.accounts.len());
println!("Showing {} results.", addr_strings.len());
println!("{}", addr_text);
}

Expand All @@ -250,6 +267,8 @@ impl WalletCommands {
key_pair: key,
});
context.config.save()?;
// Create an address to be managed
let _ = context.get_or_create_client_state(&address)?;
println!(
"Created new keypair for address : {}",
encode_address_hex(&address)
Expand Down Expand Up @@ -327,51 +346,23 @@ fn make_authority_clients(

pub struct WalletContext {
pub config: WalletConfig,
pub client_states: BTreeMap<SuiAddress, ClientState<AuthorityClient>>,
pub address_manager: ClientAddressManager<AuthorityClient>,
}

impl WalletContext {
pub fn new(config: WalletConfig) -> Self {
Self {
pub fn new(config: WalletConfig) -> Result<Self, anyhow::Error> {
let path = config.db_folder_path.clone();
Ok(Self {
config,
client_states: Default::default(),
}
}

pub fn find_owner(&self, object_id: &ObjectID) -> Result<SuiAddress, SuiError> {
self.client_states
.iter()
.find_map(|(owner, client_state)| {
if client_state.get_owned_objects().contains(object_id) {
Some(owner)
} else {
None
}
})
.copied()
.ok_or(SuiError::ObjectNotFound {
object_id: *object_id,
})
}

pub fn get_or_create_client_state(
&mut self,
owner: &SuiAddress,
) -> Result<&mut ClientState<AuthorityClient>, anyhow::Error> {
Ok(if !self.client_states.contains_key(owner) {
let new_client = self.create_client_state(owner)?;
self.client_states.entry(*owner).or_insert(new_client)
} else {
self.client_states.get_mut(owner).unwrap()
address_manager: ClientAddressManager::new(PathBuf::from_str(&path)?)?,
})
}

fn create_client_state(
&self,
fn get_or_create_client_state(
&mut self,
owner: &SuiAddress,
) -> Result<ClientState<AuthorityClient>, SuiError> {
let client_info = self.get_account_info(owner)?;

) -> Result<&mut ClientState<AuthorityClient>, SuiError> {
let kp = Box::pin(self.get_account_cfg_info(owner)?.key_pair.copy());
let voting_rights = self
.config
.authorities
Expand All @@ -385,17 +376,11 @@ impl WalletContext {
self.config.send_timeout,
self.config.recv_timeout,
);
let path = PathBuf::from(format!("{}/{:?}", self.config.db_folder_path, owner));
ClientState::new(
path,
client_info.address,
Box::pin(client_info.key_pair.copy()),
committee,
authority_clients,
)
self.address_manager
.get_or_create_state_mut(*owner, kp, committee, authority_clients)
}

pub fn get_account_info(&self, address: &SuiAddress) -> Result<&AccountInfo, SuiError> {
pub fn get_account_cfg_info(&self, address: &SuiAddress) -> Result<&AccountInfo, SuiError> {
self.config
.accounts
.iter()
Expand Down
70 changes: 66 additions & 4 deletions sui_core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,59 @@ use std::{
pub type StableSyncSigner = Pin<Box<dyn signature::Signer<ed25519_dalek::Signature> + Send + Sync>>;

pub mod client_store;
use self::client_store::ClientStore;

#[cfg(test)]
use sui_types::SUI_FRAMEWORK_ADDRESS;

pub type AsyncResult<'a, T, E> = future::BoxFuture<'a, Result<T, E>>;

pub struct ClientAddressManager<A> {
store: client_store::ClientAddressManagerStore,
address_states: BTreeMap<SuiAddress, ClientState<A>>,
}
impl<A> ClientAddressManager<A> {
/// Create a new manager which stores its managed addresses at `path`
pub fn new(path: PathBuf) -> Result<Self, SuiError> {
Ok(Self {
store: client_store::ClientAddressManagerStore::open(path),
address_states: BTreeMap::new(),
})
}

/// Get (if exists) or create a new managed address state
pub fn get_or_create_state_mut(
&mut self,
address: SuiAddress,
secret: StableSyncSigner,
committee: Committee,
authority_clients: BTreeMap<AuthorityName, A>,
) -> Result<&mut ClientState<A>, SuiError> {
if let std::collections::btree_map::Entry::Vacant(e) = self.address_states.entry(address) {
// Load the records if available
let single_store = if self.store.is_managed_address(address)? {
// Unwrap is okay since we checked cond
self.store.get_managed_address(address)
} else {
self.store.manage_new_address(address)
}?;
e.insert(ClientState::new_for_manager(
address,
secret,
committee,
authority_clients,
single_store,
)?);
}

return Ok(self.address_states.get_mut(&address).unwrap());
}

/// Get all the states
pub fn get_managed_address_states(&self) -> &BTreeMap<PublicKeyBytes, ClientState<A>> {
&self.address_states
}
}

pub struct ClientState<AuthorityAPI> {
/// Our Sui address.
address: SuiAddress,
Expand All @@ -46,7 +92,7 @@ pub struct ClientState<AuthorityAPI> {
/// Authority entry point.
authorities: AuthorityAggregator<AuthorityAPI>,
/// Persistent store for client
store: ClientStore,
store: client_store::ClientSingleAddressStore,
}

// Operations are considered successful when they successfully reach a quorum of authorities.
Expand Down Expand Up @@ -102,6 +148,7 @@ impl<A> ClientState<A> {
/// right after constructor to fetch missing info form authorities
/// TODO: client should manage multiple addresses instead of each addr having DBs
/// https://github.com/MystenLabs/fastnft/issues/332
#[cfg(test)]
pub fn new(
path: PathBuf,
address: SuiAddress,
Expand All @@ -113,7 +160,22 @@ impl<A> ClientState<A> {
address,
secret,
authorities: AuthorityAggregator::new(committee, authority_clients),
store: ClientStore::new(path),
store: client_store::ClientSingleAddressStore::new(path),
})
}

pub fn new_for_manager(
address: SuiAddress,
secret: StableSyncSigner,
committee: Committee,
authority_clients: BTreeMap<AuthorityName, A>,
store: client_store::ClientSingleAddressStore,
) -> Result<Self, SuiError> {
Ok(ClientState {
address,
secret,
authorities: AuthorityAggregator::new(committee, authority_clients),
store,
})
}

Expand Down Expand Up @@ -216,7 +278,7 @@ impl<A> ClientState<A> {
}

#[cfg(test)]
pub fn store(&self) -> &ClientStore {
pub fn store(&self) -> &client_store::ClientSingleAddressStore {
&self.store
}

Expand Down
Loading

1 comment on commit 13ccdd2

@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 1.24s
�[0m�[0m�[1m�[32m Running�[0m target/release/bench
�[2m2022-02-11T23:32:29.427973Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Starting benchmark: OrdersAndCerts
�[2m2022-02-11T23:32:29.427993Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Preparing accounts.
�[2m2022-02-11T23:32:29.429712Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Open database on path: "/tmp/DB_24C81997704CE16E37D9BAE0B7B4F9628D52EC6B"
�[2m2022-02-11T23:32:34.453678Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Preparing transactions.
�[2m2022-02-11T23:32:43.076909Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m Listening to TCP traffic on 127.0.0.1:9555
�[2m2022-02-11T23:32:44.078342Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Number of TCP connections: 2
�[2m2022-02-11T23:32:44.078379Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Set max_in_flight to 500
�[2m2022-02-11T23:32:44.078385Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Sending requests.
�[2m2022-02-11T23:32:44.084653Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m Sending TCP requests to 127.0.0.1:9555
�[2m2022-02-11T23:32:44.090211Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m Sending TCP requests to 127.0.0.1:9555
�[2m2022-02-11T23:32:44.885429Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 5000 packets
�[2m2022-02-11T23:32:45.611030Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 35000
�[2m2022-02-11T23:32:45.613264Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 35000
�[2m2022-02-11T23:32:45.692518Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 10000 packets
�[2m2022-02-11T23:32:46.486086Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 15000 packets
�[2m2022-02-11T23:32:47.220976Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 30000
�[2m2022-02-11T23:32:47.227707Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 30000
�[2m2022-02-11T23:32:47.284447Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 20000 packets
�[2m2022-02-11T23:32:48.087209Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 25000 packets
�[2m2022-02-11T23:32:48.819028Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 25000
�[2m2022-02-11T23:32:48.846150Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 25000
�[2m2022-02-11T23:32:48.923030Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 30000 packets
�[2m2022-02-11T23:32:49.743114Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 35000 packets
�[2m2022-02-11T23:32:50.460514Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 20000
�[2m2022-02-11T23:32:50.503970Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 20000
�[2m2022-02-11T23:32:50.548829Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 40000 packets
�[2m2022-02-11T23:32:51.357050Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 45000 packets
�[2m2022-02-11T23:32:52.068828Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 15000
�[2m2022-02-11T23:32:52.123194Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 15000
�[2m2022-02-11T23:32:52.165822Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 50000 packets
�[2m2022-02-11T23:32:52.973666Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 55000 packets
�[2m2022-02-11T23:32:53.672578Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 10000
�[2m2022-02-11T23:32:53.738850Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 10000
�[2m2022-02-11T23:32:53.779040Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 60000 packets
�[2m2022-02-11T23:32:54.587845Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 65000 packets
�[2m2022-02-11T23:32:55.263372Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 5000
�[2m2022-02-11T23:32:55.374819Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m In flight 500 Remaining 5000
�[2m2022-02-11T23:32:55.396368Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 70000 packets
�[2m2022-02-11T23:32:56.205287Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 75000 packets
�[2m2022-02-11T23:32:57.029803Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m Done sending TCP requests to 127.0.0.1:9555
�[2m2022-02-11T23:32:57.053969Z�[0m �[32m INFO�[0m �[2msui_core::authority_server�[0m�[2m:�[0m 127.0.0.1:9555 has processed 80000 packets
�[2m2022-02-11T23:32:57.097495Z�[0m �[32m INFO�[0m �[2msui_network::network�[0m�[2m:�[0m Done sending TCP requests to 127.0.0.1:9555
�[2m2022-02-11T23:32:57.098336Z�[0m �[32m INFO�[0m �[2mbench�[0m�[2m:�[0m Received 80000 responses.
�[2m2022-02-11T23:32:57.254598Z�[0m �[33m WARN�[0m �[2mbench�[0m�[2m:�[0m Completed benchmark for OrdersAndCerts
Total time: 13019935us, items: 40000, tx/sec: 3072.2119580474096

Please sign in to comment.