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

Improve store and state sync #68

Merged
merged 12 commits into from
Dec 29, 2023
23 changes: 14 additions & 9 deletions src/cli/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ use comfy_table::{presets, Attribute, Cell, ContentArrangement, Table};
use crypto::{
dsa::rpo_falcon512::KeyPair,
utils::{bytes_to_hex_string, Serializable},
Word,
};
use miden_client::client::accounts;
use objects::utils::collections::BTreeMap;

use objects::{accounts::AccountId, assets::TokenSymbol};

Expand Down Expand Up @@ -188,14 +190,16 @@ pub fn show_account(
.get_account_auth(account_id)
.map_err(|err| err.to_string())?;

// TODO: Decide how we want to output and import auth info

const KEY_PAIR_SIZE: usize = std::mem::size_of::<KeyPair>();
let auth_info: [u8; KEY_PAIR_SIZE] = auth_info
.to_bytes()
.try_into()
.expect("Array size is const and should always exactly fit KeyPair");
println!("Key pair:\n0x{}", bytes_to_hex_string(auth_info));
match auth_info {
miden_client::store::accounts::AuthInfo::RpoFalcon512(key_pair) => {
const KEY_PAIR_SIZE: usize = std::mem::size_of::<KeyPair>();
let auth_info: [u8; KEY_PAIR_SIZE] = key_pair
.to_bytes()
.try_into()
.expect("Array size is const and should always exactly fit KeyPair");
println!("Key pair:\n0x{}", bytes_to_hex_string(auth_info));
}
};
}

if show_vault {
Expand All @@ -214,9 +218,10 @@ pub fn show_account(
.get_account_storage(account.storage_root())
.map_err(|err| err.to_string())?;

let slots_leaves: BTreeMap<u64, &Word> = account_storage.slots().leaves().collect();
println!(
"Storage: {}\n",
serde_json::to_string(&account_storage)
serde_json::to_string(&slots_leaves)
.map_err(|_| "Error serializing account storage")?
);
}
Expand Down
89 changes: 69 additions & 20 deletions src/cli/input_notes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use std::path::PathBuf;
use super::{Client, Parser};
use comfy_table::{presets, Attribute, Cell, ContentArrangement, Table};
use crypto::utils::{Deserializable, Serializable};
use miden_client::store::notes::InputNoteFilter;
use objects::notes::RecordedNote;
use miden_client::store::notes::{InputNoteFilter, InputNoteRecord};

use objects::Digest;

#[derive(Debug, Parser, Clone)]
Expand Down Expand Up @@ -58,7 +58,7 @@ pub enum InputNotes {
}

impl InputNotes {
pub fn execute(&self, client: Client) -> Result<(), String> {
pub fn execute(&self, mut client: Client) -> Result<(), String> {
match self {
InputNotes::List => {
list_input_notes(client)?;
Expand All @@ -75,7 +75,7 @@ impl InputNotes {
export_note(&client, hash, filename.clone())?;
}
InputNotes::Import { filename } => {
import_note(client, filename.clone())?;
import_note(&mut client, filename.clone())?;
}
}
Ok(())
Expand Down Expand Up @@ -107,26 +107,30 @@ pub fn export_note(client: &Client, hash: &str, filename: Option<PathBuf>) -> Re

let mut file = File::create(file_path).map_err(|err| err.to_string())?;

let _ = file.write_all(&note.to_bytes());
file.write_all(&note.to_bytes())
.map_err(|err| err.to_string())?;

Ok(file)
}

// IMPORT INPUT NOTE
// ================================================================================================
pub fn import_note(mut client: Client, filename: PathBuf) -> Result<Digest, String> {
pub fn import_note(client: &mut Client, filename: PathBuf) -> Result<Digest, String> {
let mut contents = vec![];
let mut _file = File::open(filename)
.and_then(|mut f| f.read_to_end(&mut contents))
.map_err(|err| err.to_string());

// TODO: When importing a RecordedNote we want to make sure that the note actually exists in the chain (RPC call)
// and start monitoring its nullifiers (ie, update the list of relevant tags in the state sync table)
let note = RecordedNote::read_from_bytes(&contents).map_err(|err| err.to_string())?;
let note = InputNoteRecord::read_from_bytes(&contents).map_err(|err| err.to_string())?;

let note_hash = note.note().hash();
client
.import_input_note(note.clone())
.import_input_note(note)
.map_err(|err| err.to_string())?;
Ok(note.note().hash())

Ok(note_hash)
}

// SHOW INPUT NOTE
Expand Down Expand Up @@ -206,7 +210,7 @@ fn show_input_note(
// ================================================================================================
fn print_notes_summary<'a, I>(notes: I)
where
I: IntoIterator<Item = &'a RecordedNote>,
I: IntoIterator<Item = &'a InputNoteRecord>,
{
let mut table = Table::new();
table
Expand Down Expand Up @@ -236,16 +240,17 @@ where
#[cfg(test)]
mod tests {
igamigo marked this conversation as resolved.
Show resolved Hide resolved
use crate::cli::input_notes::{export_note, import_note};

use miden_client::{
client::Client,
config::{ClientConfig, Endpoint},
store::notes::InputNoteFilter,
store::notes::{InputNoteFilter, InputNoteRecord},
};
use std::env::temp_dir;
use uuid::Uuid;

#[tokio::test]
pub async fn import_export() {
pub async fn import_export_recorded_note() {
// generate test client
let mut path = temp_dir();
path.push(Uuid::new_v4().to_string());
Expand All @@ -259,12 +264,9 @@ mod tests {
// generate test data
miden_client::mock::insert_mock_data(&mut client);

let note = client
.get_input_notes(InputNoteFilter::All)
.unwrap()
.first()
.unwrap()
.clone();
let notes = client.get_input_notes(InputNoteFilter::All).unwrap();

let note = notes.first().unwrap();

let mut filename_path = temp_dir();
filename_path.push("test_import");
Expand All @@ -280,12 +282,59 @@ mod tests {

let mut path = temp_dir();
path.push(Uuid::new_v4().to_string());
let client = Client::new(ClientConfig::new(
let mut client = Client::new(ClientConfig::new(
path.into_os_string().into_string().unwrap(),
Endpoint::default(),
))
.await
.unwrap();

import_note(&mut client, filename_path).unwrap();
let imported_note = client.get_input_note(note.note().hash()).unwrap();

assert_eq!(note.note().hash(), imported_note.note().hash());

// Import/export pending note
// ------------------------------

// generate test client
let mut path = temp_dir();
path.push(Uuid::new_v4().to_string());
let mut client = Client::new(ClientConfig::new(
path.into_os_string().into_string().unwrap(),
Endpoint::default(),
))
.await
.unwrap();
import_note(client, filename_path).unwrap();

// generate test data
miden_client::mock::insert_mock_data(&mut client);

let pending_note = client.get_input_notes(InputNoteFilter::Pending).unwrap();
let note: &InputNoteRecord = pending_note.first().unwrap();
assert!(note.inclusion_proof().is_none());

let mut filename_path = temp_dir();
filename_path.push("test_import_pending");
export_note(
&client,
&note.note().hash().to_string(),
Some(filename_path.clone()),
)
.unwrap();

let mut path = temp_dir();
path.push(Uuid::new_v4().to_string());
let mut client = Client::new(ClientConfig::new(
path.into_os_string().into_string().unwrap(),
Endpoint::default(),
))
.await
.unwrap();

import_note(&mut client, filename_path).unwrap();
let imported_note = client.get_input_note(note.note().hash()).unwrap();

assert_eq!(note.note().hash(), imported_note.note().hash());
}
}
5 changes: 3 additions & 2 deletions src/cli/transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use comfy_table::Attribute;
use comfy_table::Cell;
use comfy_table::ContentArrangement;
use comfy_table::Table;

use miden_client::client::transactions::PaymentTransactionData;
use miden_client::client::transactions::TransactionStub;
use miden_client::client::transactions::TransactionTemplate;
Expand Down Expand Up @@ -69,12 +70,12 @@ impl Transaction {
sender_account_id,
target_account_id,
);
let (transaction_result, tx_script) = client
let transaction_execution_result = client
.new_transaction(TransactionTemplate::PayToId(payment_transaction))
.map_err(|err| err.to_string())?;

client
.send_transaction(transaction_result.into_witness(), Some(tx_script))
.send_transaction(transaction_execution_result)
.await
.map_err(|err| err.to_string())?;
}
Expand Down
12 changes: 3 additions & 9 deletions src/client/accounts.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use super::Client;

use std::collections::BTreeMap;

use crypto::{Felt, Word};
use crypto::Felt;
use miden_lib::{faucets, AuthScheme};
use objects::{
accounts::{Account, AccountId, AccountStub, AccountType},
accounts::{Account, AccountId, AccountStorage, AccountStub, AccountType},
assembly::ModuleAst,
assets::{Asset, TokenSymbol},
Digest,
Expand Down Expand Up @@ -188,10 +185,7 @@ impl Client {
}

/// Returns account storage data from a storage root.
pub fn get_account_storage(
&self,
storage_root: Digest,
) -> Result<BTreeMap<u64, Word>, ClientError> {
pub fn get_account_storage(&self, storage_root: Digest) -> Result<AccountStorage, ClientError> {
self.store
.get_account_storage(storage_root)
.map_err(|err| err.into())
Expand Down
28 changes: 16 additions & 12 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ use crate::{
store::{mock_executor_data_store::MockDataStore, Store},
};

#[cfg(not(any(test, feature = "testing")))]
use crate::errors::RpcApiError;

use miden_tx::TransactionExecutor;

#[cfg(any(test, feature = "testing"))]
#[cfg(feature = "testing")]
use crate::mock::MockRpcApi;

pub mod accounts;
Expand All @@ -37,11 +34,9 @@ pub const FILTER_ID_SHIFT: u8 = 48;
pub struct Client {
/// Local database containing information about the accounts managed by this client.
pub(crate) store: Store,
#[cfg(not(any(test, feature = "testing")))]
/// Api client for interacting with the Miden node.
rpc_api: miden_node_proto::rpc::api_client::ApiClient<tonic::transport::Channel>,
#[cfg(any(test, feature = "testing"))]
pub rpc_api: MockRpcApi,
#[cfg(any(test, feature = "testing"))]
pub(crate) tx_executor: TransactionExecutor<MockDataStore>,
}

Expand All @@ -54,17 +49,26 @@ impl Client {
/// # Errors
/// Returns an error if the client could not be instantiated.
pub async fn new(config: ClientConfig) -> Result<Self, ClientError> {
Ok(Self {
#[cfg(not(any(test, feature = "testing")))]
return Ok(Self {
store: Store::new((&config).into())?,
#[cfg(not(any(test, feature = "testing")))]
rpc_api: miden_node_proto::rpc::api_client::ApiClient::connect(
config.node_endpoint.to_string(),
)
.await
.map_err(|err| ClientError::RpcApiError(RpcApiError::ConnectionError(err)))?,
#[cfg(any(test, feature = "testing"))]
.map_err(|err| {
ClientError::RpcApiError(crate::errors::RpcApiError::ConnectionError(err))
})?,
tx_executor: TransactionExecutor::new(crate::store::data_store::SqliteDataStore::new(
Store::new((&config).into())?,
)),
});

#[cfg(any(test, feature = "testing"))]
return Ok(Self {
store: Store::new((&config).into())?,
rpc_api: Default::default(),
tx_executor: TransactionExecutor::new(MockDataStore::new()),
})
});
}
}
16 changes: 9 additions & 7 deletions src/client/notes.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use super::Client;

use crate::{errors::ClientError, store::notes::InputNoteFilter};

use objects::{notes::RecordedNote, Digest};
use crate::{
errors::ClientError,
store::notes::{InputNoteFilter, InputNoteRecord},
};
use objects::Digest;

impl Client {
// INPUT NOTE DATA RETRIEVAL
Expand All @@ -12,12 +14,12 @@ impl Client {
pub fn get_input_notes(
&self,
filter: InputNoteFilter,
) -> Result<Vec<RecordedNote>, ClientError> {
) -> Result<Vec<InputNoteRecord>, ClientError> {
self.store.get_input_notes(filter).map_err(|err| err.into())
}

/// Returns the input note with the specified hash.
pub fn get_input_note(&self, hash: Digest) -> Result<RecordedNote, ClientError> {
pub fn get_input_note(&self, hash: Digest) -> Result<InputNoteRecord, ClientError> {
self.store
.get_input_note_by_hash(hash)
.map_err(|err| err.into())
Expand All @@ -26,8 +28,8 @@ impl Client {
// INPUT NOTE CREATION
// --------------------------------------------------------------------------------------------

/// Inserts a new input note into the client's store.
pub fn import_input_note(&mut self, note: RecordedNote) -> Result<(), ClientError> {
/// Imports a new input note into the client's store.
pub fn import_input_note(&mut self, note: InputNoteRecord) -> Result<(), ClientError> {
self.store
.insert_input_note(&note)
.map_err(|err| err.into())
Expand Down
Loading
Loading