From 3317fee0a176663508473ef64cf7dadcc56349f1 Mon Sep 17 00:00:00 2001
From: Robert Habermeier <rphmeier@gmail.com>
Date: Mon, 29 Oct 2018 02:05:47 +0100
Subject: [PATCH] write authority set to DB

---
 .../core/finality-grandpa/src/authorities.rs  |  13 +--
 substrate/core/finality-grandpa/src/lib.rs    | 103 ++++++++++++------
 2 files changed, 70 insertions(+), 46 deletions(-)

diff --git a/substrate/core/finality-grandpa/src/authorities.rs b/substrate/core/finality-grandpa/src/authorities.rs
index dd6a433995e..7896c084950 100644
--- a/substrate/core/finality-grandpa/src/authorities.rs
+++ b/substrate/core/finality-grandpa/src/authorities.rs
@@ -47,16 +47,9 @@ impl<H, N> SharedAuthoritySet<H, N> {
 		}
 	}
 
-	/// Execute some work using the inner authority set.
-	pub(crate) fn with<F, U>(&self, f: F) -> U
-		where F: FnOnce(&AuthoritySet<H, N>) -> U
-	{
-		f(&*self.inner.read())
-	}
-
-	/// Execute a closure with the inner set mutably.
-	pub(crate) fn with_mut<F, U>(&self, f: F) -> U where F: FnOnce(&mut AuthoritySet<H, N>) -> U {
-		f(&mut *self.inner.write())
+	/// Acquire a reference to the inner read-write lock.
+	pub(crate) fn inner(&self) -> &RwLock<AuthoritySet<H, N>> {
+		&*self.inner
 	}
 }
 
diff --git a/substrate/core/finality-grandpa/src/lib.rs b/substrate/core/finality-grandpa/src/lib.rs
index dd5f97f264e..58de2553b3f 100644
--- a/substrate/core/finality-grandpa/src/lib.rs
+++ b/substrate/core/finality-grandpa/src/lib.rs
@@ -620,36 +620,57 @@ impl<B, E, Block: BlockT, N> voter::Environment<Block::Hash, NumberFor<Block>> f
 			return Ok(());
 		}
 
-		self.authority_set.with_mut(|authority_set| {
-			let client = &self.inner;
-			let status = authority_set.apply_changes(number, |canon_number| {
-				client.block_hash_from_id(&BlockId::number(canon_number))
-					.map(|h| h.expect("given number always less than newly-finalized number; \
-						thus there is a block with that number finalized already; qed"))
-			})?;
-
-			if status.changed {
-				// TODO [now]: write to disk. if it fails, exit the node.
-				// write `authorities.encode()`
-
-				if let Some((ref canon_hash, ref canon_number)) = status.new_set_block {
-					// write `LastFinalized` entry with `RoundState::genesis(canon)`.
-				}
-			}
+		let mut authority_set = self.authority_set.inner().write();
+		let client = &self.inner;
+		let status = authority_set.apply_changes(number, |canon_number| {
+			client.block_hash_from_id(&BlockId::number(canon_number))
+				.map(|h| h.expect("given number always less than newly-finalized number; \
+					thus there is a block with that number finalized already; qed"))
+		})?;
+
+		if status.changed {
+			// write new authority set state to disk.
+			let encoded_set = authority_set.encode();
+
+			let write_result = if let Some((ref canon_hash, ref canon_number)) = status.new_set_block {
+				// we also overwrite the "last completed round" entry with a blank slate
+				// because from the perspective of the finality gadget, the chain has
+				// reset.
+				let round_state = RoundState::genesis((*canon_hash, *canon_number));
+				let last_completed: LastCompleted<_, _> = (0, round_state);
+				let encoded = last_completed.encode();
+
+				client.backend().insert_aux(
+					&[
+						(AUTHORITY_SET_KEY, &encoded_set[..]),
+						(LAST_COMPLETED_KEY, &encoded[..]),
+					],
+					&[]
+				)
+			} else {
+				client.backend().insert_aux(&[(AUTHORITY_SET_KEY, &encoded_set[..])], &[])
+			};
+
+			if let Err(e) = write_result {
+				warn!(target: "finality", "Failed to write updated authority set to disk. Bailing.");
+				warn!(target: "finality", "Node is in a potentially inconsistent state.");
 
-			if let Some((canon_hash, canon_number)) = status.new_set_block {
-				// the authority set has changed.
-				let (new_id, set_ref) = authority_set.current();
-				return Err(ExitOrError::AuthoritiesChanged(NewAuthoritySet {
-					canon_hash,
-					canon_number,
-					set_id: new_id,
-					authorities: set_ref.to_vec(),
-				}));
+				return Err(e.into());
 			}
+		}
 
+		if let Some((canon_hash, canon_number)) = status.new_set_block {
+			// the authority set has changed.
+			let (new_id, set_ref) = authority_set.current();
+			Err(ExitOrError::AuthoritiesChanged(NewAuthoritySet {
+				canon_hash,
+				canon_number,
+				set_id: new_id,
+				authorities: set_ref.to_vec(),
+			}))
+		} else {
 			Ok(())
-		})
+		}
 	}
 
 	fn prevote_equivocation(
@@ -688,7 +709,7 @@ impl<B, E, Block: BlockT> BlockImport<Block> for GrandpaBlockImport<B, E, Block>
 {
 	type Error = ClientError;
 
-	fn import_block(&self, block: ImportBlock<Block>, new_authorities: Option<Vec<AuthorityId>>)
+	fn import_block(&self, mut block: ImportBlock<Block>, new_authorities: Option<Vec<AuthorityId>>)
 		-> Result<ImportResult, Self::Error>
 	{
 		use runtime_primitives::traits::Digest;
@@ -700,22 +721,32 @@ impl<B, E, Block: BlockT> BlockImport<Block> for GrandpaBlockImport<B, E, Block>
 			block.header.digest()
 		)?;
 
-		let maybe_change = maybe_change.map(|change| (
-			block.header.hash(),
-			block.header.number().clone(),
-			change
-		));
+		// when we update the authorities, we need to hold the lock
+		// until the block is written to prevent a race if we need to restore
+		// the old authority set on error.
+		let just_in_case = maybe_change.map(|change| {
+			let hash = block.header.hash();
+			let number = block.header.number().clone();
 
-		let result = self.inner.import_block(block, new_authorities);
-		if let (true, Some((hash, number, change))) = (result.is_ok(), maybe_change) {
-			self.authority_set.add_pending_change(PendingChange {
+			let mut authorities = self.authority_set.inner().write();
+			let old_set = authorities.clone();
+			authorities.add_pending_change(PendingChange {
 				next_authorities: change.next_authorities,
 				finalization_depth: number + change.delay,
 				canon_height: number,
 				canon_hash: hash,
 			});
 
-			// TODO [now]: write to DB, and what to do on failure?
+			block.auxiliary.push((AUTHORITY_SET_KEY.to_vec(), Some(authorities.encode())));
+			(old_set, authorities)
+		});
+
+		let result = self.inner.import_block(block, new_authorities);
+		if let Err(ref e) = result {
+			if let Some((old_set, mut authorities)) = just_in_case {
+				debug!(target: "afg", "Restoring old set after block import error: {:?}", e);
+				*authorities = old_set;
+			}
 		}
 
 		result
-- 
GitLab