Skip to content

Commit

Permalink
feat(wallet): uses tip height to calc abs acceptance period (#4271)
Browse files Browse the repository at this point in the history
Description
---
- fetches tip height from base node to determine the absolute height for the VN acceptance period

Motivation and Context
---
When using `init-constitution` command used an absolute height for the acceptance period, which made it easy to 
make the mistake of creating a constitution that expires immediately.  

This PR fetches the tip height from the base node and uses that, if able, to make the height relative. If not able, the user is warned and prompted to enter an ABSOLUTE height.

How Has This Been Tested?
---
Manually
  • Loading branch information
sdbondi authored Jul 7, 2022
1 parent c2576a9 commit 480d55d
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 11 deletions.
66 changes: 60 additions & 6 deletions applications/tari_console_wallet/src/automation/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ use tari_wallet::{
ContractUpdateProposalFileFormat,
SignatureFileFormat,
},
connectivity_service::WalletConnectivityInterface,
error::WalletError,
key_manager_service::{KeyManagerInterface, NextKeyResult},
output_manager_service::{handle::OutputManagerHandle, resources::OutputManagerKeyManagerBranch},
Expand Down Expand Up @@ -811,8 +812,8 @@ pub async fn command_runner(
async fn handle_contract_command(wallet: &WalletSqlite, command: ContractCommand) -> Result<(), CommandError> {
match command.subcommand {
ContractSubcommand::InitDefinition(args) => init_contract_definition_spec(wallet, args).await,
ContractSubcommand::InitConstitution(args) => init_contract_constitution_spec(args),
ContractSubcommand::InitUpdateProposal(args) => init_contract_update_proposal_spec(args),
ContractSubcommand::InitConstitution(args) => init_contract_constitution_spec(wallet, args).await,
ContractSubcommand::InitUpdateProposal(args) => init_contract_update_proposal_spec(wallet, args).await,
ContractSubcommand::InitAmendment(args) => init_contract_amendment_spec(args),
ContractSubcommand::PublishDefinition(args) => publish_contract_definition(wallet, args).await,
ContractSubcommand::PublishConstitution(args) => publish_contract_constitution(wallet, args).await,
Expand Down Expand Up @@ -906,7 +907,10 @@ async fn init_contract_definition_spec(wallet: &WalletSqlite, args: InitDefiniti
Ok(())
}

fn init_contract_constitution_spec(args: InitConstitutionArgs) -> Result<(), CommandError> {
async fn init_contract_constitution_spec(
wallet: &WalletSqlite,
args: InitConstitutionArgs,
) -> Result<(), CommandError> {
if args.dest_path.exists() {
if args.force {
println!("{} exists and will be overwritten.", args.dest_path.to_string_lossy());
Expand All @@ -924,10 +928,26 @@ fn init_contract_constitution_spec(args: InitConstitutionArgs) -> Result<(), Com
.skip_if_some(args.contract_id)
.ask_parsed()?;
let committee: Vec<String> = Prompt::new("Validator committee ids (hex):").ask_repeatedly()?;
let acceptance_period_expiry = Prompt::new("Acceptance period expiry (in blocks, integer):")
println!("🔗 Waiting for connection to base node...");
let tip_height = get_tip_height(wallet).await;
let prompt = if let Some(tip) = tip_height {
println!("🔗 Connected. Tip is at {}", tip);
Prompt::new("Acceptance period expiry (RELATIVE block height):")
} else {
println!("⚠️ Not online. Unable to determine current tip height");
Prompt::new("Acceptance period expiry (ABSOLUTE block height):")
};
let mut acceptance_period_expiry = prompt
.skip_if_some(args.acceptance_period_expiry)
.with_default("50")
.ask_parsed()?;
if let Some(tip) = tip_height {
acceptance_period_expiry += tip;
}
println!(
"ℹ️ Acceptance period expiry is set to absolute height {}",
acceptance_period_expiry
);
let minimum_quorum_required = Prompt::new("Minimum quorum:")
.skip_if_some(args.minimum_quorum_required)
.with_default(committee.len().to_string())
Expand Down Expand Up @@ -957,7 +977,10 @@ fn init_contract_constitution_spec(args: InitConstitutionArgs) -> Result<(), Com
Ok(())
}

fn init_contract_update_proposal_spec(args: InitUpdateProposalArgs) -> Result<(), CommandError> {
async fn init_contract_update_proposal_spec(
wallet: &WalletSqlite,
args: InitUpdateProposalArgs,
) -> Result<(), CommandError> {
if args.dest_path.exists() {
if args.force {
println!("{} exists and will be overwritten.", args.dest_path.to_string_lossy());
Expand All @@ -977,10 +1000,23 @@ fn init_contract_update_proposal_spec(args: InitUpdateProposalArgs) -> Result<()
.with_default("0".to_string())
.ask_parsed()?;
let committee: Vec<String> = Prompt::new("Validator committee ids (hex):").ask_repeatedly()?;
let acceptance_period_expiry = Prompt::new("Acceptance period expiry (in blocks, integer):")
println!("🔗 Waiting for connection to base node...");
let tip_height = get_tip_height(wallet).await;
let prompt = if let Some(tip) = tip_height {
println!("🔗 Connected. Tip is at {}", tip);
Prompt::new("Acceptance period expiry (RELATIVE block height):")
} else {
println!("⚠️ Not online. Unable to determine current tip height");
Prompt::new("Acceptance period expiry (ABSOLUTE block height):")
};
let mut acceptance_period_expiry = prompt
.skip_if_some(args.acceptance_period_expiry)
.with_default("50".to_string())
.ask_parsed()?;
if let Some(tip) = tip_height {
acceptance_period_expiry += tip;
}
println!("ℹ️ Acceptance period expiry is at height {}", acceptance_period_expiry);
let minimum_quorum_required = Prompt::new("Minimum quorum:")
.skip_if_some(args.minimum_quorum_required)
.with_default(committee.len().to_string())
Expand Down Expand Up @@ -1257,3 +1293,21 @@ fn write_to_issuer_key_file<P: AsRef<Path>>(path: P, key_result: NextKeyResult)
write_json_file(path, &root).map_err(|e| CommandError::JsonFile(e.to_string()))?;
Ok(())
}

async fn get_tip_height(wallet: &WalletSqlite) -> Option<u64> {
let client = wallet
.wallet_connectivity
.clone()
.obtain_base_node_wallet_rpc_client_timeout(Duration::from_secs(10))
.await;

match client {
Some(mut client) => client
.get_tip_info()
.await
.ok()
.and_then(|t| t.metadata)
.and_then(|m| m.height_of_longest_chain),
None => None,
}
}
9 changes: 4 additions & 5 deletions applications/tari_console_wallet/src/automation/prompt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,12 @@ impl Prompt {
io::stdin().read_line(&mut line_buf)?;
println!();

let trimmed = line_buf.trim();

match trimmed {
"N" => {
let trimmed = line_buf.trim().to_lowercase();
match trimmed.as_str() {
"n" => {
return Ok(collection);
},
"Y" => break,
"y" => break,
_ => continue,
}
}
Expand Down
12 changes: 12 additions & 0 deletions base_layer/wallet/src/connectivity_service/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::time::Duration;

use tari_comms::{
peer_manager::{NodeId, Peer},
protocol::rpc::RpcClientLease,
Expand All @@ -44,6 +46,16 @@ pub trait WalletConnectivityInterface: Clone + Send + Sync + 'static {
/// BaseNodeWalletRpcClient RPC session.
async fn obtain_base_node_wallet_rpc_client(&mut self) -> Option<RpcClientLease<BaseNodeWalletRpcClient>>;

async fn obtain_base_node_wallet_rpc_client_timeout(
&mut self,
timeout: Duration,
) -> Option<RpcClientLease<BaseNodeWalletRpcClient>> {
tokio::time::timeout(timeout, self.obtain_base_node_wallet_rpc_client())
.await
.ok()
.flatten()
}

/// Obtain a BaseNodeSyncRpcClient.
///
/// This can be relied on to obtain a pooled BaseNodeSyncRpcClient rpc session from a currently selected base
Expand Down

0 comments on commit 480d55d

Please sign in to comment.