Skip to content
Closed
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
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ miden-node-validator = { path = "crates/validator", version = "0.13" }
miden-remote-prover-client = { path = "crates/remote-prover-client", version = "0.13" }

# miden-base aka protocol dependencies. These should be updated in sync.
miden-block-prover = { branch = "next", git = "https://github.com/0xMiden/miden-base.git" }
miden-protocol = { branch = "next", default-features = false, git = "https://github.com/0xMiden/miden-base.git" }
miden-standards = { branch = "next", git = "https://github.com/0xMiden/miden-base.git" }
miden-testing = { branch = "next", git = "https://github.com/0xMiden/miden-base.git" }
miden-tx = { branch = "next", default-features = false, git = "https://github.com/0xMiden/miden-base.git" }
miden-tx-batch-prover = { branch = "next", git = "https://github.com/0xMiden/miden-base.git" }
miden-block-prover = { branch = "sergerad-tx-inputs-foreign-acc-inputs", git = "https://github.com/0xMiden/miden-base.git" }
miden-protocol = { branch = "sergerad-tx-inputs-foreign-acc-inputs", default-features = false, git = "https://github.com/0xMiden/miden-base.git" }
miden-standards = { branch = "sergerad-tx-inputs-foreign-acc-inputs", git = "https://github.com/0xMiden/miden-base.git" }
miden-testing = { branch = "sergerad-tx-inputs-foreign-acc-inputs", git = "https://github.com/0xMiden/miden-base.git" }
miden-tx = { branch = "sergerad-tx-inputs-foreign-acc-inputs", default-features = false, git = "https://github.com/0xMiden/miden-base.git" }
miden-tx-batch-prover = { branch = "sergerad-tx-inputs-foreign-acc-inputs", git = "https://github.com/0xMiden/miden-base.git" }

# Other miden dependencies. These should align with those expected by miden-base.
miden-air = { features = ["std", "testing"], version = "0.20" }
Expand Down
175 changes: 136 additions & 39 deletions crates/ntx-builder/src/actor/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use miden_protocol::account::{
Account,
AccountId,
PartialAccount,
StorageMap,
StorageMapWitness,
StorageSlotContent,
};
use miden_protocol::asset::{AssetVaultKey, AssetWitness};
use miden_protocol::asset::{AssetVault, AssetVaultKey, AssetWitness};
use miden_protocol::block::{BlockHeader, BlockNumber};
use miden_protocol::note::{Note, NoteScript};
use miden_protocol::transaction::{
Expand Down Expand Up @@ -339,9 +340,49 @@ impl DataStore for NtxDataStore {
fn get_foreign_account_inputs(
&self,
foreign_account_id: AccountId,
_ref_block: BlockNumber,
ref_block: BlockNumber,
) -> impl FutureMaybeSend<Result<AccountInputs, DataStoreError>> {
async move { Err(DataStoreError::AccountNotFound(foreign_account_id)) }
debug_assert_eq!(ref_block, self.reference_header.block_num());

let store = self.store.clone();
async move {
if foreign_account_id == self.account.id() {
return Err(DataStoreError::Other {
error_msg: format!(
"requested account with id {foreign_account_id} is local, not foreign"
)
.into(),
source: None,
});
}

// Retrieve the account proof from the store.
let account_proof = store
.get_account(foreign_account_id, Some(ref_block.as_u32()))
.await
.map_err(|err| DataStoreError::Other {
error_msg: format!("failed to get account proof from store: {err}").into(),
source: Some(Box::new(err)),
})?;

// Construct account from account proof account details.
let account_details = account_proof.details.ok_or_else(|| DataStoreError::Other {
error_msg: "account proof does not contain account details".into(),
source: None,
})?;
let partial_account = PartialAccount::try_from(&account_details).map_err(|err| {
DataStoreError::Other {
error_msg: format!(
"failed to construct partial account from account details: {err}"
)
.into(),
source: Some(Box::new(err)),
}
})?;

// Return partial account and witness.
Ok(AccountInputs::new(partial_account, account_proof.witness))
}
}

fn get_vault_asset_witnesses(
Expand All @@ -350,26 +391,42 @@ impl DataStore for NtxDataStore {
vault_root: Word,
vault_keys: BTreeSet<AssetVaultKey>,
) -> impl FutureMaybeSend<Result<Vec<AssetWitness>, DataStoreError>> {
let store = self.store.clone();
async move {
if self.account.id() != account_id {
return Err(DataStoreError::AccountNotFound(account_id));
}

if self.account.vault().root() != vault_root {
return Err(DataStoreError::Other {
error_msg: "vault root mismatch".into(),
source: None,
});
}

Result::<Vec<_>, _>::from_iter(vault_keys.into_iter().map(|vault_key| {
AssetWitness::new(self.account.vault().open(vault_key).into()).map_err(|err| {
DataStoreError::Other {
error_msg: "failed to open vault asset tree".into(),
if self.account.id() == account_id {
if self.account.vault().root() != vault_root {
return Err(DataStoreError::Other {
error_msg: "vault root mismatch".into(),
source: None,
});
}
get_asset_witnesses(vault_keys, self.account.vault())
} else {
// Get foreign account.
let account_proof = store
.get_account(account_id, self.reference_header.block_num().as_u32().into())
.await
.map_err(|err| DataStoreError::Other {
error_msg: format!("Failed to get account inputs from store: {err}").into(),
source: Some(Box::new(err)),
}
})
}))
})?;

// Construct vault from account details.
let account_details =
account_proof.details.ok_or_else(|| DataStoreError::Other {
error_msg: "account proof does not contain account details".into(),
source: None,
})?;
let asset_vault =
AssetVault::new(&account_details.vault_details.assets).map_err(|err| {
DataStoreError::Other {
error_msg: format!("failed to create asset vault: {err}").into(),
source: Some(Box::new(err)),
}
})?;

get_asset_witnesses(vault_keys, &asset_vault)
}
}
}

Expand All @@ -379,28 +436,53 @@ impl DataStore for NtxDataStore {
map_root: Word,
map_key: Word,
) -> impl FutureMaybeSend<Result<StorageMapWitness, DataStoreError>> {
let store = self.store.clone();
async move {
if self.account.id() != account_id {
return Err(DataStoreError::AccountNotFound(account_id));
}

let mut map_witness = None;
for slot in self.account.storage().slots() {
if let StorageSlotContent::Map(map) = slot.content() {
if map.root() == map_root {
map_witness = Some(map.open(&map_key));
let map_witness = if self.account.id() == account_id {
// Search through local account's storage slots.
self.account.storage().slots().iter().find_map(|slot| {
if let StorageSlotContent::Map(map) = slot.content() {
if map.root() == map_root {
Some(map.open(&map_key))
} else {
None
}
} else {
None
}
}
}

if let Some(map_witness) = map_witness {
Ok(map_witness)
})
} else {
Err(DataStoreError::Other {
error_msg: "account storage does not contain the expected root".into(),
source: None,
// Get foreign account.
let account_proof = store
.get_account(account_id, self.reference_header.block_num().as_u32().into())
.await
.map_err(|err| DataStoreError::Other {
error_msg: format!("failed to get account proof from store: {err}").into(),
source: Some(Box::new(err)),
})?;
let account_details =
account_proof.details.ok_or_else(|| DataStoreError::Other {
error_msg: "account proof does not contain account details".into(),
source: None,
})?;

// Search through foreign account's storage maps.
account_details.storage_details.map_details.iter().find_map(|map_details| {
let storage_map =
StorageMap::with_entries(map_details.map_entries.iter().copied())
.expect("no duplicate entries");
if storage_map.root() == map_root {
Some(storage_map.open(&map_key))
} else {
None
}
})
}
};

map_witness.ok_or_else(|| DataStoreError::Other {
error_msg: "account storage does not contain the expected root".into(),
source: None,
})
}
}

Expand Down Expand Up @@ -445,3 +527,18 @@ impl MastForestStore for NtxDataStore {
self.mast_store.get(procedure_hash)
}
}

// HELPERS
// ================================================================================================

fn get_asset_witnesses(
vault_keys: BTreeSet<AssetVaultKey>,
vault: &AssetVault,
) -> Result<Vec<AssetWitness>, DataStoreError> {
Result::<Vec<_>, _>::from_iter(vault_keys.into_iter().map(|vault_key| {
AssetWitness::new(vault.open(vault_key).into()).map_err(|err| DataStoreError::Other {
error_msg: "failed to open vault asset tree".into(),
source: Some(Box::new(err)),
})
}))
}
24 changes: 23 additions & 1 deletion crates/ntx-builder/src/store.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
use std::time::Duration;

use miden_node_proto::clients::{Builder, StoreNtxBuilderClient};
use miden_node_proto::domain::account::NetworkAccountPrefix;
use miden_node_proto::domain::account::{AccountProofResponse, NetworkAccountPrefix};
use miden_node_proto::domain::note::NetworkNote;
use miden_node_proto::errors::ConversionError;
use miden_node_proto::generated::rpc::BlockRange;
use miden_node_proto::generated::rpc::account_proof_request::AccountDetailRequest;
use miden_node_proto::generated::{self as proto};
use miden_node_proto::try_convert;
use miden_protocol::Word;
use miden_protocol::account::{Account, AccountId};
use miden_protocol::block::{BlockHeader, BlockNumber};
use miden_protocol::crypto::merkle::mmr::{Forest, MmrPeaks, PartialMmr};
use miden_protocol::note::NoteScript;
use miden_protocol::utils::Serializable;
use miden_tx::utils::Deserializable;
use thiserror::Error;
use tracing::{info, instrument};
Expand Down Expand Up @@ -253,6 +255,26 @@ impl StoreClient {
Ok(None)
}
}

#[instrument(target = COMPONENT, name = "store.client.get_account", skip_all, err)]
pub async fn get_account(
&self,
account_id: AccountId,
ref_block: Option<u32>,
) -> Result<AccountProofResponse, StoreError> {
let request = proto::rpc::AccountProofRequest {
account_id: Some(proto::account::AccountId { id: account_id.to_bytes() }),
block_num: ref_block.map(|block_num| proto::blockchain::BlockNumber { block_num }),
details: Some(AccountDetailRequest {
code_commitment: None,
asset_vault_commitment: None,
storage_maps: Vec::new(),
}),
};

let response = self.inner.clone().get_account_proof(request).await?.into_inner();
response.try_into().map_err(StoreError::DeserializationError)
}
}

// Store errors
Expand Down
Loading
Loading