Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a JSON-RPC client library #94

Merged
merged 41 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c591637
add solar_cli as workspace member and create whoami method with example
mycognosist Feb 12, 2024
b46b429
handle result
mycognosist Feb 12, 2024
1ff1361
add ping and example
mycognosist Feb 12, 2024
6edf08c
Merge branch 'main' into jsonrpc_client
mycognosist Feb 12, 2024
28cd2f8
add blocks and example
mycognosist Feb 12, 2024
c9f0eaa
Merge branch 'main' into jsonrpc_client
mycognosist Feb 12, 2024
cc76af8
add blockers
mycognosist Feb 13, 2024
5692920
correct the api docs for description
mycognosist Feb 13, 2024
e90cee0
add descriptions and example
mycognosist Feb 13, 2024
206c878
add self_descriptions
mycognosist Feb 13, 2024
1a41a32
correct the api docs for self_description
mycognosist Feb 13, 2024
903891c
return only the description for latest_description
mycognosist Feb 13, 2024
b05997b
return only the descriptions for self_descriptions
mycognosist Feb 13, 2024
a6fde21
add latest_description and example, fix self_descriptions return type
mycognosist Feb 13, 2024
f90adbf
add latest_self_description and example
mycognosist Feb 13, 2024
e984e1e
add feed and example
mycognosist Feb 13, 2024
4b19e30
add follows and example
mycognosist Feb 13, 2024
c0ad688
add followers
mycognosist Feb 13, 2024
713fb01
add is_following and an example
mycognosist Feb 13, 2024
99523df
add friends
mycognosist Feb 13, 2024
582b2fb
update the return types for descriptions and images
mycognosist Feb 13, 2024
9186ba4
add images and example
mycognosist Feb 13, 2024
5febf70
add self_images and example
mycognosist Feb 13, 2024
9074460
update the return types for self_images
mycognosist Feb 13, 2024
915d068
add latest_image and example, update self_images example
mycognosist Feb 13, 2024
37d5160
add latest_self_assigned_image
mycognosist Feb 13, 2024
200fb2f
add message and example, return json value from feed and message
mycognosist Feb 13, 2024
7a1c21b
update lockfile
mycognosist Feb 13, 2024
5b60e1d
add names and example
mycognosist Feb 21, 2024
9b54957
add self_names
mycognosist Feb 21, 2024
130aa50
add latest_name
mycognosist Feb 21, 2024
f689e1c
add latest_self_name
mycognosist Feb 21, 2024
b8ddb1a
fix description api usage in index tests
mycognosist Feb 21, 2024
64b03cb
return vec of tuple from get_peers and update tests
mycognosist Feb 21, 2024
689b6ed
add peers and example
mycognosist Feb 21, 2024
4d4cb70
add subscribers and example
mycognosist Feb 21, 2024
cd42725
add subscriptions and example
mycognosist Feb 21, 2024
5abd301
make publish endpoint take a message as a named object
mycognosist Feb 22, 2024
0c5cee6
add publish and example
mycognosist Feb 22, 2024
ec17451
remove JSON-RPC API docs from main repo README and update solar README
mycognosist Feb 22, 2024
84c5ebe
add minimal README
mycognosist Feb 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 251 additions & 17 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@
members = [
"solar",
"solar_cli",
"solar_client",
]
resolver = "2"
61 changes: 1 addition & 60 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ or embedded into another Rust application as a [library](https://github.com/myco

:warning: **Solar is alpha software; expect breaking changes** :construction:

[Background](#background) | [Features](#features) | [Installation](#installation) | [Usage](#usage) | [Examples](#examples) | [CLI Options](#options) | [Configuration](#configuration) | [JSON-RPC API](#json-rpc) | [License](#license)
[Background](#background) | [Features](#features) | [Installation](#installation) | [Usage](#usage) | [Examples](#examples) | [CLI Options](#options) | [Configuration](#configuration) | [License](#license)

## Background

Expand Down Expand Up @@ -126,65 +126,6 @@ Alternatively, peers can be added to the replication configuration via CLI optio

Log-level can be defined by setting the `RUST_LOG` environment variable.

## JSON-RPC API

While running, a solar node can be queried using JSON-RPC over HTTP.

| Method | Parameters | Response | Description |
| --- | --- | --- | --- |
| `blocks` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `blockers` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `descriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[<description>]` | Returns an array of descriptions |
| `self_descriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[<description>]` | Returns an array of descriptions |
| `latest_description` | `{ "pub_key": "<@...=.ed25519>" }` | `<description>` | Returns a single description |
| `latest_self_description` | `{ "pub_key": "<@...=.ed25519>" }` | `<description>` | Returns a single description |
| `feed` | `{ "pub_key": "<@...=.ed25519>" }` | `[{ "key": "<%...=.sha256>", "value": <value>, "timestamp": <timestamp>, "rts": null }]` | Returns an array of message KVTs (key, value, timestamp) from the local database |
| `follows` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `followers` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `is_following` | `{ "peer_a": "<@...=.ed25519>", "peer_b": "<@...=.ed25519>" }` | `<bool>` | Returns a boolean |
| `friends` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `images` | `{ "pub_key": "<@...=.ed25519>" }` | `[<&...=.sha256>]` | Returns an array of image references |
| `self_images` | `{ "pub_key": "<@...=.ed25519>" }` | `[<&...=.sha256>]` | Returns an array of image references |
| `latest_image` | `{ "pub_key": "<@...=.ed25519>" }` | `<&...=.sha256>` | Returns a single image reference |
| `latest_self_image` | `{ "pub_key": "<@...=.ed25519>" }` | `<&...=.sha256>` | Returns a single image reference |
| `message` | `{ "msg_ref": "<%...=.sha256>" }` | `{ "key": "<%...=.sha256>", "value": <value>, "timestamp": <timestamp>, "rts": null }` | Returns a single message KVT (key, value, timestamp) from the local database |
| `names` | `{ "pub_key": "<@...=.ed25519>" }` | `[<name>]` | Returns an array of names |
| `self_names` | `{ "pub_key": "<@...=.ed25519>" }` | `[<name>]` | Returns an array of names |
| `latest_name` | `{ "pub_key": "<@...=.ed25519>" }` | `<name>` | Returns a single name |
| `latest_self_name` | `{ "pub_key": "<@...=.ed25519>" }` | `<name>` | Returns a single name |
| `peers` | | `[{ "pub_key": "<@...=.ed25519>", "seq_num": <int> }` | Returns an array of public key and latest sequence number for each peer in the local database |
| `ping` | | `pong!` | Responds if the JSON-RPC server is running |
| `publish` | `<content>` | `{ "msg_ref": "<%...=.sha256>", "seq_num": <int> }` | Publishes a message and returns the reference (message hash) and sequence number |
| `subscribers` | `{ "channel": "<channel_name>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `subscriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[<channel>]` | Returns an array of channel names |
| `whoami` | | `<@...=.ed25519>` | Returns the public key of the local node |

### Examples

`curl` can be used to invoke the available methods from the commandline.

Request:

`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "ping", "id":1 }' 127.0.0.1:3030`

Response:

`{"jsonrpc":"2.0","result":"pong!","id":1}`

Request:

`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "publish", "params": {"type": "about", "about": "@o8lWpyLeSqV/BJV9pbxFhKpwm6Lw5k+sqexYK+zT9Tc=.ed25519", "name": "solar_glyph", "description": "glyph's experimental solar (rust) node"}, "id":1 }' 127.0.0.1:3030`

Response:

`{"jsonrpc":"2.0","result":{"msg_ref":"%ZwYwLxMHgU8eC43HOziJvYURjZzAzwFk3v5RYS/NbQY=.sha256","seq": 3,"id":1}`

_Note: You might find it easier to save your JSON to file and pass that to `curl` instead._

```
curl -X POST -H "Content-Type: application/json" --data @publish.json 127.0.0.1:3030
```

## License

AGPL-3.0
49 changes: 25 additions & 24 deletions solar/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,33 +67,34 @@ While running, a solar node can be queried using JSON-RPC over HTTP.

| Method | Parameters | Response | Description |
| --- | --- | --- | --- |
| `blocks` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `blockers` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `descriptions` | `"<@...=.ed25519>"` | `[<description>]` | Returns an array of descriptions |
| `self_descriptions` | `"<@...=.ed25519>"` | `[<description>]` | Returns an array of descriptions |
| `latest_description` | `"<@...=.ed25519>"` | `<description>` | Returns a single description |
| `latest_self_description` | `"<@...=.ed25519>"` | `<description>` | Returns a single description |
| `feed` | `"<@...=.ed25519>"` | `[{ "key": "<%...=.sha256>", "value": <value>, "timestamp": <timestamp>, "rts": null }]` | Returns an array of message KVTs (key, value, timestamp) from the local database |
| `follows` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `followers` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `is_following` | `{ "peer_a": "<@...=.ed25519>", "peer_b": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns a boolean |
| `friends` | `"<@...=.ed25519>"` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `images` | `"<@...=.ed25519>"` | `[<&...=.sha256>]` | Returns an array of image references |
| `self_images` | `"<@...=.ed25519>"` | `[<&...=.sha256>]` | Returns an array of image references |
| `latest_image` | `"<@...=.ed25519>"` | `<&...=.sha256>` | Returns a single image reference |
| `latest_self_image` | `"<@...=.ed25519>"` | `<&...=.sha256>` | Returns a single image reference |
| `message` | `"<%...=.sha256>"` | `{ "key": "<%...=.sha256>", "value": <value>, "timestamp": <timestamp>, "rts": null }` | Returns a single message KVT (key, value, timestamp) from the local database |
| `names` | `"<@...=.ed25519>"` | `[<name>]` | Returns an array of names |
| `self_names` | `"<@...=.ed25519>"` | `[<name>]` | Returns an array of names |
| `latest_name` | `"<@...=.ed25519>"` | `<name>` | Returns a single name |
| `latest_self_name` | `"<@...=.ed25519>"` | `<name>` | Returns a single name |
| `blocks` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `blockers` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `descriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[(<@...=.ed25519>, <description>)]` | Returns an array of tuples, each containing a public key and a description |
| `self_descriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[<description>]` | Returns an array of descriptions |
| `latest_description` | `{ "pub_key": "<@...=.ed25519>" }` | `<description>` | Returns a single description |
| `latest_self_description` | `{ "pub_key": "<@...=.ed25519>" }` | `<description>` | Returns a single description |
| `feed` | `{ "pub_key": "<@...=.ed25519>" }` | `[{ "key": "<%...=.sha256>", "value": <value>, "timestamp": <timestamp>, "rts": null }]` | Returns an array of message KVTs (key, value, timestamp) from the local database |
| `follows` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `followers` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `is_following` | `{ "peer_a": "<@...=.ed25519>", "peer_b": "<@...=.ed25519>" }` | `<bool>` | Returns a boolean |
| `friends` | `{ "pub_key": "<@...=.ed25519>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `images` | `{ "pub_key": "<@...=.ed25519>" }` | `[(<@...=.ed25519>, <&...=.sha256>)]` | Returns an array of tuples, each containing a public key and an image reference |
| `self_images` | `{ "pub_key": "<@...=.ed25519>" }` | `[<&...=.sha256>]` | Returns an array of image references |
| `latest_image` | `{ "pub_key": "<@...=.ed25519>" }` | `<&...=.sha256>` | Returns a single image reference |
| `latest_self_image` | `{ "pub_key": "<@...=.ed25519>" }` | `<&...=.sha256>` | Returns a single image reference |
| `message` | `{ "msg_ref": "<%...=.sha256>" }` | `{ "key": "<%...=.sha256>", "value": <value>, "timestamp": <timestamp>, "rts": null }` | Returns a single message KVT (key, value, timestamp) from the local database |
| `names` | `{ "pub_key": "<@...=.ed25519>" }` | `[<name>]` | Returns an array of names |
| `self_names` | `{ "pub_key": "<@...=.ed25519>" }` | `[<name>]` | Returns an array of names |
| `latest_name` | `{ "pub_key": "<@...=.ed25519>" }` | `<name>` | Returns a single name |
| `latest_self_name` | `{ "pub_key": "<@...=.ed25519>" }` | `<name>` | Returns a single name |
| `peers` | | `[{ "pub_key": "<@...=.ed25519>", "seq_num": <int> }` | Returns an array of public key and latest sequence number for each peer in the local database |
| `ping` | | `pong!` | Responds if the JSON-RPC server is running |
| `publish` | `<content>` | `{ "msg_ref": "<%...=.sha256>", "seq_num": <int> }` | Publishes a message and returns the reference (message hash) and sequence number |
| `subscribers` | `"<channel>"` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `subscriptions` | `"<@...=.ed25519>"` | `[<channel>]` | Returns an array of channel names |
| `publish` | `{ "msg": {<content>} }` | `("<%...=.sha256>", <int>)` | Returns a tuple of the reference (message hash) and sequence number |
| `subscribers` | `{ "channel": "<channel_name>" }` | `[<@...=.ed25519>]` | Returns an array of public keys |
| `subscriptions` | `{ "pub_key": "<@...=.ed25519>" }` | `[<channel>]` | Returns an array of channel names |
| `whoami` | | `<@...=.ed25519>` | Returns the public key of the local node |


### Examples

`curl` can be used to invoke the available methods from the commandline.
Expand All @@ -108,7 +109,7 @@ Response:

Request:

`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "publish", "params": {"type": "about", "about": "@o8lWpyLeSqV/BJV9pbxFhKpwm6Lw5k+sqexYK+zT9Tc=.ed25519", "name": "solar_glyph", "description": "glyph's experimental solar (rust) node"}, "id":1 }' 127.0.0.1:3030`
`curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0", "method": "publish", "params": {"msg": {"type": "about", "about": "@o8lWpyLeSqV/BJV9pbxFhKpwm6Lw5k+sqexYK+zT9Tc=.ed25519", "name": "solar_glyph", "description": "glyph's experimental solar (rust) node"} }, "id":1 }' 127.0.0.1:3030`

Response:

Expand Down
18 changes: 13 additions & 5 deletions solar/src/actors/jsonrpc/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ struct IsFollowing {
peer_b: String,
}

/// The contents of a raw message (of any supported type).
#[derive(Debug, Deserialize)]
struct Msg {
msg: TypedMessage,
}

/// Message reference containing the key (sha256 hash) of a message.
/// Used to parse the key from the parameters supplied to the `message`
/// endpoint.
Expand Down Expand Up @@ -474,8 +480,9 @@ pub async fn actor(server_id: OwnedIdentity, server_addr: SocketAddr) -> Result<
// Returns the key (hash) and sequence number of the published message.
rpc_module.register_method("publish", move |params: Params, _| {
task::block_on(async {
// Parse the parameter containing the post content.
let post_content: TypedMessage = params.parse()?;
// Parse the parameter containing the message content.
let msg_object: Msg = params.parse()?;
let msg_content: TypedMessage = msg_object.msg;

// Open the primary KV database for writing.
let db = KV_STORE.write().await;
Expand All @@ -484,8 +491,8 @@ pub async fn actor(server_id: OwnedIdentity, server_addr: SocketAddr) -> Result<
// Return `None` if no messages have yet been published on the feed.
let last_msg = db.get_latest_msg_val(&server_id.id)?;

// Instantiate and cryptographically-sign a new message using `post`.
let msg = Message::sign(last_msg.as_ref(), &server_id, json!(post_content))
// Instantiate and cryptographically-sign a new message.
let msg = Message::sign(last_msg.as_ref(), &server_id, json!(msg_content))
.map_err(Error::Validation)?;

// Append the signed message to the feed.
Expand All @@ -497,7 +504,8 @@ pub async fn actor(server_id: OwnedIdentity, server_addr: SocketAddr) -> Result<
seq
);

let response = json![{ "msg_ref": msg.id().to_string(), "seq_num": seq }];
// Return a tuple of message reference and sequence number.
let response = json!((msg.id().to_string(), seq));

Ok::<Value, JsonRpcError>(response)
})
Expand Down
42 changes: 24 additions & 18 deletions solar/src/storage/indexes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,27 +299,31 @@ impl Indexes {
}

/// Return all indexed self-assigned descriptions for the given public key.
pub fn get_self_assigned_descriptions(&self, ssb_id: &str) -> Result<Vec<(String, String)>> {
let mut descriptions = self.get_descriptions(ssb_id)?;
descriptions.retain(|(author, _description)| author == ssb_id);
pub fn get_self_assigned_descriptions(&self, ssb_id: &str) -> Result<Vec<String>> {
let descriptions = self
.get_descriptions(ssb_id)?
.into_iter()
.filter(|(author, _description)| author == ssb_id)
.map(|(_ssb_id, description)| description)
.collect();

Ok(descriptions)
}

/// Return the most recently indexed description for the given public key.
pub fn get_latest_description(&self, ssb_id: &str) -> Result<Option<(String, String)>> {
let descriptions = self.get_descriptions(ssb_id)?;
let description = descriptions.last().cloned();
pub fn get_latest_description(&self, ssb_id: &str) -> Result<Option<String>> {
let description = self
.get_descriptions(ssb_id)?
.last()
.map(|(_ssb_id, description)| description)
.cloned();

Ok(description)
}

/// Return the most recently indexed self-assigned description for the given
/// public key.
pub fn get_latest_self_assigned_description(
&self,
ssb_id: &str,
) -> Result<Option<(String, String)>> {
pub fn get_latest_self_assigned_description(&self, ssb_id: &str) -> Result<Option<String>> {
let self_descriptions = self.get_self_assigned_descriptions(ssb_id)?;
let description = self_descriptions.last().cloned();

Expand Down Expand Up @@ -460,9 +464,13 @@ impl Indexes {

/// Return all indexed self-assigned image references for the given public
/// key.
pub fn get_self_assigned_images(&self, ssb_id: &str) -> Result<Vec<(String, String)>> {
let mut images = self.get_images(ssb_id)?;
images.retain(|(author, _image)| author == ssb_id);
pub fn get_self_assigned_images(&self, ssb_id: &str) -> Result<Vec<String>> {
let images = self
.get_images(ssb_id)?
.into_iter()
.filter(|(author, _image)| author == ssb_id)
.map(|(_ssb_id, image)| image)
.collect();

Ok(images)
}
Expand All @@ -478,7 +486,7 @@ impl Indexes {

/// Return the most recently indexed self-assigned image reference for the
/// given public key.
pub fn get_latest_self_assigned_image(&self, ssb_id: &str) -> Result<Option<(String, String)>> {
pub fn get_latest_self_assigned_image(&self, ssb_id: &str) -> Result<Option<String>> {
let images = self.get_self_assigned_images(ssb_id)?;
let image = images.last().cloned();

Expand Down Expand Up @@ -595,7 +603,7 @@ mod test {

indexes.index_msg(&keypair.id, first_msg)?;

if let Some((_author, description)) = indexes.get_latest_description(&keypair.id)? {
if let Some(description) = indexes.get_latest_description(&keypair.id)? {
assert_eq!(description, first_description);
}

Expand Down Expand Up @@ -632,9 +640,7 @@ mod test {
assert_eq!(lastest_name, second_name);
}

if let Some((_author, latest_description)) =
indexes.get_latest_description(&keypair.id)?
{
if let Some(latest_description) = indexes.get_latest_description(&keypair.id)? {
assert_eq!(latest_description, second_description);
}
}
Expand Down
9 changes: 4 additions & 5 deletions solar/src/storage/kv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ impl KvStorage {

/// Return the public key and latest sequence number for all peers in the
/// database.
pub async fn get_peers(&self) -> Result<Vec<PubKeyAndSeqNum>> {
pub async fn get_peers(&self) -> Result<Vec<(String, u64)>> {
let db = self.db.as_ref().ok_or(Error::OptionIsNone)?;
let mut peers = Vec::new();

Expand All @@ -235,8 +235,7 @@ impl KvStorage {
// Get the latest sequence number for the peer.
// Fallback to a value of 0 if a `None` value is returned.
let seq_num = self.get_latest_seq(&pub_key)?.unwrap_or(0);
let peer_latest_sequence = PubKeyAndSeqNum { pub_key, seq_num };
peers.push(peer_latest_sequence)
peers.push((pub_key, seq_num))
}

Ok(peers)
Expand Down Expand Up @@ -479,8 +478,8 @@ mod test {
assert_eq!(peers.len(), 1);
// Ensure the public key of the peer matches expectations and that
// the sequence number is correct.
assert_eq!(peers[0].pub_key, keypair.id);
assert_eq!(peers[0].seq_num, 1);
assert_eq!(peers[0].0, keypair.id);
assert_eq!(peers[0].1, 1);

// Create, sign and append a second post-type message.
let msg_content_2 = TypedMessage::Post {
Expand Down
13 changes: 13 additions & 0 deletions solar_client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "solar_client"
version = "0.1.0"
edition = "2021"

[dependencies]
anyhow = "1.0.79"
jsonrpc_client = { version = "0.7.1", features = ["macros", "reqwest"] }
reqwest = { version = "0.11", default-features = false, features = [ "json" ] }
serde_json = { version = "1", features = ["preserve_order", "arbitrary_precision"] }

[dev-dependencies]
tokio = { version = "1.36.0", features = [ "macros", "rt-multi-thread" ] }
9 changes: 9 additions & 0 deletions solar_client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# 🌞 Solar JSON-RPC Client

An HTTP JSON-RPC client for the Solar node.

See `src/lib.rs` and `examples/` for API details and usage examples.

## License

AGPL-3.0
21 changes: 21 additions & 0 deletions solar_client/examples/blocks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use anyhow::Result;
use solar_client::{Client, SolarClient};

const SERVER_ADDR: &str = "http://127.0.0.1:3030";
const PUB_KEY: &str = "@HEqy940T6uB+T+d9Jaa58aNfRzLx9eRWqkZljBmnkmk=.ed25519";

#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(SERVER_ADDR.to_owned())?;

let blocks = client.blocks(PUB_KEY).await?;
println!("{:#?}", blocks);
// [
// "@dW5ch5miTnxLJDVDtB4ZCvrVxh+S8kGCQIBbd5paLhw=.ed25519",
// "@QIlKZ8DMw9XpjpRZ96RBLpfkLnOUZSqamC6WMddGh3I=.ed25519",
// ...
// "@+rMXLy1md42gvbBq+6l6rp95/drh6QyACO1ZZMMnWI0=.ed25519",
// ]

Ok(())
}
Loading
Loading