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

Commit

Permalink
Sassafras Protocol v0.3.2 (#14139)
Browse files Browse the repository at this point in the history
- ticket structure, production and claiming close to the final form
- experimental integration of `bandersnatch-vrf`
- ticket ownership claiming via ed25519 challenge
  • Loading branch information
davxy authored May 18, 2023
1 parent 9bb209e commit 5e7d6c5
Show file tree
Hide file tree
Showing 35 changed files with 1,653 additions and 346 deletions.
356 changes: 244 additions & 112 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,6 @@ inherits = "release"
lto = "fat"
# https://doc.rust-lang.org/rustc/codegen-options/index.html#codegen-units
codegen-units = 1

#[patch."https://github.com/w3f/ring-vrf"]
# bandersnatch_vrfs = { path = "/mnt/ssd/users/develop/w3f/ring-vrf/bandersnatch_vrfs" }
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.1-dev"
version = "0.3.2-dev"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Node testbed for Sassafras consensus."
homepage = "https://substrate.io/"
Expand All @@ -27,8 +27,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.1-dev", path = "../../../client/consensus/sassafras" }
sp-consensus-sassafras = { version = "0.3.1-dev", path = "../../../primitives/consensus/sassafras" }
sc-consensus-sassafras = { version = "0.3.2-dev", path = "../../../client/consensus/sassafras" }
sp-consensus-sassafras = { version = "0.3.2-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-consensus-grandpa = { version = "0.10.0-dev", path = "../../../client/consensus/grandpa" }
Expand Down Expand Up @@ -57,7 +57,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.1-dev", path = "../runtime" }
node-sassafras-runtime = { version = "0.3.2-dev", path = "../runtime" }

# CLI-specific dependencies
try-runtime-cli = { version = "0.10.0-dev", optional = true, path = "../../../utils/frame/try-runtime/cli" }
Expand Down
2 changes: 1 addition & 1 deletion bin/node-sassafras/node/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use sp_core::{sr25519, Pair, Public};
use sp_runtime::traits::{IdentifyAccount, Verify};

// Genesis constants for Sassafras parameters configuration.
const SASSAFRAS_TICKETS_MAX_ATTEMPTS_NUMBER: u32 = 32;
const SASSAFRAS_TICKETS_MAX_ATTEMPTS_NUMBER: u32 = 16;
const SASSAFRAS_TICKETS_REDUNDANCY_FACTOR: u32 = 1;

/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
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.1-dev"
version = "0.3.2-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.1-dev", default-features = false, path = "../../../frame/sassafras" }
pallet-sassafras = { version = "0.3.2-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.1-dev", default-features = false, path = "../../../primitives/consensus/sassafras" }
sp-consensus-sassafras = { version = "0.3.2-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
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.1-dev"
version = "0.3.2-dev"
authors = ["Parity Technologies <admin@parity.io>"]
description = "Sassafras consensus algorithm for substrate"
edition = "2021"
Expand Down Expand Up @@ -32,7 +32,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.1-dev", path = "../../../primitives/consensus/sassafras" }
sp-consensus-sassafras = { version = "0.3.2-dev", path = "../../../primitives/consensus/sassafras" }
sp-consensus-slots = { version = "0.10.0-dev", path = "../../../primitives/consensus/slots" }
sp-core = { version = "7.0.0", path = "../../../primitives/core" }
sp-inherents = { version = "4.0.0-dev", path = "../../../primitives/inherents" }
Expand Down
61 changes: 33 additions & 28 deletions client/consensus/sassafras/src/authorship.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
use super::*;

use sp_consensus_sassafras::{
digests::PreDigest, ticket_id, ticket_id_threshold, AuthorityId, Slot, TicketClaim, TicketData,
TicketEnvelope, TicketId,
digests::PreDigest, slot_claim_sign_data, ticket_id, ticket_id_threshold, AuthorityId, Slot,
TicketClaim, TicketData, TicketEnvelope, TicketId,
};
use sp_core::{twox_64, ByteArray};
use sp_core::{twox_64, ByteArray, ed25519};

use std::pin::Pin;

Expand All @@ -41,7 +41,7 @@ pub(crate) fn secondary_authority_index(
/// If ticket is `None`, then the slot should be claimed using the fallback mechanism.
pub(crate) fn claim_slot(
slot: Slot,
epoch: &Epoch,
epoch: &mut Epoch,
maybe_ticket: Option<(TicketId, TicketData)>,
keystore: &KeystorePtr,
) -> Option<(PreDigest, AuthorityId)> {
Expand All @@ -51,34 +51,38 @@ pub(crate) fn claim_slot(
return None
}

let mut vrf_sign_data = slot_claim_sign_data(&config.randomness, slot, epoch.epoch_idx);

let (authority_idx, ticket_claim) = match maybe_ticket {
Some((ticket_id, ticket_data)) => {
log::debug!(target: LOG_TARGET, "[TRY PRIMARY]");
let (authority_idx, ticket_secret) = epoch.tickets_aux.get(&ticket_id)?.clone();
log::debug!(target: LOG_TARGET, "[TRY PRIMARY (slot {slot}, tkt = {ticket_id:16x})]");
let (authority_idx, ticket_secret) = epoch.tickets_aux.remove(&ticket_id)?.clone();
log::debug!(
target: LOG_TARGET,
"Ticket = [ticket: {:x?}, auth: {}, attempt: {}]",
ticket_id,
" got ticket: auth: {}, attempt: {}",
authority_idx,
ticket_data.attempt_idx
);
// TODO DAVXY : using ticket_secret
let _ = ticket_secret;
let erased_signature = [0; 64];

vrf_sign_data.push_transcript_data(&ticket_data.encode());

let data = vrf_sign_data.challenge::<32>();
let erased_pair = ed25519::Pair::from_seed(&ticket_secret.erased_secret);
let erased_signature = *erased_pair.sign(&data).as_ref();

let claim = TicketClaim { erased_signature };
(authority_idx, Some(claim))
},
None => {
log::debug!(target: LOG_TARGET, "[TRY SECONDARY]");
log::debug!(target: LOG_TARGET, "[TRY SECONDARY (slot {slot})]");
(secondary_authority_index(slot, config), None)
},
};

let authority_id = config.authorities.get(authority_idx as usize).map(|auth| &auth.0)?;

let vrf_input = slot_claim_vrf_input(&config.randomness, slot, epoch.epoch_idx);
let vrf_signature = keystore
.sr25519_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &vrf_input.into())
.bandersnatch_vrf_sign(AuthorityId::ID, authority_id.as_ref(), &vrf_sign_data)
.ok()
.flatten()?;

Expand All @@ -103,7 +107,8 @@ fn generate_epoch_tickets(epoch: &mut Epoch, keystore: &KeystorePtr) -> Vec<Tick
config.authorities.len() as u32,
);
// TODO-SASS-P4 remove me
log::debug!(target: LOG_TARGET, "Tickets threshold: {:016x}", threshold);
log::debug!(target: LOG_TARGET, "Generating tickets for epoch {} @ slot {}", epoch.epoch_idx, epoch.start_slot);
log::debug!(target: LOG_TARGET, " threshold: {threshold:016x}");

let authorities = config.authorities.iter().enumerate().map(|(index, a)| (index, &a.0));
for (authority_idx, authority_id) in authorities {
Expand All @@ -115,31 +120,32 @@ fn generate_epoch_tickets(epoch: &mut Epoch, keystore: &KeystorePtr) -> Vec<Tick
let vrf_input = ticket_id_vrf_input(&config.randomness, attempt_idx, epoch.epoch_idx);

let vrf_preout = keystore
.sr25519_vrf_output(AuthorityId::ID, authority_id.as_ref(), &vrf_input)
.bandersnatch_vrf_output(AuthorityId::ID, authority_id.as_ref(), &vrf_input)
.ok()??;

let ticket_id = ticket_id(&vrf_input, &vrf_preout);
if ticket_id >= threshold {
return None
}

// TODO DAVXY: compute proper erased_secret/public and revealed_public
let erased_secret = [0; 32];
let erased_public = [0; 32];
let (erased_pair, erased_seed) = ed25519::Pair::generate();

let erased_public: [u8; 32] = *erased_pair.public().as_ref();
let revealed_public = [0; 32];
let data = TicketData { attempt_idx, erased_public, revealed_public };

// TODO DAVXY: placeholder
let ring_proof = ();
let ticket_envelope = TicketEnvelope { data, vrf_preout, ring_proof };

let ticket_secret = TicketSecret { attempt_idx, erased_secret };
let ticket_secret = TicketSecret { attempt_idx, erased_secret: erased_seed };

Some((ticket_envelope, ticket_id, ticket_secret))
};

for attempt in 0..max_attempts {
if let Some((envelope, ticket_id, ticket_secret)) = make_ticket(attempt) {
log::debug!(target: LOG_TARGET, " → {ticket_id:016x}");
epoch
.tickets_aux
.insert(ticket_id, (authority_idx as AuthorityIndex, ticket_secret));
Expand Down Expand Up @@ -220,21 +226,16 @@ where
slot: Slot,
epoch_descriptor: &ViableEpochDescriptor<B::Hash, NumberFor<B>, Epoch>,
) -> Option<Self::Claim> {
debug!(target: LOG_TARGET, "Attempting to claim slot {}", slot);

// Get the next slot ticket from the runtime.
let maybe_ticket =
self.client.runtime_api().slot_ticket(parent_header.hash(), slot).ok()?;

// TODO-SASS-P2: remove me
debug!(target: LOG_TARGET, "parent {}", parent_header.hash());
let mut epoch_changes = self.epoch_changes.shared_data_locked();
let mut epoch = epoch_changes.viable_epoch_mut(epoch_descriptor, |slot| Epoch::genesis(&self.genesis_config, slot))?;

let claim = authorship::claim_slot(
slot,
self.epoch_changes
.shared_data()
.viable_epoch(epoch_descriptor, |slot| Epoch::genesis(&self.genesis_config, slot))?
.as_ref(),
&mut epoch.as_mut(),
maybe_ticket,
&self.keystore,
);
Expand Down Expand Up @@ -282,6 +283,10 @@ where
// TODO DAVXY SASS-32: this seal may be revisited.
// We already have a VRF signature, this could be completelly redundant.
// The header.hash() can be added to the VRF signed data.
// OR maybe we can maintain this seal but compute it using some of the data in the
// pre-digest
// Another option is to not recompute this signature and push (reuse) the one in the
// pre-digest as the seal
let signature = self
.keystore
.sign_with(
Expand Down
13 changes: 6 additions & 7 deletions client/consensus/sassafras/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ use sp_runtime::{
pub use sp_consensus_sassafras::{
digests::{CompatibleDigestItem, ConsensusLog, NextEpochDescriptor, PreDigest},
inherents::SassafrasInherentData,
slot_claim_vrf_input, ticket_id_vrf_input, AuthorityId, AuthorityIndex, AuthorityPair,
AuthoritySignature, SassafrasApi, SassafrasAuthorityWeight, SassafrasConfiguration,
SassafrasEpochConfiguration, TicketClaim, TicketData, TicketEnvelope, TicketId, TicketSecret,
RANDOMNESS_LENGTH, SASSAFRAS_ENGINE_ID,
slot_claim_sign_data, slot_claim_vrf_input, ticket_id_vrf_input, AuthorityId, AuthorityIndex,
AuthorityPair, AuthoritySignature, SassafrasApi, SassafrasAuthorityWeight,
SassafrasConfiguration, SassafrasEpochConfiguration, TicketClaim, TicketData, TicketEnvelope,
TicketId, TicketSecret, RANDOMNESS_LENGTH, SASSAFRAS_ENGINE_ID,
};

mod authorship;
Expand Down Expand Up @@ -284,11 +284,10 @@ fn find_pre_digest<B: BlockT>(header: &B::Header) -> Result<PreDigest, Error<B>>
if header.number().is_zero() {
// Genesis block doesn't contain a pre digest so let's generate a
// dummy one to not break any invariants in the rest of the code
use sp_consensus_sassafras::VrfInput;
use sp_core::crypto::VrfSecret;
let pair = sp_consensus_sassafras::AuthorityPair::from_seed(&[0u8; 32]);
let input = VrfInput::new(b"", &[]);
let vrf_signature = pair.as_ref().vrf_sign(&input.into());
let data = sp_consensus_sassafras::slot_claim_sign_data(&Default::default(), 0.into(), 0);
let vrf_signature = pair.as_ref().vrf_sign(&data);
return Ok(PreDigest { authority_idx: 0, slot: 0.into(), ticket_claim: None, vrf_signature })
}

Expand Down
Loading

0 comments on commit 5e7d6c5

Please sign in to comment.