From 2f627a508ac344dce74fc3e15800ea9edcf3ed4c Mon Sep 17 00:00:00 2001 From: Arun Koshy Date: Thu, 24 Feb 2022 13:00:28 -0800 Subject: [PATCH 1/4] Add get addresses endpoint --- sui/src/rest_server.rs | 90 +++++++++++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 15 deletions(-) diff --git a/sui/src/rest_server.rs b/sui/src/rest_server.rs index fec79c0e6fa1a..708da8bd29db0 100644 --- a/sui/src/rest_server.rs +++ b/sui/src/rest_server.rs @@ -11,8 +11,7 @@ use serde_json::json; use sui::config::{Config, GenesisConfig, NetworkConfig, WalletConfig}; use sui::sui_commands; use sui::wallet_commands::WalletContext; -use sui_core::authority_client::AuthorityClient; -use sui_core::client::{Client, ClientState}; +use sui_core::client::Client; use sui_types::base_types::*; use sui_types::committee::Committee; @@ -48,6 +47,7 @@ async fn main() -> Result<(), String> { api.register(start).unwrap(); api.register(genesis).unwrap(); api.register(stop).unwrap(); + api.register(get_addresses).unwrap(); let api_context = ServerContext::new(); @@ -283,9 +283,13 @@ async fn start( format!("Can't create client state: {error}"), ) })?; - if let Err(err) = sync_client_state(client_state).await { - return Err(err); - } + + client_state.sync_client_state().await.map_err(|err| { + custom_http_error( + StatusCode::FAILED_DEPENDENCY, + format!("Sync error: {:?}", err), + ) + })?; } *server_context.wallet_context.lock().unwrap() = Some(wallet_context); @@ -321,17 +325,73 @@ async fn stop( Ok(HttpResponseUpdatedNoContent()) } -async fn sync_client_state( - client_state: &mut ClientState, -) -> Result<(), HttpError> { - // synchronize with authorities - let res = async move { client_state.sync_client_state().await }; - res.await.map_err(|err| { - custom_http_error( +/** + * `GetAddressResponse` represents the list of managed accounts for this client. + */ +#[derive(Deserialize, Serialize, JsonSchema)] +struct GetAddressResponse { + addresses: Vec, +} + +/** + * [WALLET] Retrieve all managed accounts. + */ +#[endpoint { + method = GET, + path = "/wallet/addresses", +}] +async fn get_addresses( + rqctx: Arc>, +) -> Result, HttpError> { + let server_context = rqctx.context(); + let wallet_context = server_context.wallet_context.lock().unwrap().take(); + if wallet_context.is_none() { + return Err(HttpError::for_client_error( + None, StatusCode::FAILED_DEPENDENCY, - format!("Sync error: {:?}", err), - ) - }) + "Wallet Context does not exist. Resync wallet via endpoint /wallet/addresses." + .to_string(), + )); + } + let mut wallet_context = wallet_context.unwrap(); + + let addresses: Vec = wallet_context + .address_manager + .get_managed_address_states() + .keys() + .copied() + .collect(); + + // Sync all accounts. + for address in addresses.iter() { + let client_state = match wallet_context.get_or_create_client_state(address) { + Ok(client_state) => client_state, + Err(error) => { + return Err(HttpError::for_client_error( + None, + StatusCode::FAILED_DEPENDENCY, + format!("Can't create client state: {error}"), + )) + } + }; + + client_state.sync_client_state().await.map_err(|err| { + custom_http_error( + StatusCode::FAILED_DEPENDENCY, + format!("Sync error: {:?}", err), + ) + })?; + } + + *server_context.wallet_context.lock().unwrap() = Some(wallet_context); + + // TODO: check if we should strip 'k#' as part of address output + Ok(HttpResponseOk(GetAddressResponse { + addresses: addresses + .into_iter() + .map(|address| format!("{:?}", address)) + .collect(), + })) } fn custom_http_error(status_code: http::StatusCode, message: String) -> HttpError { From 942347766221a9bca6bf3fe992d863a1506b820e Mon Sep 17 00:00:00 2001 From: Arun Koshy Date: Thu, 24 Feb 2022 13:03:13 -0800 Subject: [PATCH 2/4] small error output change --- sui/src/rest_server.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sui/src/rest_server.rs b/sui/src/rest_server.rs index 708da8bd29db0..d276c2a862b09 100644 --- a/sui/src/rest_server.rs +++ b/sui/src/rest_server.rs @@ -349,8 +349,7 @@ async fn get_addresses( return Err(HttpError::for_client_error( None, StatusCode::FAILED_DEPENDENCY, - "Wallet Context does not exist. Resync wallet via endpoint /wallet/addresses." - .to_string(), + "Wallet Context does not exist.".to_string(), )); } let mut wallet_context = wallet_context.unwrap(); From a1e335f072a0b20b7296cc0c445b865966d7ff7f Mon Sep 17 00:00:00 2001 From: Arun Koshy Date: Thu, 24 Feb 2022 19:02:18 -0800 Subject: [PATCH 3/4] add todos and correctly store wallet context on early exit --- sui/src/rest_server.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/sui/src/rest_server.rs b/sui/src/rest_server.rs index d276c2a862b09..1c263f69f2d95 100644 --- a/sui/src/rest_server.rs +++ b/sui/src/rest_server.rs @@ -344,6 +344,7 @@ async fn get_addresses( rqctx: Arc>, ) -> Result, HttpError> { let server_context = rqctx.context(); + // TODO: Find a better way to utilize wallet context here that does not require 'take()' let wallet_context = server_context.wallet_context.lock().unwrap().take(); if wallet_context.is_none() { return Err(HttpError::for_client_error( @@ -361,34 +362,35 @@ async fn get_addresses( .copied() .collect(); - // Sync all accounts. + // TODO: Speed up sync operations by kicking them off concurrently. + // Also need to investigate if this should be an automatic sync or manually triggered. for address in addresses.iter() { let client_state = match wallet_context.get_or_create_client_state(address) { Ok(client_state) => client_state, - Err(error) => { - return Err(HttpError::for_client_error( - None, + Err(err) => { + *server_context.wallet_context.lock().unwrap() = Some(wallet_context); + return Err(custom_http_error( StatusCode::FAILED_DEPENDENCY, - format!("Can't create client state: {error}"), - )) + format!("Can't create client state: {err}"), + )); } }; - client_state.sync_client_state().await.map_err(|err| { - custom_http_error( + if let Err(err) = client_state.sync_client_state().await { + *server_context.wallet_context.lock().unwrap() = Some(wallet_context); + return Err(custom_http_error( StatusCode::FAILED_DEPENDENCY, - format!("Sync error: {:?}", err), - ) - })?; + format!("Can't create client state: {err}"), + )); + } } *server_context.wallet_context.lock().unwrap() = Some(wallet_context); - // TODO: check if we should strip 'k#' as part of address output Ok(HttpResponseOk(GetAddressResponse { addresses: addresses .into_iter() - .map(|address| format!("{:?}", address)) + .map(|address| format!("{}", address)) .collect(), })) } From d002c0220516c93bd40e5a76c6d5537bb0259773 Mon Sep 17 00:00:00 2001 From: Arun Koshy Date: Fri, 25 Feb 2022 12:39:36 -0800 Subject: [PATCH 4/4] small ok_or_else refactor --- sui/src/rest_server.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sui/src/rest_server.rs b/sui/src/rest_server.rs index 1c263f69f2d95..72859482338fb 100644 --- a/sui/src/rest_server.rs +++ b/sui/src/rest_server.rs @@ -346,14 +346,13 @@ async fn get_addresses( let server_context = rqctx.context(); // TODO: Find a better way to utilize wallet context here that does not require 'take()' let wallet_context = server_context.wallet_context.lock().unwrap().take(); - if wallet_context.is_none() { - return Err(HttpError::for_client_error( + let mut wallet_context = wallet_context.ok_or_else(|| { + HttpError::for_client_error( None, StatusCode::FAILED_DEPENDENCY, "Wallet Context does not exist.".to_string(), - )); - } - let mut wallet_context = wallet_context.unwrap(); + ) + })?; let addresses: Vec = wallet_context .address_manager