Skip to content

Commit a44b3a6

Browse files
committed
Copy cardano tx & block range roots database from aggregator to persistence
In order to share them between the aggregator & signer. Some changes were made: * all business traits implementation were removed * most of the business traits methods are still there, but directly on the repository and may have name and signatures changes (to return the record instead of the business type most of the time). * "get_all" are now public and not just test only as it would be cumbersome to add a 'test_only' feature flags just for that.
1 parent dbe936c commit a44b3a6

16 files changed

+1692
-3
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! Migration module for cardano transactions store
2+
//!
3+
use crate::database::SqlMigration;
4+
5+
/// Get all the migrations required by this version of the software.
6+
/// There shall be one migration per database version. There could be several
7+
/// statements per migration.
8+
pub fn get_migrations() -> Vec<SqlMigration> {
9+
vec![
10+
// Migration 1
11+
// Add the `cardano_tx` table.
12+
SqlMigration::new(
13+
1,
14+
r#"
15+
create table cardano_tx (
16+
transaction_hash text not null,
17+
block_number integer not null,
18+
immutable_file_number integer not null,
19+
primary key (transaction_hash)
20+
);
21+
22+
create unique index cardano_tx_immutable_file_number_index on cardano_tx(immutable_file_number);
23+
"#,
24+
),
25+
// Migration 2
26+
// Fix the `cardano_tx` table index on immutable_file number, incorrectly marked as unique.
27+
SqlMigration::new(
28+
2,
29+
r#"
30+
-- remove all data from the cardano tx table since a lot of transactions where missing for each
31+
-- block and we rely on their insert order.
32+
delete from cardano_tx;
33+
34+
drop index cardano_tx_immutable_file_number_index;
35+
create index cardano_tx_immutable_file_number_index on cardano_tx(immutable_file_number);
36+
37+
vacuum;
38+
"#,
39+
),
40+
// Migration 3
41+
// Add `slot_number` and `block_hash` columns to `cardano_tx`.
42+
SqlMigration::new(
43+
3,
44+
r#"
45+
-- remove all data from the cardano tx table since the new columns are mandatory
46+
delete from cardano_tx;
47+
48+
alter table cardano_tx add column slot_number integer not null;
49+
alter table cardano_tx add column block_hash text not null;
50+
51+
vacuum;
52+
"#,
53+
),
54+
// Migration 4
55+
// Add index on `block_number` column of `cardano_tx` table
56+
SqlMigration::new(
57+
4,
58+
r#"
59+
create index block_number_index on cardano_tx(block_number);
60+
"#,
61+
),
62+
// Migration 5
63+
// Add `block_range_root` table
64+
SqlMigration::new(
65+
5,
66+
r#"
67+
create table block_range_root (
68+
start integer not null,
69+
end integer not null,
70+
merkle_root text not null,
71+
primary key (start, end)
72+
);
73+
"#,
74+
),
75+
// Migration 6
76+
// Add composite index on `block_number/transaction_hash` column of `cardano_tx` table
77+
// Truncate `block_range_root` table after changing the order of retrieval of the transactions
78+
SqlMigration::new(
79+
6,
80+
r#"
81+
create index block_number_transaction_hash_index on cardano_tx(block_number, transaction_hash);
82+
83+
-- remove all data from the block_range_root table since the order used to create them has changed
84+
delete from block_range_root;
85+
86+
vacuum;
87+
"#,
88+
),
89+
]
90+
}
Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
11
//! database module.
22
//! This module contains providers and entities shared between all application types.
33
4+
pub mod cardano_transaction_migration;
45
mod db_version;
6+
pub(crate) mod provider;
7+
pub mod record;
8+
pub mod repository;
59
mod signed_entity_hydrator;
610
mod version_checker;
711

8-
/// Database version.
9-
pub type DbVersion = i64;
10-
1112
pub use db_version::*;
1213
pub use signed_entity_hydrator::SignedEntityTypeHydrator;
1314
pub use version_checker::{DatabaseVersionChecker, SqlMigration};
15+
16+
/// Database version.
17+
pub type DbVersion = i64;
18+
19+
#[cfg(test)]
20+
pub mod test_helper {
21+
use sqlite::ConnectionThreadSafe;
22+
23+
use mithril_common::StdResult;
24+
25+
use crate::sqlite::{ConnectionBuilder, ConnectionOptions};
26+
27+
/// In-memory sqlite database without foreign key support with cardano db migrations applied
28+
pub fn cardano_tx_db_connection() -> StdResult<ConnectionThreadSafe> {
29+
let connection = ConnectionBuilder::open_memory()
30+
.with_options(&[ConnectionOptions::ForceDisableForeignKeys])
31+
.with_migrations(crate::database::cardano_transaction_migration::get_migrations())
32+
.build()?;
33+
Ok(connection)
34+
}
35+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use mithril_common::entities::BlockNumber;
2+
use sqlite::Value;
3+
4+
use crate::database::record::BlockRangeRootRecord;
5+
use crate::sqlite::{Provider, SourceAlias, SqLiteEntity, SqliteConnection, WhereCondition};
6+
7+
/// Simple queries to retrieve [BlockRangeRootRecord] from the sqlite database.
8+
pub struct GetBlockRangeRootProvider<'client> {
9+
connection: &'client SqliteConnection,
10+
}
11+
12+
impl<'client> GetBlockRangeRootProvider<'client> {
13+
/// Create a new instance
14+
pub fn new(connection: &'client SqliteConnection) -> Self {
15+
Self { connection }
16+
}
17+
18+
pub fn get_up_to_block_number_condition(&self, block_number: BlockNumber) -> WhereCondition {
19+
WhereCondition::new("end < ?*", vec![Value::Integer(block_number as i64)])
20+
}
21+
}
22+
23+
impl crate::sqlite::GetAllCondition for GetBlockRangeRootProvider<'_> {}
24+
25+
impl<'client> Provider<'client> for GetBlockRangeRootProvider<'client> {
26+
type Entity = BlockRangeRootRecord;
27+
28+
fn get_connection(&'client self) -> &'client SqliteConnection {
29+
self.connection
30+
}
31+
32+
fn get_definition(&self, condition: &str) -> String {
33+
let aliases = SourceAlias::new(&[("{:block_range_root:}", "block_range_root")]);
34+
let projection = Self::Entity::get_projection().expand(aliases);
35+
36+
format!("select {projection} from block_range_root where {condition} order by start, end")
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use crate::database::record::IntervalWithoutBlockRangeRootRecord;
2+
use crate::sqlite::{Provider, SourceAlias, SqLiteEntity, SqliteConnection, WhereCondition};
3+
4+
/// Query that return the interval of block numbers that does not have a block range root.
5+
pub struct GetIntervalWithoutBlockRangeRootProvider<'client> {
6+
connection: &'client SqliteConnection,
7+
}
8+
9+
impl<'client> GetIntervalWithoutBlockRangeRootProvider<'client> {
10+
/// Create a new instance
11+
pub fn new(connection: &'client SqliteConnection) -> Self {
12+
Self { connection }
13+
}
14+
15+
pub fn get_interval_without_block_range_condition(&self) -> WhereCondition {
16+
WhereCondition::default()
17+
}
18+
}
19+
20+
impl<'client> Provider<'client> for GetIntervalWithoutBlockRangeRootProvider<'client> {
21+
type Entity = IntervalWithoutBlockRangeRootRecord;
22+
23+
fn get_connection(&'client self) -> &'client SqliteConnection {
24+
self.connection
25+
}
26+
27+
fn get_definition(&self, condition: &str) -> String {
28+
let aliases = SourceAlias::new(&[
29+
("{:interval_start:}", "interval_start"),
30+
("{:interval_end:}", "interval_end"),
31+
]);
32+
let projection = Self::Entity::get_projection().expand(aliases);
33+
34+
format!(
35+
"select {projection} from \
36+
(select max(end) as start from block_range_root where {condition}) as interval_start, \
37+
(select max(block_number) as end from cardano_tx where {condition}) as interval_end"
38+
)
39+
}
40+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
use std::iter::repeat;
2+
3+
use sqlite::Value;
4+
5+
use mithril_common::StdResult;
6+
7+
use crate::database::record::BlockRangeRootRecord;
8+
use crate::sqlite::{Provider, SourceAlias, SqLiteEntity, SqliteConnection, WhereCondition};
9+
10+
/// Query to insert [BlockRangeRootRecord] in the sqlite database
11+
pub struct InsertBlockRangeRootProvider<'client> {
12+
connection: &'client SqliteConnection,
13+
}
14+
15+
impl<'client> InsertBlockRangeRootProvider<'client> {
16+
/// Create a new instance
17+
pub fn new(connection: &'client SqliteConnection) -> Self {
18+
Self { connection }
19+
}
20+
21+
/// Condition to insert multiples records.
22+
pub fn get_insert_many_condition(
23+
&self,
24+
block_range_records: Vec<BlockRangeRootRecord>,
25+
) -> StdResult<WhereCondition> {
26+
let columns = "(start, end, merkle_root)";
27+
let values_columns: Vec<&str> = repeat("(?*, ?*, ?*)")
28+
.take(block_range_records.len())
29+
.collect();
30+
31+
let values: StdResult<Vec<Value>> =
32+
block_range_records
33+
.into_iter()
34+
.try_fold(vec![], |mut vec, record| {
35+
vec.append(&mut vec![
36+
Value::Integer(record.range.start.try_into()?),
37+
Value::Integer(record.range.end.try_into()?),
38+
Value::String(record.merkle_root.to_hex()),
39+
]);
40+
Ok(vec)
41+
});
42+
43+
Ok(WhereCondition::new(
44+
format!("{columns} values {}", values_columns.join(", ")).as_str(),
45+
values?,
46+
))
47+
}
48+
}
49+
50+
impl<'client> Provider<'client> for InsertBlockRangeRootProvider<'client> {
51+
type Entity = BlockRangeRootRecord;
52+
53+
fn get_connection(&'client self) -> &'client SqliteConnection {
54+
self.connection
55+
}
56+
57+
fn get_definition(&self, condition: &str) -> String {
58+
let aliases = SourceAlias::new(&[("{:block_range_root:}", "block_range_root")]);
59+
let projection = Self::Entity::get_projection().expand(aliases);
60+
61+
format!("insert or ignore into block_range_root {condition} returning {projection}")
62+
}
63+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
mod get_block_range_root;
2+
mod get_interval_without_block_range_provider;
3+
mod insert_block_range;
4+
5+
pub use get_block_range_root::*;
6+
pub use get_interval_without_block_range_provider::*;
7+
pub use insert_block_range::*;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use std::ops::Range;
2+
3+
use sqlite::Value;
4+
5+
use mithril_common::entities::{BlockNumber, BlockRange, TransactionHash};
6+
7+
use crate::database::record::CardanoTransactionRecord;
8+
use crate::sqlite::{
9+
GetAllCondition, Provider, SourceAlias, SqLiteEntity, SqliteConnection, WhereCondition,
10+
};
11+
12+
/// Simple queries to retrieve [CardanoTransaction] from the sqlite database.
13+
pub struct GetCardanoTransactionProvider<'client> {
14+
connection: &'client SqliteConnection,
15+
}
16+
17+
impl<'client> GetCardanoTransactionProvider<'client> {
18+
/// Create a new instance
19+
pub fn new(connection: &'client SqliteConnection) -> Self {
20+
Self { connection }
21+
}
22+
23+
// Useful in test and probably in the future.
24+
pub fn get_transaction_hash_condition(
25+
&self,
26+
transaction_hash: &TransactionHash,
27+
) -> WhereCondition {
28+
WhereCondition::new(
29+
"transaction_hash = ?*",
30+
vec![Value::String(transaction_hash.to_owned())],
31+
)
32+
}
33+
34+
pub fn get_transaction_hashes_condition(
35+
&self,
36+
transactions_hashes: Vec<TransactionHash>,
37+
) -> WhereCondition {
38+
let hashes_values = transactions_hashes.into_iter().map(Value::String).collect();
39+
40+
WhereCondition::where_in("transaction_hash", hashes_values)
41+
}
42+
43+
pub fn get_transaction_block_ranges_condition(
44+
&self,
45+
block_ranges: Vec<BlockRange>,
46+
) -> WhereCondition {
47+
let mut where_condition = WhereCondition::default();
48+
for block_range in block_ranges {
49+
where_condition = where_condition.or_where(WhereCondition::new(
50+
"(block_number >= ?* and block_number < ?*)",
51+
vec![
52+
Value::Integer(block_range.start as i64),
53+
Value::Integer(block_range.end as i64),
54+
],
55+
))
56+
}
57+
58+
where_condition
59+
}
60+
61+
pub fn get_transaction_between_blocks_condition(
62+
&self,
63+
range: Range<BlockNumber>,
64+
) -> WhereCondition {
65+
WhereCondition::new(
66+
"block_number >= ?*",
67+
vec![Value::Integer(range.start as i64)],
68+
)
69+
.and_where(WhereCondition::new(
70+
"block_number < ?*",
71+
vec![Value::Integer(range.end as i64)],
72+
))
73+
}
74+
}
75+
76+
impl<'client> Provider<'client> for GetCardanoTransactionProvider<'client> {
77+
type Entity = CardanoTransactionRecord;
78+
79+
fn get_connection(&'client self) -> &'client SqliteConnection {
80+
self.connection
81+
}
82+
83+
fn get_definition(&self, condition: &str) -> String {
84+
let aliases = SourceAlias::new(&[("{:cardano_tx:}", "cardano_tx")]);
85+
let projection = Self::Entity::get_projection().expand(aliases);
86+
87+
format!("select {projection} from cardano_tx where {condition} order by block_number, transaction_hash")
88+
}
89+
}
90+
91+
impl GetAllCondition for GetCardanoTransactionProvider<'_> {}

0 commit comments

Comments
 (0)