Skip to content

Commit 5ca11e8

Browse files
committed
client: implement initial commands
1 parent cbefc66 commit 5ca11e8

16 files changed

Lines changed: 356 additions & 86 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ name = "rgbclient"
2121

2222
[dependencies]
2323
amplify.workspace = true
24-
strict_encoding.workspace = true
24+
bp-std = { workspace = true, optional = true }
2525
rgb-rpc = { version = "0.12.0-alpha.1", path = "../rpc" }
2626
io-reactor.workspace = true
2727
netservices.workspace = true
@@ -40,5 +40,5 @@ rgb-rpc = { version = "0.12.0-alpha.1", path = "../rpc" }
4040
[features]
4141
default = ["cli"]
4242
all = ["log", "cli"]
43-
cli = ["log", "dep:clap", "dep:shellexpand", "dep:serde_yaml"]
43+
cli = ["dep:bp-std", "log", "dep:clap", "dep:shellexpand", "dep:serde_yaml"]
4444
log = ["dep:log", "dep:loglevel", "io-reactor/log", "netservices/log"]

client/src/args.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
use rgbrpc::RemoteAddr;
2323

24-
/// Command-line tool for working with store daemon
24+
/// Command-line tool for working with the RGB daemon
2525
#[derive(Parser, Clone, PartialEq, Eq, Debug)]
2626
#[command(name = "bp-cli", bin_name = "bp-cli", author, version)]
2727
pub struct Args {
@@ -43,6 +43,15 @@ pub struct Args {
4343
/// Command-line commands:
4444
#[derive(Subcommand, Clone, PartialEq, Eq, Debug, Display)]
4545
pub enum Command {
46-
#[display("ping")]
47-
Ping,
46+
/// Get RGB node status information
47+
#[display("status")]
48+
Status,
49+
50+
/// List wallets known to the RGB node
51+
#[display("wallets")]
52+
Wallets,
53+
54+
/// List contracts known to the RGB node
55+
#[display("contracts")]
56+
Contracts,
4857
}

client/src/client.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ pub struct Delegate {
3636
cb: fn(RgbRpcResp),
3737
}
3838

39-
pub struct BpClient {
39+
pub struct RgbClient {
4040
inner: Client<RgbRpcReq>,
4141
}
4242

43-
impl BpClient {
43+
impl RgbClient {
4444
pub fn new(remote: RemoteAddr, cb: fn(RgbRpcResp)) -> io::Result<Self> {
4545
let delegate = Delegate { cb };
46-
let inner = Client::new(delegate, remote)?;
46+
let inner = Client::new::<_, Session, _>(delegate, remote)?;
4747
Ok(Self { inner })
4848
}
4949

@@ -52,6 +52,10 @@ impl BpClient {
5252
self.inner.send(RgbRpcReq::Ping(noise))
5353
}
5454

55+
pub fn status(&self) -> io::Result<()> { self.inner.send(RgbRpcReq::Status) }
56+
pub fn contracts(&self) -> io::Result<()> { self.inner.send(RgbRpcReq::Contracts) }
57+
pub fn wallets(&self) -> io::Result<()> { self.inner.send(RgbRpcReq::Wallets) }
58+
5559
pub fn join(self) -> Result<(), Box<dyn Any + Send>> { self.inner.join() }
5660
}
5761

@@ -61,8 +65,8 @@ impl ConnectionDelegate<RemoteAddr, Session> for Delegate {
6165
fn connect(&mut self, remote: &RemoteAddr) -> Session {
6266
TcpStream::connect(remote).unwrap_or_else(|err| {
6367
#[cfg(feature = "log")]
64-
log::error!("Unable to connect BP Node {remote} due to {err}");
65-
eprintln!("Unable to connect BP Node {remote}");
68+
log::error!("Unable to connect RGB Node {remote} due to {err}");
69+
eprintln!("Unable to connect RGB Node {remote}");
6670
exit(1);
6771
})
6872
}

client/src/command.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
use std::io;
2323

2424
use crate::Command;
25-
use crate::client::BpClient;
25+
use crate::client::RgbClient;
2626

2727
#[derive(Debug, Display, Error, From)]
2828
#[non_exhaustive]
@@ -33,11 +33,13 @@ pub enum ExecError {
3333
}
3434

3535
impl Command {
36-
pub fn exec(self, mut client: BpClient) -> Result<(), ExecError> {
36+
pub fn exec(self, client: RgbClient) -> Result<(), ExecError> {
3737
match self {
38-
Command::Ping => {
39-
client.ping()?;
38+
Command::Status => {
39+
client.status()?;
4040
}
41+
Command::Wallets => client.wallets()?,
42+
Command::Contracts => client.contracts()?,
4143
}
4244
client.join().expect("Client thread panicked");
4345
Ok(())

client/src/lib.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@
1919
// or implied. See the License for the specific language governing permissions and limitations under
2020
// the License.
2121

22-
#[macro_use]
23-
extern crate amplify;
24-
#[macro_use]
25-
extern crate strict_encoding;
26-
2722
pub use ::rgbrpc as rpc;
2823
mod client;
2924

30-
pub use client::{BpClient, Delegate};
25+
pub use client::{Delegate, RgbClient};

client/src/main.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,23 @@ mod args;
3232
mod client;
3333
mod command;
3434

35+
use std::process::exit;
36+
37+
use bpstd::Descriptor;
3538
use clap::Parser;
3639
use loglevel::LogLevel;
3740
use rgbrpc::RgbRpcResp;
3841

3942
pub use crate::args::{Args, Command};
40-
use crate::client::BpClient;
43+
use crate::client::RgbClient;
4144
use crate::command::ExecError;
4245

4346
fn main() -> Result<(), ExecError> {
4447
let args = Args::parse();
4548
LogLevel::from_verbosity_flag_count(args.verbose).apply();
4649
trace!("Command-line arguments: {:#?}", &args);
4750

48-
let client = BpClient::new(args.remote, cb)?;
51+
let client = RgbClient::new(args.remote, cb)?;
4952

5053
args.command.exec(client)
5154
}
@@ -55,13 +58,31 @@ fn cb(reply: RgbRpcResp) {
5558
RgbRpcResp::Failure(failure) => {
5659
println!("Failure: {failure}");
5760
}
61+
RgbRpcResp::Message(msg) => {
62+
println!("Message from RGB Node: {msg}");
63+
}
5864
RgbRpcResp::Pong(_noise) => {}
5965
RgbRpcResp::Status(status) => {
6066
println!("{}", serde_yaml::to_string(&status).unwrap());
6167
}
62-
RgbRpcResp::ContractState(contract_id, ..) => {
63-
println!("Contract status for {contract_id}:");
68+
RgbRpcResp::Contracts(contracts) => {
69+
for contract in contracts {
70+
println!("---");
71+
println!("{}", serde_yaml::to_string(&contract).expect("Unable to generate YAML"));
72+
}
73+
}
74+
RgbRpcResp::ContractState(contract_id, state) => {
75+
println!("Contract state for {contract_id}:");
76+
println!("{}", serde_yaml::to_string(&state).expect("Unable to generate YAML"));
77+
}
78+
RgbRpcResp::Wallets(wallets) => {
79+
println!("Wallets:");
80+
println!("Id\tName\tDescriptor class");
81+
for wallet in wallets {
82+
println!("{}\t{}\t{}", wallet.id, wallet.name, wallet.descriptor.class())
83+
}
6484
}
6585
_ => todo!(),
6686
}
87+
exit(0)
6788
}

rpc/src/request.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
// or implied. See the License for the specific language governing permissions and limitations under
2020
// the License.
2121

22-
use std::io::{Read, Write};
22+
use std::io::{ErrorKind, Read, Write};
2323

2424
use amplify::confinement::{SmallBlob, TinyBlob};
2525
use bpstd::{DescrId, Network};
@@ -30,7 +30,6 @@ use sonicapi::{CodexId, ContractId};
3030
use crate::CiboriumError;
3131

3232
#[derive(Clone, Debug, Display)]
33-
#[display(UPPERCASE)]
3433
#[derive(Serialize, Deserialize)]
3534
pub enum RgbRpcReq {
3635
#[display("HELLO({0})")]
@@ -39,8 +38,10 @@ pub enum RgbRpcReq {
3938
#[display("PING")]
4039
Ping(TinyBlob),
4140

41+
#[display("STATUS")]
4242
Status,
4343

44+
#[display("WALLETS")]
4445
Wallets,
4546

4647
#[display("WALLET({0})")]
@@ -52,8 +53,10 @@ pub enum RgbRpcReq {
5253
#[display("DELETE({0})")]
5354
Delete(DescrId),
5455

56+
#[display("ISSUERS")]
5557
Issuers,
5658

59+
#[display("CONTRACTS")]
5760
Contracts,
5861

5962
#[display("ISSUER({0})")]
@@ -83,11 +86,43 @@ impl Frame for RgbRpcReq {
8386
type Error = CiboriumError;
8487

8588
fn unmarshall(reader: impl Read) -> Result<Option<Self>, Self::Error> {
86-
ciborium::from_reader(reader).map_err(CiboriumError::from)
89+
match ciborium::from_reader(reader) {
90+
Ok(msg) => Ok(Some(msg)),
91+
Err(ciborium::de::Error::Io(e)) if e.kind() == ErrorKind::UnexpectedEof => Ok(None),
92+
Err(e) => Err(CiboriumError::from(e)),
93+
}
8794
}
8895

8996
fn marshall(&self, writer: impl Write) -> Result<(), Self::Error> {
9097
ciborium::into_writer(self, writer)?;
9198
Ok(())
9299
}
93100
}
101+
102+
#[cfg(test)]
103+
mod tests {
104+
use std::io::Cursor;
105+
106+
use super::*;
107+
108+
#[test]
109+
fn serialization() {
110+
let mut buf = Vec::new();
111+
RgbRpcReq::Wallets.marshall(&mut buf).unwrap();
112+
assert_eq!(buf, *b"\x67Wallets");
113+
let deser = RgbRpcReq::unmarshall(&mut buf.as_slice()).unwrap().unwrap();
114+
assert!(matches!(deser, RgbRpcReq::Wallets));
115+
}
116+
117+
#[test]
118+
fn stream_serialization() {
119+
let mut buf = Vec::new();
120+
RgbRpcReq::Wallets.marshall(&mut buf).unwrap();
121+
assert_eq!(buf, *b"\x67Wallets");
122+
let mut cursor = Cursor::new(&mut buf);
123+
let deser = RgbRpcReq::unmarshall(&mut cursor).unwrap().unwrap();
124+
assert!(matches!(deser, RgbRpcReq::Wallets));
125+
let nothing = RgbRpcReq::unmarshall(&mut cursor).unwrap();
126+
assert!(matches!(nothing, None));
127+
}
128+
}

rpc/src/response.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// the License.
2121

2222
use std::collections::BTreeMap;
23-
use std::io::{Read, Write};
23+
use std::io::{ErrorKind, Read, Write};
2424

2525
use amplify::confinement::{SmallBlob, TinyBlob};
2626
use bpstd::DescrId;
@@ -50,7 +50,7 @@ pub enum RgbRpcResp {
5050
#[display("STATUS")]
5151
Status(Status),
5252

53-
#[display("STATUS")]
53+
#[display("WALLETS(...)")]
5454
Wallets(Vec<WalletInfo>),
5555

5656
#[display("WALLET_STATE({0}, ...)")]
@@ -92,7 +92,11 @@ impl Frame for RgbRpcResp {
9292
type Error = CiboriumError;
9393

9494
fn unmarshall(reader: impl Read) -> Result<Option<Self>, Self::Error> {
95-
ciborium::from_reader(reader).map_err(CiboriumError::from)
95+
match ciborium::from_reader(reader) {
96+
Ok(msg) => Ok(Some(msg)),
97+
Err(ciborium::de::Error::Io(e)) if e.kind() == ErrorKind::UnexpectedEof => Ok(None),
98+
Err(e) => Err(CiboriumError::from(e)),
99+
}
96100
}
97101

98102
fn marshall(&self, writer: impl Write) -> Result<(), Self::Error> {
@@ -118,3 +122,33 @@ pub struct WalletState {
118122
pub aggregated: BTreeMap<ContractStateName, StrictVal>,
119123
pub confirmations: BTreeMap<Opid, WitnessStatus>,
120124
}
125+
126+
#[cfg(test)]
127+
mod tests {
128+
use std::io::Cursor;
129+
130+
use super::*;
131+
132+
#[test]
133+
fn serialization() {
134+
let mut buf = Vec::new();
135+
RgbRpcResp::Message(s!("Test")).marshall(&mut buf).unwrap();
136+
assert_eq!(buf, *b"\xA1\x67Message\x64Test");
137+
let deser = RgbRpcResp::unmarshall(&mut buf.as_slice())
138+
.unwrap()
139+
.unwrap();
140+
assert!(matches!(deser, RgbRpcResp::Message(m) if m == "Test"));
141+
}
142+
143+
#[test]
144+
fn stream_serialization() {
145+
let mut buf = Vec::new();
146+
RgbRpcResp::Message(s!("Test")).marshall(&mut buf).unwrap();
147+
assert_eq!(buf, *b"\xA1\x67Message\x64Test");
148+
let mut cursor = Cursor::new(&mut buf);
149+
let deser = RgbRpcResp::unmarshall(&mut cursor).unwrap().unwrap();
150+
assert!(matches!(deser, RgbRpcResp::Message(m) if m == "Test"));
151+
let nothing = RgbRpcResp::unmarshall(&mut cursor).unwrap();
152+
assert!(matches!(nothing, None));
153+
}
154+
}

0 commit comments

Comments
 (0)