Skip to content

Commit

Permalink
move the current RPC patterns under "shell" sub-router
Browse files Browse the repository at this point in the history
  • Loading branch information
tzemanovic committed Oct 11, 2022
1 parent f460e74 commit da6cc62
Show file tree
Hide file tree
Showing 4 changed files with 348 additions and 312 deletions.
15 changes: 9 additions & 6 deletions apps/src/lib/client/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use crate::client::tendermint_rpc_types::TxResponse;
/// Query the epoch of the last committed block
pub async fn query_epoch(args: args::Query) -> Epoch {
let client = HttpClient::new(args.ledger_address).unwrap();
let epoch = unwrap_client_response(RPC.epoch(&client).await);
let epoch = unwrap_client_response(RPC.shell().epoch(&client).await);
println!("Last committed epoch: {}", epoch);
epoch
}
Expand All @@ -53,7 +53,7 @@ pub async fn query_epoch(args: args::Query) -> Epoch {
pub async fn query_raw_bytes(_ctx: Context, args: args::QueryRawBytes) {
let client = HttpClient::new(args.query.ledger_address).unwrap();
let bytes = unwrap_client_response(
RPC.storage_value(&client, &args.storage_key).await,
RPC.shell().storage_value(&client, &args.storage_key).await,
);
match bytes {
Some(bytes) => println!("Found data: 0x{}", hex::encode(&bytes)),
Expand Down Expand Up @@ -1026,7 +1026,8 @@ pub async fn dry_run_tx(ledger_address: &TendermintAddress, tx_bytes: Vec<u8>) {
let client = HttpClient::new(ledger_address.clone()).unwrap();
let (data, height, prove) = (Some(tx_bytes), None, false);
let result = unwrap_client_response(
RPC.dry_run_tx_with_options(&client, data, height, prove)
RPC.shell()
.dry_run_tx_with_options(&client, data, height, prove)
.await,
)
.data;
Expand Down Expand Up @@ -1242,7 +1243,8 @@ pub async fn query_storage_value<T>(
where
T: BorshDeserialize,
{
let bytes = unwrap_client_response(RPC.storage_value(client, key).await);
let bytes =
unwrap_client_response(RPC.shell().storage_value(client, key).await);
bytes.map(|bytes| {
T::try_from_slice(&bytes[..]).unwrap_or_else(|err| {
eprintln!("Error decoding the value: {}", err);
Expand All @@ -1261,7 +1263,8 @@ pub async fn query_storage_prefix<T>(
where
T: BorshDeserialize,
{
let values = unwrap_client_response(RPC.storage_prefix(client, key).await);
let values =
unwrap_client_response(RPC.shell().storage_prefix(client, key).await);
let decode =
|PrefixValue { key, value }: PrefixValue| match T::try_from_slice(
&value[..],
Expand All @@ -1287,7 +1290,7 @@ pub async fn query_has_storage_key(
client: &HttpClient,
key: &storage::Key,
) -> bool {
unwrap_client_response(RPC.storage_has_key(client, key).await)
unwrap_client_response(RPC.shell().storage_has_key(client, key).await)
}

/// Represents a query for an event pertaining to the specified transaction
Expand Down
309 changes: 5 additions & 304 deletions shared/src/ledger/queries/mod.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,25 @@
//! Ledger read-only queries can be handled and dispatched via the [`RPC`]
//! defined via `router!` macro.
use tendermint_proto::crypto::{ProofOp, ProofOps};
use shell::{Shell, SHELL};
#[cfg(any(test, feature = "async-client"))]
pub use types::Client;
pub use types::{
EncodedResponseQuery, RequestCtx, RequestQuery, ResponseQuery, Router,
};

use super::storage::{DBIter, StorageHasher, DB};
use super::storage_api::{self, ResultExt, StorageRead};
use crate::types::storage::{self, Epoch, PrefixValue};
use crate::types::transaction::TxResult;
#[cfg(all(feature = "wasm-runtime", feature = "ferveo-tpke"))]
use crate::types::transaction::{DecryptedTx, TxType};
use super::storage_api;

#[macro_use]
mod router;
mod shell;
mod types;

// Most commonly expected patterns should be declared first
router! {RPC,
// Epoch of the last committed block
( "epoch" ) -> Epoch = epoch,

// Raw storage access - read value
( "value" / [storage_key: storage::Key] )
-> Option<Vec<u8>> = storage_value,

// Dry run a transaction
( "dry_run_tx" ) -> TxResult = dry_run_tx,

// Raw storage access - prefix iterator
( "prefix" / [storage_key: storage::Key] )
-> Vec<PrefixValue> = storage_prefix,

// Raw storage access - is given storage key present?
( "has_key" / [storage_key: storage::Key] )
-> bool = storage_has_key,
// Shell provides storage read access, block metadata and can dry-run a tx
( "shell" ) = (sub SHELL),
}

/// Handle RPC query request in the ledger. On success, returns response with
Expand Down Expand Up @@ -86,188 +68,6 @@ pub fn require_no_proof(request: &RequestQuery) -> storage_api::Result<()> {
Ok(())
}

// Handlers:

#[cfg(all(feature = "wasm-runtime", feature = "ferveo-tpke"))]
fn dry_run_tx<D, H>(
mut ctx: RequestCtx<'_, D, H>,
request: &RequestQuery,
) -> storage_api::Result<ResponseQuery<TxResult>>
where
D: 'static + DB + for<'iter> DBIter<'iter> + Sync,
H: 'static + StorageHasher + Sync,
{
use super::gas::BlockGasMeter;
use super::storage::write_log::WriteLog;
use crate::proto::Tx;

let mut gas_meter = BlockGasMeter::default();
let mut write_log = WriteLog::default();
let tx = Tx::try_from(&request.data[..]).into_storage_result()?;
let tx = TxType::Decrypted(DecryptedTx::Decrypted(tx));
let data = super::protocol::apply_tx(
tx,
request.data.len(),
&mut gas_meter,
&mut write_log,
ctx.storage,
&mut ctx.vp_wasm_cache,
&mut ctx.tx_wasm_cache,
)
.into_storage_result()?;
Ok(ResponseQuery {
data,
..ResponseQuery::default()
})
}

#[cfg(not(all(feature = "wasm-runtime", feature = "ferveo-tpke")))]
fn dry_run_tx<D, H>(
_ctx: RequestCtx<'_, D, H>,
_request: &RequestQuery,
) -> storage_api::Result<ResponseQuery<TxResult>>
where
D: 'static + DB + for<'iter> DBIter<'iter> + Sync,
H: 'static + StorageHasher + Sync,
{
unimplemented!(
"dry_run_tx request handler requires \"wasm-runtime\" and \
\"ferveo-tpke\" features enabled."
)
}

fn epoch<D, H>(
ctx: RequestCtx<'_, D, H>,
request: &RequestQuery,
) -> storage_api::Result<ResponseQuery<Epoch>>
where
D: 'static + DB + for<'iter> DBIter<'iter> + Sync,
H: 'static + StorageHasher + Sync,
{
require_latest_height(&ctx, request)?;
require_no_proof(request)?;

let data = ctx.storage.last_epoch;
Ok(ResponseQuery {
data,
..Default::default()
})
}

fn storage_value<D, H>(
ctx: RequestCtx<'_, D, H>,
request: &RequestQuery,
storage_key: storage::Key,
) -> storage_api::Result<ResponseQuery<Option<Vec<u8>>>>
where
D: 'static + DB + for<'iter> DBIter<'iter> + Sync,
H: 'static + StorageHasher + Sync,
{
match ctx
.storage
.read_with_height(&storage_key, request.height)
.into_storage_result()?
{
(Some(data), _gas) => {
let proof = if request.prove {
let proof = ctx
.storage
.get_existence_proof(
&storage_key,
data.clone(),
request.height,
)
.into_storage_result()?;
Some(proof.into())
} else {
None
};
Ok(ResponseQuery {
data: Some(data),
proof_ops: proof,
..Default::default()
})
}
(None, _gas) => {
let proof = if request.prove {
let proof = ctx
.storage
.get_non_existence_proof(&storage_key, request.height)
.into_storage_result()?;
Some(proof.into())
} else {
None
};
Ok(ResponseQuery {
data: None,
proof_ops: proof,
info: format!("No value found for key: {}", storage_key),
})
}
}
}

fn storage_prefix<D, H>(
ctx: RequestCtx<'_, D, H>,
request: &RequestQuery,
storage_key: storage::Key,
) -> storage_api::Result<ResponseQuery<Vec<storage::PrefixValue>>>
where
D: 'static + DB + for<'iter> DBIter<'iter> + Sync,
H: 'static + StorageHasher + Sync,
{
require_latest_height(&ctx, request)?;

let (iter, _gas) = ctx.storage.iter_prefix(&storage_key);
let data: storage_api::Result<Vec<PrefixValue>> = iter
.map(|(key, value, _gas)| {
let key = storage::Key::parse(key).into_storage_result()?;
Ok(PrefixValue { key, value })
})
.collect();
let data = data?;
let proof_ops = if request.prove {
let mut ops = vec![];
for PrefixValue { key, value } in &data {
let proof = ctx
.storage
.get_existence_proof(key, value.clone(), request.height)
.into_storage_result()?;
let mut cur_ops: Vec<ProofOp> =
proof.ops.into_iter().map(|op| op.into()).collect();
ops.append(&mut cur_ops);
}
// ops is not empty in this case
Some(ProofOps { ops })
} else {
None
};
Ok(ResponseQuery {
data,
proof_ops,
..Default::default()
})
}

fn storage_has_key<D, H>(
ctx: RequestCtx<'_, D, H>,
request: &RequestQuery,
storage_key: storage::Key,
) -> storage_api::Result<ResponseQuery<bool>>
where
D: 'static + DB + for<'iter> DBIter<'iter> + Sync,
H: 'static + StorageHasher + Sync,
{
require_latest_height(&ctx, request)?;
require_no_proof(request)?;

let data = StorageRead::has_key(ctx.storage, &storage_key)?;
Ok(ResponseQuery {
data,
..Default::default()
})
}

#[cfg(any(test, feature = "tendermint-rpc"))]
/// Provides [`Client`] implementation for Tendermint RPC client
pub mod tm {
Expand Down Expand Up @@ -418,102 +218,3 @@ mod testing {
}
}
}

#[cfg(test)]
mod test {
use borsh::BorshDeserialize;

use super::testing::TestClient;
use super::*;
use crate::ledger::storage_api::StorageWrite;
use crate::proto::Tx;
use crate::types::{address, token};

const TX_NO_OP_WASM: &str = "../wasm_for_tests/tx_no_op.wasm";

#[test]
fn test_queries_router_paths() {
let path = RPC.epoch_path();
assert_eq!("/epoch", path);

let token_addr = address::testing::established_address_1();
let owner = address::testing::established_address_2();
let key = token::balance_key(&token_addr, &owner);
let path = RPC.storage_value_path(&key);
assert_eq!(format!("/value/{}", key), path);

let path = RPC.dry_run_tx_path();
assert_eq!("/dry_run_tx", path);

let path = RPC.storage_prefix_path(&key);
assert_eq!(format!("/prefix/{}", key), path);

let path = RPC.storage_has_key_path(&key);
assert_eq!(format!("/has_key/{}", key), path);
}

#[tokio::test]
async fn test_queries_router_with_client() -> storage_api::Result<()> {
// Initialize the `TestClient`
let mut client = TestClient::new(RPC);

// Request last committed epoch
let read_epoch = RPC.epoch(&client).await.unwrap();
let current_epoch = client.storage.last_epoch;
assert_eq!(current_epoch, read_epoch);

// Request dry run tx
let tx_no_op = std::fs::read(TX_NO_OP_WASM).expect("cannot load wasm");
let tx = Tx::new(tx_no_op, None);
let tx_bytes = tx.to_bytes();
let result = RPC
.dry_run_tx_with_options(&client, Some(tx_bytes), None, false)
.await
.unwrap();
assert!(result.data.is_accepted());

// Request storage value for a balance key ...
let token_addr = address::testing::established_address_1();
let owner = address::testing::established_address_2();
let balance_key = token::balance_key(&token_addr, &owner);
// ... there should be no value yet.
let read_balance =
RPC.storage_value(&client, &balance_key).await.unwrap();
assert!(read_balance.is_none());

// Request storage prefix iterator
let balance_prefix = token::balance_prefix(&token_addr);
let read_balances =
RPC.storage_prefix(&client, &balance_prefix).await.unwrap();
assert!(read_balances.is_empty());

// Request storage has key
let has_balance_key =
RPC.storage_has_key(&client, &balance_key).await.unwrap();
assert!(!has_balance_key);

// Then write some balance ...
let balance = token::Amount::from(1000);
StorageWrite::write(&mut client.storage, &balance_key, balance)?;
// ... there should be the same value now
let read_balance =
RPC.storage_value(&client, &balance_key).await.unwrap();
assert_eq!(
balance,
token::Amount::try_from_slice(&read_balance.unwrap()).unwrap()
);

// Request storage prefix iterator
let balance_prefix = token::balance_prefix(&token_addr);
let read_balances =
RPC.storage_prefix(&client, &balance_prefix).await.unwrap();
assert_eq!(read_balances.len(), 1);

// Request storage has key
let has_balance_key =
RPC.storage_has_key(&client, &balance_key).await.unwrap();
assert!(has_balance_key);

Ok(())
}
}
Loading

0 comments on commit da6cc62

Please sign in to comment.