Skip to content

Commit c0a7b67

Browse files
authored
Merge pull request #1650 from input-output-hk/ensemble/1633/store-block-ranges-in-db
Store block range roots in db
2 parents 86c172c + b0decd8 commit c0a7b67

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2836
-481
lines changed

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/mithril-persistence/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-persistence"
3-
version = "0.1.6"
3+
version = "0.1.7"
44
description = "Common types, interfaces, and utilities to persist data for Mithril nodes."
55
authors = { workspace = true }
66
edition = { workspace = true }

internal/mithril-persistence/src/sqlite/condition.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ impl WhereCondition {
137137
}
138138
}
139139

140+
/// Get all condition builder.
141+
pub trait GetAllCondition {
142+
/// Get the condition for a get all query.
143+
fn get_all_condition() -> WhereCondition {
144+
WhereCondition::default()
145+
}
146+
}
147+
140148
#[cfg(test)]
141149
mod tests {
142150
use super::*;
@@ -349,4 +357,15 @@ mod tests {
349357
assert_eq!("a = ?1", &sql);
350358
assert_eq!(1, params.len());
351359
}
360+
361+
#[test]
362+
fn expression_get_all_default() {
363+
impl GetAllCondition for String {}
364+
365+
let expression = String::get_all_condition();
366+
let (sql, params) = expression.expand();
367+
368+
assert_eq!("true".to_string(), sql);
369+
assert!(params.is_empty());
370+
}
352371
}

internal/mithril-persistence/src/sqlite/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ mod projection;
1010
mod provider;
1111
mod source_alias;
1212

13-
pub use condition::WhereCondition;
13+
pub use condition::{GetAllCondition, WhereCondition};
1414
pub use connection_builder::{ConnectionBuilder, ConnectionOptions};
1515
pub use cursor::EntityCursor;
1616
pub use entity::{HydrationError, SqLiteEntity};
1717
pub use projection::{Projection, ProjectionField};
18-
pub use provider::Provider;
18+
pub use provider::{GetAllProvider, Provider};
1919
pub use source_alias::SourceAlias;
2020

2121
use mithril_common::StdResult;

internal/mithril-persistence/src/sqlite/provider.rs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
use crate::sqlite::condition::GetAllCondition;
12
use anyhow::Context;
23
use mithril_common::StdResult;
34

45
use super::{EntityCursor, SqLiteEntity, SqliteConnection, WhereCondition};
56

6-
/// A Provider is able to performe queries on a database and return iterator of a defined entity.
7+
/// A Provider is able to perform queries on a database and return iterator of a defined entity.
78
/// It aims at being easily testable and adaptable.
89
pub trait Provider<'conn> {
910
/// Entity type returned by the result cursor.
@@ -38,6 +39,22 @@ pub trait Provider<'conn> {
3839
fn get_definition(&self, condition: &str) -> String;
3940
}
4041

42+
/// An extension of the [Provider] that can return all the entities of a given type.
43+
pub trait GetAllProvider<'conn, E: SqLiteEntity> {
44+
/// Return all the entities of the given type.
45+
fn get_all(&'conn self) -> StdResult<EntityCursor<'conn, E>>;
46+
}
47+
48+
impl<'conn, P, E> GetAllProvider<'conn, E> for P
49+
where
50+
P: Provider<'conn, Entity = E> + GetAllCondition,
51+
E: SqLiteEntity,
52+
{
53+
fn get_all(&'conn self) -> StdResult<EntityCursor<'conn, E>> {
54+
self.find(P::get_all_condition())
55+
}
56+
}
57+
4158
#[cfg(test)]
4259
mod tests {
4360
use sqlite::{Connection, Value};
@@ -136,6 +153,8 @@ returning {projection}
136153
}
137154
}
138155

156+
impl GetAllCondition for TestEntityProvider<'_> {}
157+
139158
fn init_database() -> SqliteConnection {
140159
let connection = Connection::open_thread_safe(":memory:").unwrap();
141160
connection
@@ -231,6 +250,7 @@ returning {projection}
231250
);
232251
assert!(cursor.next().is_none());
233252
}
253+
234254
#[test]
235255
fn test_upsertion() {
236256
let connection = init_database();
@@ -258,4 +278,30 @@ returning {projection}
258278
);
259279
assert!(cursor.next().is_none());
260280
}
281+
282+
#[test]
283+
pub fn test_blanket_get_all() {
284+
let connection = init_database();
285+
let provider = TestEntityProvider::new(&connection);
286+
let cursor = provider.get_all().unwrap();
287+
let entities: Vec<TestEntity> = cursor.collect();
288+
289+
assert_eq!(
290+
vec![
291+
TestEntity {
292+
text_data: "row 1".to_string(),
293+
real_data: 1.23,
294+
integer_data: -52,
295+
maybe_null: None
296+
},
297+
TestEntity {
298+
text_data: "row 2".to_string(),
299+
real_data: 2.34,
300+
integer_data: 1789,
301+
maybe_null: Some(0)
302+
}
303+
],
304+
entities
305+
);
306+
}
261307
}

mithril-aggregator/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "mithril-aggregator"
3-
version = "0.4.62"
3+
version = "0.4.63"
44
description = "A Mithril Aggregator server"
55
authors = { workspace = true }
66
edition = { workspace = true }

mithril-aggregator/src/database/cardano_transaction_migration.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,19 @@ vacuum;
5757
4,
5858
r#"
5959
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+
);
6073
"#,
6174
),
6275
]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use mithril_persistence::sqlite::{Provider, SourceAlias, SqLiteEntity, SqliteConnection};
2+
3+
use crate::database::record::BlockRangeRootRecord;
4+
5+
/// Simple queries to retrieve [BlockRangeRootRecord] from the sqlite database.
6+
pub struct GetBlockRangeRootProvider<'client> {
7+
connection: &'client SqliteConnection,
8+
}
9+
10+
impl<'client> GetBlockRangeRootProvider<'client> {
11+
#[cfg(test)]
12+
/// Create a new instance
13+
pub fn new(connection: &'client SqliteConnection) -> Self {
14+
Self { connection }
15+
}
16+
}
17+
18+
#[cfg(test)]
19+
impl mithril_persistence::sqlite::GetAllCondition for GetBlockRangeRootProvider<'_> {}
20+
21+
impl<'client> Provider<'client> for GetBlockRangeRootProvider<'client> {
22+
type Entity = BlockRangeRootRecord;
23+
24+
fn get_connection(&'client self) -> &'client SqliteConnection {
25+
self.connection
26+
}
27+
28+
fn get_definition(&self, condition: &str) -> String {
29+
let aliases = SourceAlias::new(&[("{:block_range_root:}", "block_range_root")]);
30+
let projection = Self::Entity::get_projection().expand(aliases);
31+
32+
format!("select {projection} from block_range_root where {condition} order by start, end")
33+
}
34+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use mithril_persistence::sqlite::{
2+
Provider, SourceAlias, SqLiteEntity, SqliteConnection, WhereCondition,
3+
};
4+
5+
use crate::database::record::IntervalWithoutBlockRangeRootRecord;
6+
7+
/// Query that return the interval of block numbers that does not have a block range root.
8+
pub struct GetIntervalWithoutBlockRangeRootProvider<'client> {
9+
connection: &'client SqliteConnection,
10+
}
11+
12+
impl<'client> GetIntervalWithoutBlockRangeRootProvider<'client> {
13+
/// Create a new instance
14+
pub fn new(connection: &'client SqliteConnection) -> Self {
15+
Self { connection }
16+
}
17+
18+
pub fn get_interval_without_block_range_condition(&self) -> WhereCondition {
19+
WhereCondition::default()
20+
}
21+
}
22+
23+
impl<'client> Provider<'client> for GetIntervalWithoutBlockRangeRootProvider<'client> {
24+
type Entity = IntervalWithoutBlockRangeRootRecord;
25+
26+
fn get_connection(&'client self) -> &'client SqliteConnection {
27+
self.connection
28+
}
29+
30+
fn get_definition(&self, condition: &str) -> String {
31+
let aliases = SourceAlias::new(&[
32+
("{:interval_start:}", "interval_start"),
33+
("{:interval_end:}", "interval_end"),
34+
]);
35+
let projection = Self::Entity::get_projection().expand(aliases);
36+
37+
format!(
38+
"select {projection} from \
39+
(select max(end) as start from block_range_root where {condition}) as interval_start, \
40+
(select max(block_number) as end from cardano_tx where {condition}) as interval_end"
41+
)
42+
}
43+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use std::iter::repeat;
2+
3+
use sqlite::Value;
4+
5+
use mithril_common::StdResult;
6+
use mithril_persistence::sqlite::{
7+
Provider, SourceAlias, SqLiteEntity, SqliteConnection, WhereCondition,
8+
};
9+
10+
use crate::database::record::BlockRangeRootRecord;
11+
12+
/// Query to insert [BlockRangeRootRecord] in the sqlite database
13+
pub struct InsertBlockRangeRootProvider<'client> {
14+
connection: &'client SqliteConnection,
15+
}
16+
17+
impl<'client> InsertBlockRangeRootProvider<'client> {
18+
/// Create a new instance
19+
pub fn new(connection: &'client SqliteConnection) -> Self {
20+
Self { connection }
21+
}
22+
23+
/// Condition to insert multiples records.
24+
pub fn get_insert_many_condition(
25+
&self,
26+
block_range_records: Vec<BlockRangeRootRecord>,
27+
) -> StdResult<WhereCondition> {
28+
let columns = "(start, end, merkle_root)";
29+
let values_columns: Vec<&str> = repeat("(?*, ?*, ?*)")
30+
.take(block_range_records.len())
31+
.collect();
32+
33+
let values: StdResult<Vec<Value>> =
34+
block_range_records
35+
.into_iter()
36+
.try_fold(vec![], |mut vec, record| {
37+
vec.append(&mut vec![
38+
Value::Integer(record.range.start.try_into()?),
39+
Value::Integer(record.range.end.try_into()?),
40+
Value::String(record.merkle_root.to_hex()),
41+
]);
42+
Ok(vec)
43+
});
44+
45+
Ok(WhereCondition::new(
46+
format!("{columns} values {}", values_columns.join(", ")).as_str(),
47+
values?,
48+
))
49+
}
50+
}
51+
52+
impl<'client> Provider<'client> for InsertBlockRangeRootProvider<'client> {
53+
type Entity = BlockRangeRootRecord;
54+
55+
fn get_connection(&'client self) -> &'client SqliteConnection {
56+
self.connection
57+
}
58+
59+
fn get_definition(&self, condition: &str) -> String {
60+
let aliases = SourceAlias::new(&[("{:block_range_root:}", "block_range_root")]);
61+
let projection = Self::Entity::get_projection().expand(aliases);
62+
63+
format!("insert or ignore into block_range_root {condition} returning {projection}")
64+
}
65+
}

0 commit comments

Comments
 (0)