Skip to content

Commit

Permalink
Ref #2067: querying contract state (#2075)
Browse files Browse the repository at this point in the history
* Fix #2067: querying contract state

* Change format to {key, value, proof} for state view. Proofs are empty for now
  • Loading branch information
ilblackdragon authored Feb 6, 2020
1 parent d8c9f6a commit 1c0ff7c
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 17 deletions.
15 changes: 13 additions & 2 deletions core/primitives/src/views.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::collections::HashMap;
use std::convert::{TryFrom, TryInto};
use std::fmt;

Expand Down Expand Up @@ -122,9 +121,21 @@ impl From<AccessKeyView> for AccessKey {
}
}

/// Set of serialized TrieNodes that are encoded in base64. Represent proof of inclusion of some TrieNode in the MerkleTrie.
pub type TrieProofPath = Vec<String>;

/// Item of the state, key and value are serialized in base64 and proof for inclusion of given state item.
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct StateItem {
pub key: String,
pub value: String,
pub proof: TrieProofPath,
}

#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct ViewStateResult {
pub values: HashMap<Vec<u8>, Vec<u8>>,
pub values: Vec<StateItem>,
pub proof: TrieProofPath,
}

#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
Expand Down
2 changes: 1 addition & 1 deletion core/store/src/trie/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::StorageError;
/// Provides a way to access Storage and record changes with future commit.
pub struct TrieUpdate {
pub trie: Arc<Trie>,
root: CryptoHash,
pub root: CryptoHash,
committed: BTreeMap<Vec<u8>, Option<Vec<u8>>>,
prospective: BTreeMap<Vec<u8>, Option<Vec<u8>>>,
}
Expand Down
56 changes: 42 additions & 14 deletions runtime/runtime/src/state_viewer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::collections::HashMap;
use std::str;
use std::time::Instant;

Expand All @@ -7,9 +6,10 @@ use borsh::BorshSerialize;
use near_crypto::{KeyType, PublicKey};
use near_primitives::account::{AccessKey, Account};
use near_primitives::hash::CryptoHash;
use near_primitives::serialize::to_base64;
use near_primitives::types::{AccountId, BlockHeight};
use near_primitives::utils::{is_valid_account_id, prefix_for_data};
use near_primitives::views::ViewStateResult;
use near_primitives::views::{StateItem, ViewStateResult};
use near_runtime_fees::RuntimeFeesConfig;
use near_store::{get_access_key, get_account, TrieUpdate};
use near_vm_logic::{ReturnData, VMConfig, VMContext};
Expand Down Expand Up @@ -60,17 +60,25 @@ impl TrieViewer {
if !is_valid_account_id(account_id) {
return Err(format!("Account ID '{}' is not valid", account_id).into());
}
let mut values = HashMap::default();
let mut values = vec![];
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| {
// TODO error
if let Ok(Some(value)) = state_update.get(key) {
values.insert(key[acc_sep_len..].to_vec(), value.to_vec());
let mut iter = state_update.trie.iter(&state_update.root)?;
iter.seek(&query)?;
for item in iter {
let (key, value) = item?;
if !key.starts_with(&query) {
break;
}
})?;
Ok(ViewStateResult { values })
values.push(StateItem {
key: to_base64(&key[acc_sep_len..]),
value: to_base64(&value),
proof: vec![],
});
}
// TODO(2076): Add proofs for the storage items.
Ok(ViewStateResult { values, proof: vec![] })
}

pub fn call_function(
Expand Down Expand Up @@ -165,6 +173,7 @@ impl TrieViewer {
#[cfg(test)]
mod tests {
use near_primitives::utils::key_for_data;
use near_primitives::views::StateItem;
use testlib::runtime_utils::{
alice_account, encode_int, get_runtime_and_trie, get_test_trie_viewer,
};
Expand Down Expand Up @@ -232,22 +241,41 @@ mod tests {
let (_, trie, root) = get_runtime_and_trie();
let mut state_update = TrieUpdate::new(trie.clone(), root);
state_update.set(key_for_data(&alice_account(), b"test123"), b"123".to_vec());
state_update.set(key_for_data(&alice_account(), b"test321"), b"321".to_vec());
state_update.set(key_for_data(&"alina".to_string(), b"qqq"), b"321".to_vec());
state_update.set(key_for_data(&"alex".to_string(), b"qqq"), b"321".to_vec());
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 trie_viewer = TrieViewer::new();
let result = trie_viewer.view_state(&state_update, &alice_account(), b"").unwrap();
assert_eq!(result.proof, Vec::<String>::new());
assert_eq!(
result.values,
[(b"test123".to_vec(), b"123".to_vec())].iter().cloned().collect()
[
StateItem {
key: "dGVzdDEyMw==".to_string(),
value: "MTIz".to_string(),
proof: vec![]
},
StateItem {
key: "dGVzdDMyMQ==".to_string(),
value: "MzIx".to_string(),
proof: vec![]
}
]
);
let result = trie_viewer.view_state(&state_update, &alice_account(), b"test321").unwrap();
assert_eq!(result.values, [].iter().cloned().collect());
let result = trie_viewer.view_state(&state_update, &alice_account(), b"xyz").unwrap();
assert_eq!(result.values, []);
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()
)
[StateItem {
key: "dGVzdDEyMw==".to_string(),
value: "MTIz".to_string(),
proof: vec![]
}]
);
}
}

0 comments on commit 1c0ff7c

Please sign in to comment.