aboutsummaryrefslogtreecommitdiffstats
path: root/src/state/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/mod.rs')
-rw-r--r--src/state/mod.rs30
1 files changed, 25 insertions, 5 deletions
diff --git a/src/state/mod.rs b/src/state/mod.rs
index 671aff7..ef1559d 100644
--- a/src/state/mod.rs
+++ b/src/state/mod.rs
@@ -38,6 +38,7 @@ use tracing::{debug, error, warn};
use crate::document::lock::DocumentLock;
use crate::document::replica::{Epoch, Stamp};
+use crate::document::signatures::Sign;
use crate::document::store::{ChangeSet, StoreError};
use crate::document::{Document, Transaction};
use crate::locks::{MutexExt, RwLockExt};
@@ -143,7 +144,8 @@ pub(crate) struct AppState {
pub(crate) protocol_writers: Arc<std::collections::HashSet<String>>,
/// The swarm's first users, recorded by a genesis node (`NodeConfig::first_users`).
pub(crate) first_users: Arc<crate::access::registry::FirstUsers>,
- /// Signs the tokens this node issues (`NodeConfig::node_key`).
+ /// Signs the tokens this node issues and the changes it commits
+ /// (`NodeConfig::node_key`).
pub(crate) node_key: Option<Arc<crate::identity::NodeKey>>,
pub(crate) token_lifetime: Duration,
pub(crate) session_lifetime: Duration,
@@ -452,7 +454,7 @@ impl AppState {
/// cancelled request can't leave a committed write unannounced. While a rejoin runs,
/// the write goes into the document before it (`StaleView`), replayed when the
/// rejoin ends; on a node that keeps none, it is refused (`StoreError::Rejoining`,
- /// `f` not called).
+ /// `f` not called). Protocol data: the change is signed (`document::signatures`).
pub(crate) async fn mutate_doc<T, E>(
&self,
f: impl FnOnce(&mut Transaction<'_>) -> Result<T, E> + Send + 'static,
@@ -461,15 +463,19 @@ impl AppState {
T: Send + 'static,
E: From<StoreError> + Send + 'static,
{
- self.mutate_doc_for(None, f).await.map(|(out, _)| out)
+ self.mutate_doc_for(None, Sign::Protocol, f)
+ .await
+ .map(|(out, _)| out)
}
/// [`mutate_doc`](Self::mutate_doc) for `user`, who wrote through the API: the
- /// change names them (`replica::Writer`). With the heads of the document written to
- /// after the write (sorted): what a read must have to include it.
+ /// change names them (`replica::Writer`), and is signed as `sign` says, with this
+ /// node's key. With the heads of the document written to after the
+ /// write (sorted): what a read must have to include it.
pub(crate) async fn mutate_doc_for<T, E>(
&self,
user: Option<String>,
+ sign: Sign,
f: impl FnOnce(&mut Transaction<'_>) -> Result<T, E> + Send + 'static,
) -> Result<(T, Vec<crate::document::ChangeHash>), E>
where
@@ -511,7 +517,21 @@ impl AppState {
node: state.local_id(),
user: user.as_deref(),
};
+ let before = replica.doc.get_heads();
let result = replica.transact(state.clock.now_millis(), &writer, f);
+ if result.is_ok()
+ && let Some(key) = &state.node_key
+ {
+ // The change made, if any: the one head after it, as it builds on
+ // every head before.
+ let after = replica.doc.get_heads();
+ if after != before
+ && let [hash] = after[..]
+ && sign.wanted(&replica.doc)
+ {
+ replica.signatures.sign(hash, key);
+ }
+ }
replica.refresh_heads();
result.map(|out| (out, replica.heads().to_vec()))
}; // lock released before notifying