Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Sassafras Protocol v0.3.1 #12713

Merged
merged 5 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions bin/node-sassafras/node/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "node-sassafras"
version = "0.3.0"
version = "0.3.1-dev"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Node testbed for Sassafras consensus."
homepage = "https://substrate.io/"
Expand All @@ -26,8 +26,8 @@ sc-telemetry = { version = "4.0.0-dev", path = "../../../client/telemetry" }
sc-keystore = { version = "4.0.0-dev", path = "../../../client/keystore" }
sc-transaction-pool = { version = "4.0.0-dev", path = "../../../client/transaction-pool" }
sc-transaction-pool-api = { version = "4.0.0-dev", path = "../../../client/transaction-pool/api" }
sc-consensus-sassafras = { version = "0.3.0", path = "../../../client/consensus/sassafras" }
sp-consensus-sassafras = { version = "0.3.0", path = "../../../primitives/consensus/sassafras" }
sc-consensus-sassafras = { version = "0.3.1-dev", path = "../../../client/consensus/sassafras" }
sp-consensus-sassafras = { version = "0.3.1-dev", path = "../../../primitives/consensus/sassafras" }
sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" }
sc-consensus = { version = "0.10.0-dev", path = "../../../client/consensus/common" }
sc-finality-grandpa = { version = "0.10.0-dev", path = "../../../client/finality-grandpa" }
Expand Down Expand Up @@ -56,7 +56,7 @@ frame-benchmarking = { version = "4.0.0-dev", path = "../../../frame/benchmarkin
frame-benchmarking-cli = { version = "4.0.0-dev", path = "../../../utils/frame/benchmarking-cli" }

# Local Dependencies
node-sassafras-runtime = { version = "0.3.0", path = "../runtime" }
node-sassafras-runtime = { version = "0.3.1-dev", path = "../runtime" }

# CLI-specific dependencies
try-runtime-cli = { version = "0.10.0-dev", optional = true, path = "../../../utils/frame/try-runtime/cli" }
Expand Down
6 changes: 3 additions & 3 deletions bin/node-sassafras/runtime/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "node-sassafras-runtime"
version = "0.3.0"
version = "0.3.1-dev"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Runtime testbed for Sassafras consensus."
homepage = "https://substrate.io/"
Expand All @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"]
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
scale-info = { version = "2.1.1", default-features = false, features = ["derive"] }

pallet-sassafras = { version = "0.3.0", default-features = false, path = "../../../frame/sassafras" }
pallet-sassafras = { version = "0.3.1-dev", default-features = false, path = "../../../frame/sassafras" }
pallet-balances = { version = "4.0.0-dev", default-features = false, path = "../../../frame/balances" }
pallet-session = { version = "4.0.0-dev", default-features = false, path = "../../../frame/session" }
frame-support = { version = "4.0.0-dev", default-features = false, path = "../../../frame/support" }
Expand All @@ -28,7 +28,7 @@ pallet-transaction-payment = { version = "4.0.0-dev", default-features = false,
frame-executive = { version = "4.0.0-dev", default-features = false, path = "../../../frame/executive" }
sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" }
sp-block-builder = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/block-builder"}
sp-consensus-sassafras = { version = "0.3.0", default-features = false, path = "../../../primitives/consensus/sassafras" }
sp-consensus-sassafras = { version = "0.3.1-dev", default-features = false, path = "../../../primitives/consensus/sassafras" }
sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" }
sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/inherents"}
sp-offchain = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/offchain" }
Expand Down
18 changes: 8 additions & 10 deletions bin/node-sassafras/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,16 +379,6 @@ impl_runtime_apis! {
}

impl sp_consensus_sassafras::SassafrasApi<Block> for Runtime {
fn configuration() -> sp_consensus_sassafras::SassafrasConfiguration {
sp_consensus_sassafras::SassafrasConfiguration {
slot_duration: SLOT_DURATION_IN_MILLISECONDS,
epoch_duration: EPOCH_DURATION_IN_SLOTS,
authorities: Sassafras::authorities().to_vec(),
randomness: Sassafras::randomness(),
threshold_params: Sassafras::config(),
}
}

fn submit_tickets_unsigned_extrinsic(
tickets: Vec<sp_consensus_sassafras::Ticket>
) -> bool {
Expand All @@ -399,6 +389,14 @@ impl_runtime_apis! {
Sassafras::slot_ticket(slot)
}

fn current_epoch() -> sp_consensus_sassafras::Epoch {
Sassafras::current_epoch()
}

fn next_epoch() -> sp_consensus_sassafras::Epoch {
Sassafras::next_epoch()
}

fn generate_key_ownership_proof(
_slot: sp_consensus_sassafras::Slot,
_authority_id: sp_consensus_sassafras::AuthorityId,
Expand Down
4 changes: 2 additions & 2 deletions client/consensus/sassafras/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sc-consensus-sassafras"
version = "0.3.0"
version = "0.3.1-dev"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Sassafras consensus algorithm for substrate"
edition = "2021"
Expand Down Expand Up @@ -33,7 +33,7 @@ sp-application-crypto = { version = "7.0.0", path = "../../../primitives/applica
sp-block-builder = { version = "4.0.0-dev", path = "../../../primitives/block-builder" }
sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" }
sp-consensus = { version = "0.10.0-dev", path = "../../../primitives/consensus/common" }
sp-consensus-sassafras = { version = "0.3.0", path = "../../../primitives/consensus/sassafras" }
sp-consensus-sassafras = { version = "0.3.1-dev", path = "../../../primitives/consensus/sassafras" }
sp-consensus-slots = { version = "0.10.0-dev", path = "../../../primitives/consensus/slots" }
sp-consensus-vrf = { version = "0.10.0-dev", path = "../../../primitives/consensus/vrf" }
sp-core = { version = "7.0.0", path = "../../../primitives/core" }
Expand Down
14 changes: 10 additions & 4 deletions client/consensus/sassafras/src/authorship.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ pub(crate) fn claim_slot(
let (authority_idx, ticket_aux) = match ticket {
Some(ticket) => {
log::debug!(target: "sassafras", "🌳 [TRY PRIMARY]");
let (authority_idx, ticket_aux) = epoch.tickets_aux.get(&ticket)?.clone();
let (authority_idx, ticket_aux) = epoch.tickets_aux.get(&ticket.output)?.clone();
log::debug!(target: "sassafras", "🌳 Ticket = [ticket: {:02x?}, auth: {}, attempt: {}]",
&ticket.as_bytes()[0..8], authority_idx, ticket_aux.attempt);
&ticket.output.as_bytes()[0..8], authority_idx, ticket_aux.attempt);
(authority_idx, Some(ticket_aux))
},
None => {
Expand Down Expand Up @@ -128,7 +128,11 @@ fn generate_epoch_tickets(epoch: &mut Epoch, keystore: &SyncCryptoStorePtr) -> V
)
.ok()??;

let ticket = VRFOutput(signature.output);
let ticket = Ticket {
output: VRFOutput(signature.output),
// TODO-SASS-P3
proof: VRFProof::try_from([0; 64]).expect("FIXME"),
};
if !sp_consensus_sassafras::check_threshold(&ticket, threshold) {
return None
}
Expand All @@ -141,8 +145,10 @@ fn generate_epoch_tickets(epoch: &mut Epoch, keystore: &SyncCryptoStorePtr) -> V

for attempt in 0..max_attempts {
if let Some((ticket, ticket_aux)) = make_ticket(attempt) {
epoch
.tickets_aux
.insert(ticket.output, (authority_idx as AuthorityIndex, ticket_aux));
tickets.push(ticket);
epoch.tickets_aux.insert(ticket, (authority_idx as AuthorityIndex, ticket_aux));
}
}
}
Expand Down
88 changes: 88 additions & 0 deletions client/consensus/sassafras/src/block_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

use super::*;
use sc_client_api::{AuxDataOperations, FinalityNotification, PreCommitActions};
use sp_blockchain::BlockStatus;

/// Block-import handler for Sassafras.
///
Expand Down Expand Up @@ -230,6 +231,77 @@ where
}
}

impl<Block, Client, Inner> SassafrasBlockImport<Block, Client, Inner>
where
Block: BlockT,
Inner: BlockImport<Block, Transaction = sp_api::TransactionFor<Client, Block>> + Send + Sync,
Inner::Error: Into<ConsensusError>,
Client: HeaderBackend<Block>
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
+ AuxStore
+ ProvideRuntimeApi<Block>
+ Send
+ Sync,
Client::Api: SassafrasApi<Block> + ApiExt<Block>,
{
/// Import whole state after a warp sync.
///
/// This function makes multiple transactions to the DB. If one of them fails we may
/// end up in an inconsistent state and have to resync
async fn import_state(
&mut self,
mut block: BlockImportParams<Block, sp_api::TransactionFor<Client, Block>>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, ConsensusError> {
let hash = block.post_hash();
let parent_hash = *block.header.parent_hash();
let number = *block.header.number();

// Check for the unit tag.
block.remove_intermediate::<()>(INTERMEDIATE_KEY)?;

// Import as best
block.fork_choice = Some(ForkChoiceStrategy::Custom(true));

// Reset block weight
aux_schema::write_block_weight(hash, 0, |values| {
block
.auxiliary
.extend(values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))))
});

// First make the client import the state
let aux = match self.inner.import_block(block, new_cache).await {
Ok(ImportResult::Imported(aux)) => aux,
Ok(r) =>
return Err(ConsensusError::ClientImport(format!(
"Unexpected import result: {:?}",
r
))),
Err(e) => return Err(e.into()),
};

// Read epoch info from the imported state
let block_id = BlockId::Hash(hash);
let curr_epoch = self.client.runtime_api().current_epoch(&block_id).map_err(|e| {
ConsensusError::ClientImport(sassafras_err::<Block>(Error::RuntimeApi(e)).into())
})?;
let next_epoch = self.client.runtime_api().next_epoch(&block_id).map_err(|e| {
ConsensusError::ClientImport(sassafras_err::<Block>(Error::RuntimeApi(e)).into())
})?;

let mut epoch_changes = self.epoch_changes.shared_data();
epoch_changes.reset(parent_hash, hash, number, curr_epoch.into(), next_epoch.into());

aux_schema::write_epoch_changes::<Block, _, _>(&*epoch_changes, |insert| {
self.client.insert_aux(insert, [])
})
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;

Ok(ImportResult::Imported(aux))
}
}

#[async_trait::async_trait]
impl<Block, Client, Inner> BlockImport<Block> for SassafrasBlockImport<Block, Client, Inner>
where
Expand All @@ -255,6 +327,22 @@ where
let hash = block.post_hash();
let number = *block.header.number();

// Early exit if block already in chain, otherwise the check for epoch changes
// will error when trying to re-import
match self.client.status(BlockId::Hash(hash)) {
Ok(BlockStatus::InChain) => {
block.remove_intermediate::<SassafrasIntermediate<Block>>(INTERMEDIATE_KEY)?;
block.fork_choice = Some(ForkChoiceStrategy::Custom(false));
return self.inner.import_block(block, new_cache).await.map_err(Into::into)
},
Ok(BlockStatus::Unknown) => {},
Err(e) => return Err(ConsensusError::ClientImport(e.to_string())),
}

if block.with_state() {
return self.import_state(block, new_cache).await
}

let viable_epoch_desc = block
.remove_intermediate::<SassafrasIntermediate<Block>>(INTERMEDIATE_KEY)?
.epoch_descriptor;
Expand Down
21 changes: 16 additions & 5 deletions client/consensus/sassafras/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,17 +184,28 @@ fn sassafras_err<B: BlockT>(error: Error<B>) -> Error<B> {
error
}

/// Sassafras epoch information
/// Sassafras epoch information augmented with private tickets information.
#[derive(Encode, Decode, PartialEq, Eq, Clone, Debug)]
pub struct Epoch {
/// The epoch index.
pub epoch_idx: u64,
/// The starting slot of the epoch.
pub start_slot: Slot,
/// Epoch configuration
/// Epoch configuration.
pub config: SassafrasConfiguration,
/// Tickets auxiliary data.
pub tickets_aux: BTreeMap<Ticket, (AuthorityIndex, TicketAux)>,
pub tickets_aux: BTreeMap<VRFOutput, (AuthorityIndex, TicketAux)>,
}

impl From<sp_consensus_sassafras::Epoch> for Epoch {
fn from(epoch: sp_consensus_sassafras::Epoch) -> Self {
Epoch {
epoch_idx: epoch.epoch_idx,
start_slot: epoch.start_slot,
config: epoch.config,
tickets_aux: BTreeMap::new(),
}
}
}

impl EpochT for Epoch {
Expand Down Expand Up @@ -252,8 +263,8 @@ where
info.genesis_hash
});

let config = client.runtime_api().configuration(&BlockId::Hash(hash))?;
Ok(config)
let epoch = client.runtime_api().current_epoch(&BlockId::Hash(hash))?;
Ok(epoch.config)
}

/// Intermediate value passed to block importer from authoring or validation logic.
Expand Down
Loading