-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce a API to migrate a lock file
- Loading branch information
1 parent
ce3c5eb
commit 638db8f
Showing
7 changed files
with
256 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
use anyhow::{bail, Result}; | ||
|
||
use crate::api::{migrate, LockFile}; | ||
use crate::bus::{ServiceMessage, ServiceSender}; | ||
use crate::context::Context; | ||
|
||
/// Interface to interact with the node in a programmatic, "low-level" way. | ||
#[derive(Debug)] | ||
pub struct NodeInterface { | ||
context: Context, | ||
tx: ServiceSender, | ||
} | ||
|
||
impl NodeInterface { | ||
pub fn new(context: Context, tx: ServiceSender) -> Self { | ||
Self { context, tx } | ||
} | ||
|
||
pub async fn migrate(&self, lock_file: LockFile) -> Result<bool> { | ||
let committed_operations = migrate( | ||
&self.context.store, | ||
&self.context.schema_provider, | ||
lock_file, | ||
) | ||
.await?; | ||
|
||
let did_migration_happen = !committed_operations.is_empty(); | ||
|
||
// Send new operations from migration on service communication bus, this will arrive | ||
// eventually at the materializer service | ||
for operation_id in committed_operations { | ||
if self | ||
.tx | ||
.send(ServiceMessage::NewOperation(operation_id)) | ||
.is_err() | ||
{ | ||
bail!("Failed to inform materialization service about migration"); | ||
} | ||
} | ||
|
||
Ok(did_migration_happen) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
use anyhow::Result; | ||
use p2panda_rs::entry::EncodedEntry; | ||
use p2panda_rs::hash::Hash; | ||
use p2panda_rs::operation::EncodedOperation; | ||
use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||
|
||
/// Serializable format holding encoded and signed p2panda operations and entries. | ||
/// | ||
/// ```toml | ||
/// version = 1 | ||
/// | ||
/// [[commits]] | ||
/// entry_hash = "..." | ||
/// entry = "..." | ||
/// operation = "..." | ||
/// | ||
/// [[commits]] | ||
/// entry_hash = "..." | ||
/// entry = "..." | ||
/// operation = "..." | ||
/// | ||
/// # ... | ||
/// ``` | ||
#[derive(Debug, Serialize, Deserialize)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct LockFile { | ||
/// Version of this lock file. | ||
pub version: LockFileVersion, | ||
|
||
/// List of commits holding the signed operation and entry data. | ||
pub commits: Option<Vec<Commit>>, | ||
} | ||
|
||
/// Known versions of lock file format. | ||
#[derive(Debug, Clone)] | ||
pub enum LockFileVersion { | ||
V1, | ||
} | ||
|
||
impl LockFileVersion { | ||
/// Returns the operation version encoded as u64. | ||
pub fn as_u64(&self) -> u64 { | ||
match self { | ||
LockFileVersion::V1 => 1, | ||
} | ||
} | ||
} | ||
|
||
impl Serialize for LockFileVersion { | ||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
where | ||
S: Serializer, | ||
{ | ||
serializer.serialize_u64(self.as_u64()) | ||
} | ||
} | ||
|
||
impl<'de> Deserialize<'de> for LockFileVersion { | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
let version = u64::deserialize(deserializer)?; | ||
|
||
match version { | ||
1 => Ok(LockFileVersion::V1), | ||
_ => Err(serde::de::Error::custom(format!( | ||
"unsupported lock file version {}", | ||
version | ||
))), | ||
} | ||
} | ||
} | ||
|
||
/// Single commit with encoded entry and operation pair. | ||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
#[serde(deny_unknown_fields)] | ||
pub struct Commit { | ||
/// Hash of the entry. | ||
pub entry_hash: Hash, | ||
|
||
/// Encoded and signed p2panda entry. | ||
pub entry: EncodedEntry, | ||
|
||
/// Encoded p2panda operation. | ||
pub operation: EncodedOperation, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
use anyhow::{anyhow, bail, Context as AnyhowContext, Result}; | ||
use p2panda_rs::api::publish; | ||
use p2panda_rs::entry::decode::decode_entry; | ||
use p2panda_rs::entry::traits::{AsEncodedEntry, AsEntry}; | ||
use p2panda_rs::operation::decode::decode_operation; | ||
use p2panda_rs::operation::traits::Schematic; | ||
use p2panda_rs::operation::OperationId; | ||
use p2panda_rs::storage_provider::traits::{EntryStore, LogStore, OperationStore}; | ||
|
||
use crate::api::LockFile; | ||
use crate::schema::SchemaProvider; | ||
|
||
/// Utility method to publish multiple operations and entries in the node database. | ||
/// | ||
/// Returns a list of operation ids which have been committed during this migration process. Can be | ||
/// empty if no migration was required. | ||
pub async fn migrate<S>( | ||
store: &S, | ||
schema_provider: &SchemaProvider, | ||
lock_file: LockFile, | ||
) -> Result<Vec<OperationId>> | ||
where | ||
S: OperationStore + EntryStore + LogStore, | ||
{ | ||
let mut committed_operations = Vec::new(); | ||
let commits = lock_file.commits.unwrap_or(Vec::new()); | ||
|
||
for commit in commits { | ||
let planned_entry = decode_entry(&commit.entry) | ||
.context("Invalid entry encoding encountered in lock file")?; | ||
|
||
let existing_entry = store | ||
.get_entry_at_seq_num( | ||
planned_entry.public_key(), | ||
planned_entry.log_id(), | ||
planned_entry.seq_num(), | ||
) | ||
.await | ||
.context("Internal database error occurred while retrieving entry")?; | ||
|
||
// Check if node already knows about this commit | ||
match existing_entry { | ||
Some(existing_entry) => { | ||
// Check its integrity with our lock file by comparing entry hashes | ||
if existing_entry.hash() != commit.entry_hash { | ||
bail!("Integrity check failed when comparing planned and existing commit") | ||
} | ||
} | ||
None => { | ||
// .. otherwise publish the planned commit! | ||
let plain_operation = decode_operation(&commit.operation) | ||
.context("Invalid operation encoding encountered in lock file")?; | ||
|
||
let schema = schema_provider | ||
.get(plain_operation.schema_id()) | ||
.await | ||
.ok_or_else(|| anyhow!("Could not migrate commit with unknown schema id"))?; | ||
|
||
publish( | ||
store, | ||
&schema, | ||
&commit.entry, | ||
&plain_operation, | ||
&commit.operation, | ||
) | ||
.await | ||
.context("Internal database error occurred while publishing migration commit")?; | ||
|
||
committed_operations.push(commit.entry_hash.into()); | ||
} | ||
}; | ||
} | ||
|
||
Ok(committed_operations) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
mod api; | ||
mod lock_file; | ||
mod migration; | ||
|
||
pub use api::NodeInterface; | ||
pub use lock_file::LockFile; | ||
pub use migration::migrate; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters