Skip to content

Commit

Permalink
Merge pull request #1485 from input-output-hk/ensemble/1468/cert_hash…
Browse files Browse the repository at this point in the history
…_in_tx_set_message

Certificate hash in transaction set message
  • Loading branch information
Alenar committed Feb 5, 2024
2 parents 69adabe + 66d5053 commit 794aa57
Show file tree
Hide file tree
Showing 17 changed files with 397 additions and 105 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion mithril-aggregator/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mithril-aggregator"
version = "0.4.32"
version = "0.4.33"
description = "A Mithril Aggregator server"
authors = { workspace = true }
edition = { workspace = true }
Expand Down
97 changes: 90 additions & 7 deletions mithril-aggregator/src/database/provider/cardano_transaction.rs
@@ -1,5 +1,5 @@
use mithril_common::{
entities::{BlockNumber, CardanoTransaction, ImmutableFileNumber, TransactionHash},
entities::{Beacon, BlockNumber, CardanoTransaction, ImmutableFileNumber, TransactionHash},
signable_builder::TransactionStore,
sqlite::{
HydrationError, Projection, Provider, SourceAlias, SqLiteEntity, SqliteConnection,
Expand Down Expand Up @@ -102,6 +102,13 @@ impl<'client> CardanoTransactionProvider<'client> {
vec![Value::String(transaction_hash.to_owned())],
)
}

pub(crate) fn get_transaction_up_to_beacon_condition(&self, beacon: &Beacon) -> WhereCondition {
WhereCondition::new(
"immutable_file_number <= ?*",
vec![Value::Integer(beacon.immutable_file_number as i64)],
)
}
}

impl<'client> Provider<'client> for CardanoTransactionProvider<'client> {
Expand Down Expand Up @@ -207,6 +214,18 @@ impl CardanoTransactionRepository {
Ok(transactions.collect())
}

/// Return all the [CardanoTransactionRecord]s in the database up to the given beacon.
pub async fn get_transactions_up_to(
&self,
beacon: &Beacon,
) -> StdResult<Vec<CardanoTransactionRecord>> {
let provider = CardanoTransactionProvider::new(&self.connection);
let filters = provider.get_transaction_up_to_beacon_condition(beacon);
let transactions = provider.find(filters)?;

Ok(transactions.collect())
}

/// Return the [CardanoTransactionRecord] for the given transaction hash.
pub async fn get_transaction(
&self,
Expand Down Expand Up @@ -265,8 +284,8 @@ impl TransactionStore for CardanoTransactionRepository {

#[async_trait]
impl TransactionsRetriever for CardanoTransactionRepository {
async fn get_all(&self) -> StdResult<Vec<CardanoTransaction>> {
self.get_all_transactions().await.map(|v| {
async fn get_up_to(&self, beacon: &Beacon) -> StdResult<Vec<CardanoTransaction>> {
self.get_transactions_up_to(beacon).await.map(|v| {
v.into_iter()
.map(|record| record.into())
.collect::<Vec<CardanoTransaction>>()
Expand Down Expand Up @@ -322,6 +341,21 @@ mod tests {
);
}

#[test]
fn provider_transaction_up_to_beacon_condition() {
let connection = Connection::open_thread_safe(":memory:").unwrap();
let provider = CardanoTransactionProvider::new(&connection);
let (expr, params) = provider
.get_transaction_up_to_beacon_condition(&Beacon {
immutable_file_number: 2309,
..Beacon::default()
})
.expand();

assert_eq!("immutable_file_number <= ?1".to_string(), expr);
assert_eq!(vec![Value::Integer(2309)], params,);
}

#[test]
fn insert_provider_condition() {
let connection = Connection::open_thread_safe(":memory:").unwrap();
Expand Down Expand Up @@ -495,6 +529,55 @@ mod tests {
);
}

#[tokio::test]
async fn repository_store_transactions_and_get_up_to_beacon_transactions() {
let connection = get_connection().await;
let repository = CardanoTransactionRepository::new(connection.clone());

let cardano_transactions: Vec<CardanoTransaction> = (20..=40)
.map(|i| CardanoTransaction {
transaction_hash: format!("tx-hash-{i}"),
block_number: i % 10,
immutable_file_number: i,
})
.collect();
repository
.store_transactions(&cardano_transactions)
.await
.unwrap();

let transaction_result = repository
.get_up_to(&Beacon::new("".to_string(), 1, 34))
.await
.unwrap();

assert_eq!(
cardano_transactions[0..=14]
.iter()
.cloned()
.rev()
.collect::<Vec<_>>(),
transaction_result
);

let transaction_result = repository
.get_up_to(&Beacon::new("".to_string(), 1, 300))
.await
.unwrap();

assert_eq!(
cardano_transactions.into_iter().rev().collect::<Vec<_>>(),
transaction_result
);

let transaction_result = repository
.get_up_to(&Beacon::new("".to_string(), 1, 19))
.await
.unwrap();

assert_eq!(Vec::<CardanoTransaction>::new(), transaction_result);
}

#[tokio::test]
async fn repository_store_transactions_and_get_all_stored_transactions() {
let connection = get_connection().await;
Expand All @@ -517,12 +600,12 @@ mod tests {
.await
.unwrap();

let transactions_result = repository.get_all().await.unwrap();
let transactions_expected = cardano_transactions
let transactions_result = repository.get_all_transactions().await.unwrap();
let transactions_expected: Vec<CardanoTransactionRecord> = cardano_transactions
.iter()
.rev()
.cloned()
.collect::<Vec<_>>();
.map(|tx| tx.clone().into())
.collect();

assert_eq!(transactions_expected, transactions_result);
}
Expand Down
17 changes: 17 additions & 0 deletions mithril-aggregator/src/http_server/routes/mod.rs
Expand Up @@ -9,3 +9,20 @@ pub mod router;
mod signatures_routes;
mod signer_routes;
mod statistics_routes;

/// Match the given result and do an early return with an internal server error (500)
/// if it was an Error. Else return the unwrapped value.
#[macro_export]
macro_rules! unwrap_to_internal_server_error {
($code:expr, $($warn_comment:tt)*) => {
match $code {
Ok(res) => res,
Err(err) => {
warn!($($warn_comment)*; "error" => ?err);
return Ok($crate::http_server::routes::reply::internal_server_error(
err,
));
}
}
};
}
111 changes: 87 additions & 24 deletions mithril-aggregator/src/http_server/routes/proof_routes.rs
Expand Up @@ -29,25 +29,30 @@ fn proof_cardano_transaction(
warp::path!("proof" / "cardano-transaction")
.and(warp::get())
.and(warp::query::<CardanoTransactionProofQueryParams>())
.and(middlewares::with_signed_entity_service(
dependency_manager.clone(),
))
.and(middlewares::with_prover_service(dependency_manager))
.and_then(handlers::proof_cardano_transaction)
}

mod handlers {
use std::{convert::Infallible, sync::Arc};

use reqwest::StatusCode;
use slog_scope::{debug, warn};
use std::{convert::Infallible, sync::Arc};

use crate::{
http_server::routes::reply, message_adapters::ToCardanoTransactionsProofsMessageAdapter,
services::ProverService,
http_server::routes::reply,
message_adapters::ToCardanoTransactionsProofsMessageAdapter,
services::{ProverService, SignedEntityService},
unwrap_to_internal_server_error,
};

use super::CardanoTransactionProofQueryParams;

pub async fn proof_cardano_transaction(
transaction_parameters: CardanoTransactionProofQueryParams,
signed_entity_service: Arc<dyn SignedEntityService>,
prover_service: Arc<dyn ProverService>,
) -> Result<impl warp::Reply, Infallible> {
let transaction_hashes = transaction_parameters
Expand All @@ -60,20 +65,36 @@ mod handlers {
transaction_parameters.transaction_hashes
);

match prover_service
.compute_transactions_proofs(transaction_hashes.as_slice())
.await
{
Ok(transactions_set_proofs) => Ok(reply::json(
&ToCardanoTransactionsProofsMessageAdapter::adapt(
transactions_set_proofs,
transaction_hashes,
),
StatusCode::OK,
)),
Err(err) => {
warn!("proof_cardano_transaction::error"; "error" => ?err);
Ok(reply::internal_server_error(err))
match unwrap_to_internal_server_error!(
signed_entity_service
.get_last_cardano_transaction_commitment()
.await,
"proof_cardano_transaction::error"
) {
Some(signed_entity) => {
let transactions_set_proofs = unwrap_to_internal_server_error!(
prover_service
.compute_transactions_proofs(
&signed_entity.artifact.beacon,
transaction_hashes.as_slice(),
)
.await,
"proof_cardano_transaction::error"
);

let message = unwrap_to_internal_server_error!(
ToCardanoTransactionsProofsMessageAdapter::try_adapt(
&signed_entity.certificate_id,
transactions_set_proofs,
transaction_hashes,
),
"proof_cardano_transaction::error"
);
Ok(reply::json(&message, StatusCode::OK))
}
None => {
warn!("proof_cardano_transaction::not_found");
Ok(reply::empty(StatusCode::NOT_FOUND))
}
}
}
Expand All @@ -82,13 +103,18 @@ mod handlers {
#[cfg(test)]
mod tests {
use super::*;
use std::vec;

use mithril_common::test_utils::apispec::APISpec;

use anyhow::anyhow;
use mithril_common::entities::{
CardanoTransactionsCommitment, CardanoTransactionsSetProof, SignedEntity,
};
use serde_json::Value::Null;
use warp::{http::Method, test::request};

use crate::services::MockSignedEntityService;
use crate::{
dependency_injection::DependenciesBuilder, http_server::SERVER_BASE_PATH,
services::MockProverService, Configuration,
Expand All @@ -109,6 +135,44 @@ mod tests {

#[tokio::test]
async fn proof_cardano_transaction_ok() {
let config = Configuration::new_sample();
let mut builder = DependenciesBuilder::new(config);
let mut dependency_manager = builder.build_dependency_container().await.unwrap();
let mut mock_signed_entity_service = MockSignedEntityService::new();
mock_signed_entity_service
.expect_get_last_cardano_transaction_commitment()
.returning(|| Ok(Some(SignedEntity::<CardanoTransactionsCommitment>::dummy())));
dependency_manager.signed_entity_service = Arc::new(mock_signed_entity_service);

let mut mock_prover_service = MockProverService::new();
mock_prover_service
.expect_compute_transactions_proofs()
.returning(|_, _| Ok(vec![CardanoTransactionsSetProof::dummy()]));
dependency_manager.prover_service = Arc::new(mock_prover_service);

let method = Method::GET.as_str();
let path = "/proof/cardano-transaction";

let response = request()
.method(method)
.path(&format!(
"/{SERVER_BASE_PATH}{path}?transaction_hashes=tx-123,tx-456"
))
.reply(&setup_router(Arc::new(dependency_manager)))
.await;

APISpec::verify_conformity(
APISpec::get_all_spec_files(),
method,
path,
"application/json",
&Null,
&response,
);
}

#[tokio::test]
async fn proof_cardano_transaction_not_found() {
let config = Configuration::new_sample();
let mut builder = DependenciesBuilder::new(config);
let dependency_manager = builder.build_dependency_container().await.unwrap();
Expand Down Expand Up @@ -139,12 +203,11 @@ mod tests {
let config = Configuration::new_sample();
let mut builder = DependenciesBuilder::new(config);
let mut dependency_manager = builder.build_dependency_container().await.unwrap();
let mut mock_prover_service = MockProverService::new();
mock_prover_service
.expect_compute_transactions_proofs()
.returning(|_| Err(anyhow!("Error")))
.times(1);
dependency_manager.prover_service = Arc::new(mock_prover_service);
let mut mock_signed_entity_service = MockSignedEntityService::new();
mock_signed_entity_service
.expect_get_last_cardano_transaction_commitment()
.returning(|| Err(anyhow!("Error")));
dependency_manager.signed_entity_service = Arc::new(mock_signed_entity_service);

let method = Method::GET.as_str();
let path = "/proof/cardano-transaction";
Expand Down

0 comments on commit 794aa57

Please sign in to comment.