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

ViewState RPC supports prefix search #1144

Merged
merged 6 commits into from
Aug 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 9 additions & 8 deletions core/primitives/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use crate::crypto::signature::PublicKey;
use crate::hash::{hash, CryptoHash};
use crate::types::{AccountId, ShardId};

pub const ACCOUNT_DATA_SEPARATOR: &[u8; 1] = b",";

pub mod col {
pub const ACCOUNT: &[u8] = &[0];
pub const CALLBACK: &[u8] = &[1];
Expand All @@ -22,8 +24,6 @@ pub mod col {
pub const POSTPONED_RECEIPT: &[u8] = &[7];
}

pub const ACCOUNT_DATA_SEPARATOR: &[u8; 1] = b",";

fn key_for_column_account_id(column: &[u8], account_key: &AccountId) -> Vec<u8> {
let mut key = column.to_vec();
key.append(&mut account_key.clone().into_bytes());
Expand All @@ -34,6 +34,13 @@ pub fn key_for_account(account_key: &AccountId) -> Vec<u8> {
key_for_column_account_id(col::ACCOUNT, account_key)
}

pub fn key_for_data(account_id: &AccountId, data: &[u8]) -> Vec<u8> {
let mut bytes = key_for_account(account_id);
bytes.extend(ACCOUNT_DATA_SEPARATOR);
bytes.extend(data);
bytes
}

pub fn prefix_for_access_key(account_id: &AccountId) -> Vec<u8> {
let mut key = key_for_column_account_id(col::ACCESS_KEY, account_id);
key.extend_from_slice(col::ACCESS_KEY);
Expand All @@ -57,12 +64,6 @@ pub fn key_for_code(account_key: &AccountId) -> Vec<u8> {
key_for_column_account_id(col::CODE, account_key)
}

pub fn key_for_data(account_id: &AccountId, key: &[u8]) -> Vec<u8> {
let mut prefix = prefix_for_data(account_id);
prefix.extend_from_slice(key);
prefix
}

pub fn key_for_received_data(account_id: &AccountId, data_id: &CryptoHash) -> Vec<u8> {
let mut key = key_for_column_account_id(col::RECEIVED_DATA, account_id);
key.append(&mut ACCOUNT_DATA_SEPARATOR.to_vec());
Expand Down
3 changes: 2 additions & 1 deletion near/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,9 +446,10 @@ impl node_runtime::adapter::ViewRuntimeAdapter for NightshadeRuntime {
&self,
state_root: MerkleHash,
account_id: &AccountId,
prefix: &[u8],
) -> Result<ViewStateResult, Box<dyn std::error::Error>> {
let state_update = TrieUpdate::new(self.trie.clone(), state_root);
self.trie_viewer.view_state(&state_update, account_id)
self.trie_viewer.view_state(&state_update, account_id, prefix)
}
}

Expand Down
3 changes: 2 additions & 1 deletion runtime/runtime/src/adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub trait ViewRuntimeAdapter {
&self,
state_root: MerkleHash,
account_id: &AccountId,
prefix: &[u8],
) -> Result<ViewStateResult, Box<dyn std::error::Error>>;
}

Expand Down Expand Up @@ -76,7 +77,7 @@ pub fn query_client(
Err(err) => Ok(QueryResponse::Error(QueryError { error: err.to_string(), logs })),
}
}
"contract" => match adapter.view_state(state_root, &AccountId::from(path_parts[1])) {
"contract" => match adapter.view_state(state_root, &AccountId::from(path_parts[1]), data) {
Ok(result) => Ok(QueryResponse::ViewState(result)),
Err(err) => {
Ok(QueryResponse::Error(QueryError { error: err.to_string(), logs: vec![] }))
Expand Down
43 changes: 20 additions & 23 deletions runtime/runtime/src/state_viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,18 @@ impl TrieViewer {
&self,
state_update: &TrieUpdate,
account_id: &AccountId,
prefix: &[u8],
) -> Result<ViewStateResult, Box<dyn std::error::Error>> {
if !is_valid_account_id(account_id) {
return Err(format!("Account ID '{}' is not valid", account_id).into());
}
let mut values = HashMap::default();
let prefix = prefix_for_data(account_id);
state_update.for_keys_with_prefix(&prefix, |key| {
let mut query = prefix_for_data(account_id);
let acc_sep_len = query.len();
query.extend_from_slice(prefix);
state_update.for_keys_with_prefix(&query, |key| {
if let Some(value) = state_update.get(key) {
values.insert(key[prefix.len()..].to_vec(), value.to_vec());
values.insert(key[acc_sep_len..].to_vec(), value.to_vec());
}
});
Ok(ViewStateResult { values })
Expand Down Expand Up @@ -176,7 +179,7 @@ mod tests {
use kvdb::DBValue;
use tempdir::TempDir;

use near_primitives::types::AccountId;
use near_primitives::utils::key_for_data;
use testlib::runtime_utils::{
alice_account, encode_int, get_runtime_and_trie, get_test_trie_viewer,
};
Expand All @@ -188,8 +191,7 @@ mod tests {
let (viewer, root) = get_test_trie_viewer();

let mut logs = vec![];
let result =
viewer.call_function(root, 1, &alice_account(), "run_test", &vec![], &mut logs);
let result = viewer.call_function(root, 1, &alice_account(), "run_test", &[], &mut logs);

assert_eq!(result.unwrap(), encode_int(10));
}
Expand All @@ -199,14 +201,8 @@ mod tests {
let (viewer, root) = get_test_trie_viewer();

let mut logs = vec![];
let result = viewer.call_function(
root,
1,
&"bad!contract".to_string(),
"run_test",
&vec![],
&mut logs,
);
let result =
viewer.call_function(root, 1, &"bad!contract".to_string(), "run_test", &[], &mut logs);

assert!(result.is_err());
}
Expand All @@ -221,7 +217,7 @@ mod tests {
1,
&alice_account(),
"run_test_with_storage_change",
&vec![],
&[],
&mut logs,
);
// run_test tries to change storage, so it should fail
Expand All @@ -238,28 +234,29 @@ mod tests {
assert_eq!(view_call_result.unwrap(), 3u64.to_le_bytes().to_vec());
}

fn account_suffix(account_id: &AccountId, suffix: &[u8]) -> Vec<u8> {
let mut bytes = prefix_for_data(account_id);
bytes.append(&mut suffix.clone().to_vec());
bytes
}

#[test]
fn test_view_state() {
let (_, trie, root) = get_runtime_and_trie();
let mut state_update = TrieUpdate::new(trie.clone(), root);
state_update.set(account_suffix(&alice_account(), b"test123"), DBValue::from_slice(b"123"));
state_update.set(key_for_data(&alice_account(), b"test123"), DBValue::from_slice(b"123"));
let (db_changes, new_root) = state_update.finalize().unwrap().into(trie.clone()).unwrap();
db_changes.commit().unwrap();

let state_update = TrieUpdate::new(trie, new_root);
let ethash_provider =
EthashProvider::new(TempDir::new("runtime_user_test_ethash").unwrap().path());
let trie_viewer = TrieViewer::new(Arc::new(Mutex::new(ethash_provider)));
let result = trie_viewer.view_state(&state_update, &alice_account()).unwrap();
let result = trie_viewer.view_state(&state_update, &alice_account(), b"").unwrap();
assert_eq!(
result.values,
[(b"test123".to_vec(), b"123".to_vec())].iter().cloned().collect()
);
let result = trie_viewer.view_state(&state_update, &alice_account(), b"test321").unwrap();
assert_eq!(result.values, [].iter().cloned().collect());
ilblackdragon marked this conversation as resolved.
Show resolved Hide resolved
let result = trie_viewer.view_state(&state_update, &alice_account(), b"test123").unwrap();
assert_eq!(
result.values,
[(b"test123".to_vec(), b"123".to_vec())].iter().cloned().collect()
)
}
}
4 changes: 2 additions & 2 deletions test-utils/testlib/src/standard_test_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -868,15 +868,15 @@ pub fn test_stake_fail_not_enough_rent(node: impl Node) {
pub fn test_delete_account(node: impl Node) {
let node_user = node.user();
// There is some data attached to the account.
assert!(node_user.view_state(&bob_account()).unwrap().values.len() > 0);
assert!(node_user.view_state(&bob_account(), b"").unwrap().values.len() > 0);
let initial_amount = node_user.view_account(&node.account_id().unwrap()).unwrap().amount;
let bobs_amount = node_user.view_account(&bob_account()).unwrap().amount;
let transaction_result = node_user.delete_account(alice_account(), bob_account());
assert_eq!(transaction_result.status, FinalTransactionStatus::Completed);
assert_eq!(transaction_result.transactions.len(), 3);
assert!(node_user.view_account(&bob_account()).is_err());
// No data left.
assert_eq!(node_user.view_state(&bob_account()).unwrap().values.len(), 0);
assert_eq!(node_user.view_state(&bob_account(), b"").unwrap().values.len(), 0);
// Receive back reward the balance of the bob's account.
assert_eq!(
node_user.view_account(&node.account_id().unwrap()).unwrap().amount,
Expand Down
2 changes: 1 addition & 1 deletion test-utils/testlib/src/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub trait User {
Ok(self.view_account(account_id)?.amount)
}

fn view_state(&self, account_id: &AccountId) -> Result<ViewStateResult, String>;
fn view_state(&self, account_id: &AccountId, prefix: &[u8]) -> Result<ViewStateResult, String>;

fn add_transaction(&self, signed_transaction: SignedTransaction) -> Result<(), String>;

Expand Down
13 changes: 6 additions & 7 deletions test-utils/testlib/src/user/rpc_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,18 @@ impl RpcUser {
System::new("actix").block_on(self.client.write().unwrap().status()).ok()
}

pub fn query(&self, path: String, data: Vec<u8>) -> Result<QueryResponse, String> {
System::new("actix").block_on(self.client.write().unwrap().query(path, to_base(&data)))
pub fn query(&self, path: String, data: &[u8]) -> Result<QueryResponse, String> {
System::new("actix").block_on(self.client.write().unwrap().query(path, to_base(data)))
}
}

impl User for RpcUser {
fn view_account(&self, account_id: &AccountId) -> Result<AccountViewCallResult, String> {
self.query(format!("account/{}", account_id), vec![])?.try_into()
self.query(format!("account/{}", account_id), &[])?.try_into()
}

fn view_state(&self, account_id: &AccountId) -> Result<ViewStateResult, String> {
self.query(format!("contract/{}", account_id), vec![])?.try_into()
fn view_state(&self, account_id: &AccountId, prefix: &[u8]) -> Result<ViewStateResult, String> {
self.query(format!("contract/{}", account_id), prefix)?.try_into()
}

fn add_transaction(&self, transaction: SignedTransaction) -> Result<(), String> {
Expand Down Expand Up @@ -104,8 +104,7 @@ impl User for RpcUser {
account_id: &AccountId,
public_key: &PublicKey,
) -> Result<Option<AccessKey>, String> {
self.query(format!("access_key/{}/{}", account_id, public_key.to_base()), vec![])?
.try_into()
self.query(format!("access_key/{}/{}", account_id, public_key.to_base()), &[])?.try_into()
}

fn signer(&self) -> Arc<dyn EDSigner> {
Expand Down
6 changes: 4 additions & 2 deletions test-utils/testlib/src/user/runtime_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,11 @@ impl User for RuntimeUser {
self.trie_viewer.view_account(&state_update, account_id).map_err(|err| err.to_string())
}

fn view_state(&self, account_id: &AccountId) -> Result<ViewStateResult, String> {
fn view_state(&self, account_id: &AccountId, prefix: &[u8]) -> Result<ViewStateResult, String> {
let state_update = self.client.read().expect(POISONED_LOCK_ERR).get_state_update();
self.trie_viewer.view_state(&state_update, account_id).map_err(|err| err.to_string())
self.trie_viewer
.view_state(&state_update, account_id, prefix)
.map_err(|err| err.to_string())
}

fn add_transaction(&self, transaction: SignedTransaction) -> Result<(), String> {
Expand Down