Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
Change local challenge to use hash of the signature to make sure all …
Browse files Browse the repository at this point in the history
…bytes are taken into account
  • Loading branch information
nazar-pc committed Nov 9, 2021
1 parent fac541c commit c5ee9ce
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 36 deletions.
7 changes: 4 additions & 3 deletions crates/pallet-subspace/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ use sp_runtime::{
Perbill,
};
use subspace_core_primitives::{
ArchivedBlockProgress, LastArchivedBlock, Piece, RootBlock, Sha256Hash, Signature, Tag,
ArchivedBlockProgress, LastArchivedBlock, LocalChallenge, Piece, RootBlock, Sha256Hash,
Signature, Tag,
};
use subspace_solving::{SubspaceCodec, SOLUTION_SIGNING_CONTEXT};

Expand Down Expand Up @@ -205,7 +206,7 @@ pub fn go_to_block(keypair: &Keypair, block: u64, slot: u64) {
piece_index: 0,
encoding,
signature: keypair.sign(ctx.bytes(&tag)).to_bytes().into(),
local_challenge: Signature::default(),
local_challenge: LocalChallenge::default(),
tag,
},
);
Expand Down Expand Up @@ -266,7 +267,7 @@ pub fn generate_equivocation_proof(
piece_index,
encoding,
signature: signature.into(),
local_challenge: Signature::default(),
local_challenge: LocalChallenge::default(),
tag,
},
);
Expand Down
6 changes: 3 additions & 3 deletions crates/sc-consensus-subspace/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use sp_runtime::{
};
use sp_timestamp::InherentDataProvider as TimestampInherentDataProvider;
use std::{cell::RefCell, task::Poll, time::Duration};
use subspace_core_primitives::{Piece, Signature, Tag};
use subspace_core_primitives::{LocalChallenge, Piece, Signature, Tag};
use subspace_solving::SubspaceCodec;
use substrate_test_runtime::{Block as TestBlock, Hash};

Expand Down Expand Up @@ -726,7 +726,7 @@ pub fn dummy_claim_slot(slot: Slot, _epoch: &Epoch) -> Option<(PreDigest, Farmer
piece_index: 0,
encoding: Piece::default(),
signature: Signature::default(),
local_challenge: Signature::default(),
local_challenge: LocalChallenge::default(),
tag: Tag::default(),
},
slot,
Expand Down Expand Up @@ -799,7 +799,7 @@ fn propose_and_import_block<Transaction: Send + 'static>(
piece_index: 0,
encoding,
signature: signature.into(),
local_challenge: Signature::default(),
local_challenge: LocalChallenge::default(),
tag,
},
})],
Expand Down
10 changes: 2 additions & 8 deletions crates/sc-consensus-subspace/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ use sp_core::Public;
use sp_runtime::{traits::DigestItemFor, traits::Header, RuntimeAppPublic};
use subspace_archiving::archiver;
use subspace_core_primitives::{Randomness, Salt, Sha256Hash};
use subspace_solving::{
derive_global_challenge, is_local_challenge_valid, SubspaceCodec, TAG_SIZE,
};
use subspace_solving::{derive_global_challenge, is_local_challenge_valid, SubspaceCodec};

/// Subspace verification parameters
pub(super) struct VerificationParams<'a, B: 'a + BlockT> {
Expand Down Expand Up @@ -196,11 +194,7 @@ fn check_piece<B: BlockT>(

/// Returns true if `solution.tag` is within the solution range.
fn is_within_solution_range(solution: &Solution, solution_range: u64) -> bool {
let target = u64::from_be_bytes(
solution.local_challenge[..TAG_SIZE]
.try_into()
.expect("Signature is always bigger than tag; qed"),
);
let target = u64::from_be_bytes(solution.local_challenge.derive_target());
let (lower, is_lower_overflowed) = target.overflowing_sub(solution_range / 2);
let (upper, is_upper_overflowed) = target.overflowing_add(solution_range / 2);

Expand Down
8 changes: 4 additions & 4 deletions crates/sp-consensus-subspace/src/digests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use codec::{Codec, Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
use sp_consensus_slots::Slot;
use sp_runtime::{DigestItem, RuntimeDebug};
use subspace_core_primitives::{Piece, Randomness, Signature, Tag};
use subspace_core_primitives::{LocalChallenge, Piece, Randomness, Signature, Tag};

// TODO: better documentation here
/// Solution
Expand All @@ -38,8 +38,8 @@ pub struct Solution {
pub encoding: Piece,
/// Signature of the tag
pub signature: Signature,
/// Local challenge derived with farmer's identity
pub local_challenge: Signature,
/// Local challenge derived from global challenge using farmer's identity.
pub local_challenge: LocalChallenge,
/// Tag (hmac of encoding and salt)
pub tag: Tag,
}
Expand All @@ -52,7 +52,7 @@ impl Solution {
piece_index: 0u64,
encoding: Piece::default(),
signature: Signature::default(),
local_challenge: Signature::default(),
local_challenge: LocalChallenge::default(),
tag: Tag::default(),
}
}
Expand Down
58 changes: 54 additions & 4 deletions crates/subspace-core-primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ pub type Sha256Hash = [u8; SHA256_HASH_SIZE];
/// Type of randomness.
pub type Randomness = [u8; RANDOMNESS_LENGTH];

/// Size of `Tag` in bytes.
pub const TAG_SIZE: usize = 8;

/// Type of the commitment for a particular piece.
///
/// TODO: why not use `Commitment` directly?
pub type Tag = [u8; 8];
pub type Tag = [u8; TAG_SIZE];

/// Salt used for creating commitment tags for pieces.
pub type Salt = [u8; 8];
pub type Salt = [u8; TAG_SIZE];

const PUBLIC_KEY_LENGTH: usize = 32;

Expand Down Expand Up @@ -131,6 +132,55 @@ impl AsRef<[u8]> for Signature {
}
}

/// A Ristretto Schnorr signature as bytes produced by `schnorrkel` crate.
#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Encode, Decode, TypeInfo)]
#[cfg_attr(feature = "std", derive(Debug))]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct LocalChallenge(
#[cfg_attr(feature = "std", serde(with = "serde_arrays"))] [u8; SIGNATURE_LENGTH],
);

impl Default for LocalChallenge {
fn default() -> Self {
Self([0u8; SIGNATURE_LENGTH])
}
}

impl From<[u8; SIGNATURE_LENGTH]> for LocalChallenge {
fn from(bytes: [u8; SIGNATURE_LENGTH]) -> Self {
Self(bytes)
}
}

impl From<LocalChallenge> for [u8; SIGNATURE_LENGTH] {
fn from(signature: LocalChallenge) -> Self {
signature.0
}
}

impl Deref for LocalChallenge {
type Target = [u8];

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl AsRef<[u8]> for LocalChallenge {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl LocalChallenge {
/// Derive tags search target from local challenge.
pub fn derive_target(&self) -> Tag {
crypto::sha256_hash(&self.0)[..TAG_SIZE]
.try_into()
.expect("Signature is always bigger than tag; qed")
}
}

/// A piece of archival history in Subspace Network.
///
/// Internally piece contains a record and corresponding witness that together with [`RootBlock`] of
Expand Down
12 changes: 6 additions & 6 deletions crates/subspace-farmer/src/farming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ use anyhow::Result;
use futures::{future, future::Either};
use log::{debug, error, info, trace};
use std::time::Instant;
use subspace_core_primitives::{Salt, Signature};
use subspace_core_primitives::{LocalChallenge, Salt};
use subspace_rpc_primitives::{SlotInfo, Solution, SolutionResponse};
use subspace_solving::TAG_SIZE;

/// Farming Instance to store the necessary information for the farming operations,
/// and also a channel to stop/pause the background farming task
Expand Down Expand Up @@ -105,9 +104,7 @@ async fn subscribe_to_slot_info(

let maybe_solution = match commitments
.find_by_range(
local_challenge[..TAG_SIZE]
.try_into()
.expect("Signature is always bigger than tag; qed"),
local_challenge.derive_target(),
slot_info.solution_range,
slot_info.salt,
)
Expand Down Expand Up @@ -225,6 +222,9 @@ fn update_commitments(
}

/// Derive local challenge for farmer's identity from the global challenge.
fn derive_local_challenge<C: AsRef<[u8]>>(global_challenge: C, identity: &Identity) -> Signature {
fn derive_local_challenge<C: AsRef<[u8]>>(
global_challenge: C,
identity: &Identity,
) -> LocalChallenge {
identity.sign(global_challenge.as_ref()).to_bytes().into()
}
6 changes: 3 additions & 3 deletions crates/subspace-rpc-primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use hex_buffer_serde::{Hex, HexForm};
use serde::{Deserialize, Serialize};
use subspace_core_primitives::objects::BlockObjectMapping;
use subspace_core_primitives::{Piece, PublicKey, Salt, Signature, Tag};
use subspace_core_primitives::{LocalChallenge, Piece, PublicKey, Salt, Signature, Tag};

/// Type of a slot number.
pub type SlotNumber = u64;
Expand Down Expand Up @@ -104,8 +104,8 @@ pub struct Solution {
pub encoding: Piece,
/// Signature of the tag
pub signature: Signature,
/// Local challenge derived with farmer's identity
pub local_challenge: Signature,
/// Local challenge derived from global challenge using farmer's identity.
pub local_challenge: LocalChallenge,
/// Tag (hmac of encoding and salt)
pub tag: Tag,
}
7 changes: 2 additions & 5 deletions crates/subspace-solving/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,11 @@ mod codec;
pub use codec::SubspaceCodec;
use schnorrkel::SignatureResult;
use sha2::{Digest, Sha256};
use subspace_core_primitives::{crypto, Piece, Randomness, Salt, Signature, Tag};
use subspace_core_primitives::{crypto, LocalChallenge, Piece, Randomness, Salt, Tag, TAG_SIZE};

/// Signing context used for creating solution signatures by farmer
pub const SOLUTION_SIGNING_CONTEXT: &[u8] = b"FARMER";

/// Size of `Tag` in bytes.
pub const TAG_SIZE: usize = core::mem::size_of::<Tag>();

/// Check whether commitment tag of a piece is valid for a particular salt, which is used as a
/// Proof-of-Replication
pub fn is_tag_valid(piece: &Piece, salt: Salt, tag: Tag) -> bool {
Expand All @@ -58,7 +55,7 @@ pub fn derive_global_challenge<Slot: Into<u64>>(epoch_randomness: &Randomness, s
/// Verify local challenge for farmer's public key that was derived from the global challenge.
pub fn is_local_challenge_valid<P: AsRef<[u8]>>(
global_challenge: Tag,
local_challenge: &Signature,
local_challenge: &LocalChallenge,
public_key: P,
) -> SignatureResult<()> {
let signature = schnorrkel::Signature::from_bytes(local_challenge)?;
Expand Down

0 comments on commit c5ee9ce

Please sign in to comment.