use diesel::dsl::insert_into;
use diesel::prelude::*;
use crate::error::InternalError;
use crate::state::merkle::sql::{
models::{NewMerkleRadixChangeLogAddition, NewMerkleRadixChangeLogDeletion},
schema::merkle_radix_change_log_addition,
schema::merkle_radix_change_log_deletion,
};
use super::MerkleRadixOperations;
pub trait MerkleRadixUpdateUpdateChangeLogOperation {
fn update_change_log(
&self,
tree_id: i64,
state_root: &str,
parent_state_root: &str,
additions: &[&str],
deletions: &[&str],
) -> Result<(), InternalError>;
}
#[cfg(feature = "sqlite")]
impl<'a> MerkleRadixUpdateUpdateChangeLogOperation for MerkleRadixOperations<'a, SqliteConnection> {
fn update_change_log(
&self,
tree_id: i64,
state_root: &str,
parent_state_root: &str,
additions: &[&str],
deletions: &[&str],
) -> Result<(), InternalError> {
self.conn.transaction::<_, InternalError, _>(|| {
let change_log_additions = additions
.iter()
.map(|hash| NewMerkleRadixChangeLogAddition {
state_root,
tree_id,
parent_state_root: Some(parent_state_root),
addition: hash,
})
.collect::<Vec<_>>();
insert_into(merkle_radix_change_log_addition::table)
.values(change_log_additions)
.execute(self.conn)?;
let change_log_deletions = deletions
.iter()
.map(|hash| NewMerkleRadixChangeLogDeletion {
state_root: parent_state_root,
tree_id,
successor_state_root: state_root,
deletion: hash,
})
.collect::<Vec<_>>();
insert_into(merkle_radix_change_log_deletion::table)
.values(change_log_deletions)
.execute(self.conn)?;
Ok(())
})
}
}
#[cfg(feature = "postgres")]
impl<'a> MerkleRadixUpdateUpdateChangeLogOperation for MerkleRadixOperations<'a, PgConnection> {
fn update_change_log(
&self,
tree_id: i64,
state_root: &str,
parent_state_root: &str,
additions: &[&str],
deletions: &[&str],
) -> Result<(), InternalError> {
self.conn.transaction::<_, InternalError, _>(|| {
let change_log_additions = additions
.iter()
.map(|hash| NewMerkleRadixChangeLogAddition {
state_root,
tree_id,
parent_state_root: Some(parent_state_root),
addition: hash,
})
.collect::<Vec<_>>();
insert_into(merkle_radix_change_log_addition::table)
.values(change_log_additions)
.execute(self.conn)?;
let change_log_deletions = deletions
.iter()
.map(|hash| NewMerkleRadixChangeLogDeletion {
state_root: parent_state_root,
tree_id,
successor_state_root: state_root,
deletion: hash,
})
.collect::<Vec<_>>();
insert_into(merkle_radix_change_log_deletion::table)
.values(change_log_deletions)
.execute(self.conn)?;
Ok(())
})
}
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "state-merkle-sql-postgres-tests")]
use crate::state::merkle::sql::backend::postgres::test::run_postgres_test;
#[cfg(feature = "sqlite")]
use crate::state::merkle::sql::{
migration, models::sqlite, schema::sqlite_merkle_radix_tree_node,
};
#[cfg(feature = "state-merkle-sql-postgres-tests")]
use crate::state::merkle::sql::{models::postgres, schema::postgres_merkle_radix_tree_node};
#[cfg(feature = "sqlite")]
#[test]
fn sqlite_update_change_log() -> Result<(), Box<dyn std::error::Error>> {
let conn = SqliteConnection::establish(":memory:")?;
migration::sqlite::run_migrations(&conn)?;
sqlite_insert_state_root_nodes(&conn)?;
MerkleRadixOperations::new(&conn).update_change_log(
1,
"new-state-root",
"initial-state-root",
&[],
&[],
)?;
Ok(())
}
#[cfg(feature = "state-merkle-sql-postgres-tests")]
#[test]
fn postgres_update_change_log() -> Result<(), Box<dyn std::error::Error>> {
run_postgres_test(|url| {
let conn = PgConnection::establish(&url)?;
postgres_insert_state_root_nodes(&conn)?;
MerkleRadixOperations::new(&conn).update_change_log(
1,
"new-state-root",
"initial-state-root",
&[],
&[],
)?;
Ok(())
})
}
#[cfg(feature = "sqlite")]
fn sqlite_insert_state_root_nodes(
conn: &SqliteConnection,
) -> Result<(), Box<dyn std::error::Error>> {
insert_into(sqlite_merkle_radix_tree_node::table)
.values(vec![
sqlite::MerkleRadixTreeNode {
hash: "initial-state-root".into(),
tree_id: 1,
leaf_id: None,
children: sqlite::Children(vec![]),
},
sqlite::MerkleRadixTreeNode {
hash: "new-state-root".into(),
tree_id: 1,
leaf_id: None,
children: sqlite::Children(vec![]),
},
sqlite::MerkleRadixTreeNode {
hash: "first-state-root".into(),
tree_id: 1,
leaf_id: None,
children: sqlite::Children(vec![]),
},
sqlite::MerkleRadixTreeNode {
hash: "second-state-root".into(),
tree_id: 1,
leaf_id: None,
children: sqlite::Children(vec![]),
},
])
.execute(conn)?;
Ok(())
}
#[cfg(feature = "state-merkle-sql-postgres-tests")]
fn postgres_insert_state_root_nodes(
conn: &PgConnection,
) -> Result<(), Box<dyn std::error::Error>> {
insert_into(postgres_merkle_radix_tree_node::table)
.values(vec![
postgres::MerkleRadixTreeNode {
hash: "initial-state-root".into(),
tree_id: 1,
leaf_id: None,
children: vec![],
},
postgres::MerkleRadixTreeNode {
hash: "new-state-root".into(),
tree_id: 1,
leaf_id: None,
children: vec![],
},
postgres::MerkleRadixTreeNode {
hash: "first-state-root".into(),
tree_id: 1,
leaf_id: None,
children: vec![],
},
postgres::MerkleRadixTreeNode {
hash: "second-state-root".into(),
tree_id: 1,
leaf_id: None,
children: vec![],
},
])
.execute(conn)?;
Ok(())
}
}