Skip to content
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
11 changes: 11 additions & 0 deletions src/api/method/get_compressed_account_proof/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
mod v1;
mod v2;

pub use v1::{
get_compressed_account_proof, GetCompressedAccountProofResponse,
GetCompressedAccountProofResponseValueV1,
};
pub use v2::{
get_compressed_account_proof_v2, GetCompressedAccountProofResponseV2,
GetCompressedAccountProofResponseValueV2,
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use super::{super::error::PhotonApiError, utils::HashRequest};
use crate::api::error::PhotonApiError;
use crate::api::method::utils::HashRequest;
use crate::common::typedefs::context::Context;
use crate::ingester::persist::get_multiple_compressed_leaf_proofs;
use crate::ingester::persist::persisted_state_tree::MerkleProofWithContext;
use crate::common::typedefs::hash::Hash;
use crate::common::typedefs::serializable_pubkey::SerializablePubkey;
use crate::ingester::persist::{get_multiple_compressed_leaf_proofs, MerkleProofWithContext};
use sea_orm::{ConnectionTrait, DatabaseBackend, DatabaseConnection, Statement, TransactionTrait};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
Expand All @@ -10,7 +12,32 @@ use utoipa::ToSchema;
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct GetCompressedAccountProofResponse {
pub context: Context,
pub value: MerkleProofWithContext,
pub value: GetCompressedAccountProofResponseValueV1,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
#[allow(non_snake_case)]
pub struct GetCompressedAccountProofResponseValueV1 {
pub proof: Vec<Hash>,
pub root: Hash,
pub leaf_index: u32,
pub hash: Hash,
pub merkle_tree: SerializablePubkey,
pub root_seq: u64,
}

impl From<MerkleProofWithContext> for GetCompressedAccountProofResponseValueV1 {
fn from(proof: MerkleProofWithContext) -> Self {
GetCompressedAccountProofResponseValueV1 {
proof: proof.proof,
root: proof.root,
leaf_index: proof.leaf_index,
hash: proof.hash,
merkle_tree: proof.merkle_tree,
root_seq: proof.root_seq,
}
}
}

pub async fn get_compressed_account_proof(
Expand All @@ -32,7 +59,7 @@ pub async fn get_compressed_account_proof(
.into_iter()
.next()
.map(|account| GetCompressedAccountProofResponse {
value: account,
value: account.into(),
context,
})
.ok_or(PhotonApiError::RecordNotFound(
Expand Down
141 changes: 141 additions & 0 deletions src/api/method/get_compressed_account_proof/v2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use crate::api::error::PhotonApiError;
use crate::api::method::get_validity_proof::ContextInfo;
use crate::api::method::utils::HashRequest;
use crate::common::typedefs::context::Context;
use crate::common::typedefs::hash::Hash;
use crate::common::typedefs::serializable_pubkey::SerializablePubkey;
use crate::dao::generated::{accounts, state_trees};
use crate::ingester::persist::{
get_multiple_compressed_leaf_proofs, get_multiple_compressed_leaf_proofs_by_indices,
MerkleProofWithContext,
};
use jsonrpsee_core::Serialize;
use sea_orm::{
ColumnTrait, ConnectionTrait, DatabaseBackend, DatabaseConnection, EntityTrait, QueryFilter,
Statement, TransactionTrait,
};
use serde::Deserialize;
use utoipa::ToSchema;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct GetCompressedAccountProofResponseV2 {
pub context: Context,
pub value: GetCompressedAccountProofResponseValueV2,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct GetCompressedAccountProofResponseValueV2 {
pub proof: Vec<Hash>,
pub root: Hash,
pub leaf_index: u32,
pub hash: Hash,
pub root_seq: u64,
pub prove_by_index: bool,
pub context: ContextInfo,
}

impl From<MerkleProofWithContext> for GetCompressedAccountProofResponseValueV2 {
fn from(proof: MerkleProofWithContext) -> Self {
GetCompressedAccountProofResponseValueV2 {
proof: proof.proof,
root: proof.root,
leaf_index: proof.leaf_index,
hash: proof.hash,
root_seq: proof.root_seq,
prove_by_index: false,
// Default values to be overridden as needed
context: ContextInfo {
tree_type: 0,
merkle_tree: proof.merkle_tree,
queue: Default::default(),
cpi_context: None,
},
}
}
}

pub async fn get_compressed_account_proof_v2(
conn: &DatabaseConnection,
request: HashRequest,
) -> Result<GetCompressedAccountProofResponseV2, PhotonApiError> {
let context = Context::extract(conn).await?;
let hash = request.hash;
let tx = conn.begin().await?;
if tx.get_database_backend() == DatabaseBackend::Postgres {
tx.execute(Statement::from_string(
tx.get_database_backend(),
"SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;".to_string(),
))
.await?;
}

let account = accounts::Entity::find()
.filter(accounts::Column::Hash.eq(hash.to_vec()))
.one(&tx)
.await?;

if account.is_none() {
return Err(PhotonApiError::RecordNotFound(
"Account not found".to_string(),
));
}

let leaf_node = state_trees::Entity::find()
.filter(
state_trees::Column::Hash
.eq(hash.to_vec())
.and(state_trees::Column::Level.eq(0)),
)
.one(&tx)
.await?;

// Determine how to generate the proof based on available data
let mut result: GetCompressedAccountProofResponseValueV2 = if leaf_node.is_some() {
let mut response: GetCompressedAccountProofResponseValueV2 =
get_multiple_compressed_leaf_proofs(&tx, vec![hash])
.await?
.into_iter()
.next()
.ok_or(PhotonApiError::RecordNotFound(
"Account not found by hash".to_string(),
))?
.into();
response.prove_by_index = false;
response
} else if let Some(account) = account.clone() {
// Use index-based proof if we found the account in a queue but not in state_trees
let leaf_index = account.leaf_index as u64;
let merkle_tree = SerializablePubkey::try_from(account.tree.clone())?;
let mut response: GetCompressedAccountProofResponseValueV2 =
get_multiple_compressed_leaf_proofs_by_indices(&tx, merkle_tree, vec![leaf_index])
.await?
.into_iter()
.next()
.ok_or(PhotonApiError::RecordNotFound(
"Account not found by index".to_string(),
))?
.into();
response.prove_by_index = true;
response
} else {
return Err(PhotonApiError::RecordNotFound(
"Account not found".to_string(),
));
};

// Enrich with account data if available

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't the call fail if no data is available?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if let Some(account) = account {
result.context.tree_type = account.tree_type as u16;
result.context.queue = SerializablePubkey::try_from(account.queue)?;
}

let response = GetCompressedAccountProofResponseV2 {
value: result,
context,
};

tx.commit().await?;
Ok(response)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use crate::ingester::persist::persisted_state_tree::MerkleProofWithContext;
mod v2;
pub use v2::{
get_multiple_compressed_account_proofs_v2, GetMultipleCompressedAccountProofsResponseV2,
};

use super::{super::error::PhotonApiError, utils::PAGE_LIMIT};
use crate::common::typedefs::context::Context;
use crate::common::typedefs::hash::Hash;
use crate::common::typedefs::serializable_pubkey::SerializablePubkey;
use crate::ingester::persist::get_multiple_compressed_leaf_proofs;
use crate::ingester::persist::MerkleProofWithContext;
use sea_orm::{ConnectionTrait, DatabaseBackend, DatabaseConnection, Statement, TransactionTrait};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
Expand All @@ -13,7 +18,31 @@ use utoipa::ToSchema;
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct GetMultipleCompressedAccountProofsResponse {
pub context: Context,
pub value: Vec<MerkleProofWithContext>,
pub value: Vec<GetMultipleCompressedAccountProofsResponseValue>,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct GetMultipleCompressedAccountProofsResponseValue {
pub proof: Vec<Hash>,
pub root: Hash,
pub leaf_index: u32,
pub hash: Hash,
pub merkle_tree: SerializablePubkey,
pub root_seq: u64,
}

impl From<MerkleProofWithContext> for GetMultipleCompressedAccountProofsResponseValue {
fn from(proof: MerkleProofWithContext) -> Self {
GetMultipleCompressedAccountProofsResponseValue {
proof: proof.proof,
root: proof.root,
leaf_index: proof.leaf_index,
hash: proof.hash,
merkle_tree: proof.merkle_tree,
root_seq: proof.root_seq,
}
}
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
Expand Down Expand Up @@ -43,7 +72,7 @@ pub async fn get_multiple_compressed_account_proofs(
let proofs = get_multiple_compressed_leaf_proofs(&tx, request).await?;
tx.commit().await?;
Ok(GetMultipleCompressedAccountProofsResponse {
value: proofs,
value: proofs.into_iter().map(Into::into).collect(),
context,
})
}
Loading