Skip to content

Commit

Permalink
pcli: fix terminal input in pcli threshold
Browse files Browse the repository at this point in the history
Closes #4230
  • Loading branch information
hdevalence committed Apr 18, 2024
1 parent ea8cf1a commit 5c12fcf
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 6 deletions.
38 changes: 37 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ tendermint-config = { version = "0.34.0" }
tendermint-light-client-verifier = { version = "0.34.0" }
tendermint-proto = { version = "0.34.0" }
tendermint-rpc = { version = "0.34.0" }
termion = { version = "3" }
thiserror = { version = "1.0" }
tokio = { version = "1.3" }
tokio-stream = { version = "0.1.8" }
Expand Down
1 change: 1 addition & 0 deletions crates/bin/pcli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ tokio-util = {workspace = true}
toml = {workspace = true, features = ["preserve_order"]}
tonic = {workspace = true, features = ["tls-webpki-roots", "tls"]}
tower = {workspace = true, features = ["full"]}
termion = {workspace = true}
tracing = {workspace = true}
tracing-subscriber = {workspace = true, features = ["env-filter", "ansi"]}
url = {workspace = true, features = ["serde"]}
Expand Down
33 changes: 28 additions & 5 deletions crates/bin/pcli/src/terminal.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::io::{Read, Write};

use anyhow::Result;
use penumbra_custody::threshold::{SigningRequest, Terminal};
use tokio::io::{self, AsyncBufReadExt};
use tonic::async_trait;

/// For threshold custody, we need to implement this weird terminal abstraction.
Expand Down Expand Up @@ -40,11 +41,33 @@ impl Terminal for ActualTerminal {
}

async fn next_response(&self) -> Result<Option<String>> {
let stdin = io::stdin();
let mut stdin = io::BufReader::new(stdin);
// Use raw mode to allow reading more than 1KB/4KB of data at a time
// See https://unix.stackexchange.com/questions/204815/terminal-does-not-accept-pasted-or-typed-lines-of-more-than-1024-characters
use termion::raw::IntoRawMode;
tracing::debug!("about to enter raw mode for long pasted input");

// In raw mode, the input is not mirrored into the terminal, so we need
// to read char-by-char and echo it back.
let mut stdout = std::io::stdout().into_raw_mode()?;

let mut bytes = Vec::with_capacity(8192);
for b in std::io::stdin().bytes() {
let b = b?;
// In raw mode, the enter key might generate \r or \n, check either.
if b == b'\n' || b == b'\r' {
break;
}
bytes.push(b);
stdout.write(&[b]).unwrap();
// Flushing may not be the most efficient but performance isn't critical here.
stdout.flush()?;
}
// Drop _stdout to restore the terminal to normal mode
std::mem::drop(stdout);
tracing::debug!("exited raw mode and returned to cooked mode");

let mut line = String::new();
stdin.read_line(&mut line).await?;
let line = String::from_utf8(bytes)?;
tracing::debug!(?line, "read response line");

if line.is_empty() {
return Ok(None);
Expand Down

0 comments on commit 5c12fcf

Please sign in to comment.