diff --git a/chain-crypto/src/algorithms/ed25519.rs b/chain-crypto/src/algorithms/ed25519.rs index a95f5ada6..0b1fd1f90 100644 --- a/chain-crypto/src/algorithms/ed25519.rs +++ b/chain-crypto/src/algorithms/ed25519.rs @@ -93,7 +93,10 @@ impl VerificationAlgorithm for Ed25519 { fn signature_from_bytes(data: &[u8]) -> Result { if data.len() != ed25519::SIGNATURE_LENGTH { - return Err(SignatureError::SizeInvalid); + return Err(SignatureError::SizeInvalid { + expected: ed25519::SIGNATURE_LENGTH, + got: data.len(), + }); } let mut buf = [0; ed25519::SIGNATURE_LENGTH]; buf[0..ed25519::SIGNATURE_LENGTH].clone_from_slice(data); diff --git a/chain-crypto/src/algorithms/ed25519_derive.rs b/chain-crypto/src/algorithms/ed25519_derive.rs index a52941ed4..202831556 100644 --- a/chain-crypto/src/algorithms/ed25519_derive.rs +++ b/chain-crypto/src/algorithms/ed25519_derive.rs @@ -62,7 +62,10 @@ impl SecretKeySizeStatic for Ed25519Bip32 { impl From for SignatureError { fn from(v: i::SignatureError) -> Self { match v { - i::SignatureError::InvalidLength(_) => SignatureError::SizeInvalid, + i::SignatureError::InvalidLength(got) => SignatureError::SizeInvalid { + expected: ed25519_bip32::SIGNATURE_SIZE, + got: got, + }, } } } diff --git a/chain-crypto/src/algorithms/ed25519_extended.rs b/chain-crypto/src/algorithms/ed25519_extended.rs index 1ad759245..1c8c15aee 100644 --- a/chain-crypto/src/algorithms/ed25519_extended.rs +++ b/chain-crypto/src/algorithms/ed25519_extended.rs @@ -83,7 +83,10 @@ impl VerificationAlgorithm for Ed25519Extended { fn signature_from_bytes(data: &[u8]) -> Result { if data.len() != ed25519::SIGNATURE_LENGTH { - return Err(SignatureError::SizeInvalid); + return Err(SignatureError::SizeInvalid { + expected: ed25519::SIGNATURE_LENGTH, + got: data.len(), + }); } let mut buf = [0; ed25519::SIGNATURE_LENGTH]; buf[0..ed25519::SIGNATURE_LENGTH].clone_from_slice(data); diff --git a/chain-crypto/src/algorithms/fakemmm.rs b/chain-crypto/src/algorithms/fakemmm.rs index 2f35da031..0c0051098 100644 --- a/chain-crypto/src/algorithms/fakemmm.rs +++ b/chain-crypto/src/algorithms/fakemmm.rs @@ -80,7 +80,10 @@ impl VerificationAlgorithm for FakeMMM { fn signature_from_bytes(data: &[u8]) -> Result { if data.len() != ed25519::SIGNATURE_LENGTH { - return Err(SignatureError::SizeInvalid); + return Err(SignatureError::SizeInvalid { + expected: ed25519::SIGNATURE_LENGTH, + got: data.len(), + }); } let mut buf = [0; ed25519::SIGNATURE_LENGTH]; buf[0..ed25519::SIGNATURE_LENGTH].clone_from_slice(data); diff --git a/chain-crypto/src/algorithms/sumed25519/mod.rs b/chain-crypto/src/algorithms/sumed25519/mod.rs index d9f089d42..4c5c691a1 100644 --- a/chain-crypto/src/algorithms/sumed25519/mod.rs +++ b/chain-crypto/src/algorithms/sumed25519/mod.rs @@ -60,7 +60,10 @@ impl VerificationAlgorithm for SumEd25519_12 { fn signature_from_bytes(data: &[u8]) -> Result { sum::Signature::from_bytes(DEPTH, data).map_err(|e| match e { - sum::Error::InvalidSignatureSize(_) => SignatureError::SizeInvalid, + sum::Error::InvalidSignatureSize(_) => SignatureError::SizeInvalid { + expected: Self::SIGNATURE_SIZE, + got: data.len(), + }, _ => SignatureError::StructureInvalid, }) } diff --git a/chain-crypto/src/algorithms/sumed25519/sum.rs b/chain-crypto/src/algorithms/sumed25519/sum.rs index b8ed45ec3..b41a2f204 100644 --- a/chain-crypto/src/algorithms/sumed25519/sum.rs +++ b/chain-crypto/src/algorithms/sumed25519/sum.rs @@ -465,7 +465,7 @@ impl Signature { return Err(Error::InvalidSignatureSize(bytes.len())); } let found_depth = (bytes.len() - minimum_size) / 32; - if found_depth == depth.0 { + if found_depth != depth.0 { return Err(Error::InvalidSignatureSize(bytes.len())); } @@ -720,6 +720,9 @@ mod tests { if sk.is_updatable() { update(&mut sk).unwrap(); } + + let sigdata = sig.as_bytes(); + assert_eq!(signature_size(depth), sigdata.len()) } } diff --git a/chain-crypto/src/sign.rs b/chain-crypto/src/sign.rs index bd0f9357f..49d928d72 100644 --- a/chain-crypto/src/sign.rs +++ b/chain-crypto/src/sign.rs @@ -21,7 +21,7 @@ impl From for Verification { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum SignatureError { - SizeInvalid, + SizeInvalid { expected: usize, got: usize }, // expected, got in bytes StructureInvalid, } @@ -59,7 +59,11 @@ impl fmt::Display for Signature { impl fmt::Display for SignatureError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - SignatureError::SizeInvalid => write!(f, "Invalid Signature size"), + SignatureError::SizeInvalid { expected, got } => write!( + f, + "Invalid Signature size expecting {} got {}", + expected, got + ), SignatureError::StructureInvalid => write!(f, "Invalid Signature structure"), } } diff --git a/chain-impl-mockchain/doc/format.md b/chain-impl-mockchain/doc/format.md index 25136000e..f1337e53e 100644 --- a/chain-impl-mockchain/doc/format.md +++ b/chain-impl-mockchain/doc/format.md @@ -23,25 +23,30 @@ The header is a small piece of data, containing enough informations for validati Common (2 * 64 bits + 1 * 32 bits + 2 * 256 bits = 84 bytes): -* Size of Header: 16 bits: Maximum header is thus 64K not including the block content -* Version of block: 16 bits -* Size of Content: 32 bits -* Block Date: Epoch (32 bits) + Slot-id (32 bits) -* Chain length (number of ancestor blocks; first block has chain length 0): 32 bits -* Hash of content `H(Content)` (256 bits) -* Parent Header hash : 256 bits (with the special value of 0 to represent the lack of parent for the first block) +* Size of Header: 2 bytes (16 bits): Maximum header is thus 64K not including the block content +* Version of block: 2 bytes (16 bits) +* Size of Content: 4 bytes (32 bits) +* Block Date: Epoch (4 bytes, 32 bits) + Slot-id (4 bytes - 32 bits) +* Chain length (number of ancestor blocks; first block has chain length 0): 4 bytes (32 bits) +* Hash of content `H(Content)` (32 bytes - 256 bits) +* Parent Header hash : 32 bytes (256 bits) + +We reserved the special value of all 0 for the parent header hash, to +represent the lack of parent for the block0, but for other blocks it's not +reserved and could represent, although with negligeable probability, a valid +block. In any case, it means that there's no special meaning to this value in +normal context. In BFT the header also contains (768 bits = 96 bytes): -* BFT Public Key of the leader (256 bits) -* BFT Signature (512 bits) +* BFT Public Key of the leader (32 bytes) +* BFT Signature (64 bytes) -In Praos/Genesis the header also contains (128 bytes + between 480 to 1184 bytes = between 608 to 1312 bytes): +In Praos/Genesis the header also contains (616 bytes): -* VRF PubKey: 256 bits (curve25519-dalek) -* VRF Proof: 768 bits (curve25519-dalek DLEQs) -* KES Signature (content TBD) - * MMM+ed25519: Between 480 Bytes <=> 1184 Bytes +* VRF PubKey: 32 bytes (curve25519-dalek) +* VRF Proof: 96 bytes (curve25519-dalek DLEQs) +* KES Signature: 484 bytes (sumed25519-12) Additionally, we introduce the capability to address each header individually by using a cryptographic hash function : `H(HEADER)`. The hash include all @@ -187,23 +192,23 @@ bytes. The following parameter types exist: -| tag | name | value type | description | -|:-------|:-------|:------------|:--------------| -| 1 | discrimination | u8 | address discrimination; 1 for production, 2 for testing | -| 2 | block0-date | u64 | the official start time of the blockchain, in seconds since the Unix epoch | -| 3 | consensus| u16 | consensus version; 1 for BFT, 2 for Genesis Praos | -| 4 | slots-per-epoch | u32 | number of slots in an epoch | -| 5 | slot-duration | u8 | slot duration in seconds | -| 6 | epoch-stability-depth | u32 | the length of the suffix of the chain (in blocks) considered unstable | -| 8 | genesis-praos-param-f | Milli | determines maximum probability of a stakeholder being elected as leader in a slot | -| 9 | max-number-of-transactions-per-block | u32 | maximum number of transactions in a block | -| 10 | bft-slots-ratio | Milli | fraction of blocks to be created by BFT leaders | -| 11 | add-bft-leader | LeaderId | add a BFT leader | -| 12 | remove-bft-leader | LeaderId | remove a BFT leader | -| 13 | allow-account-creation | bool (u8) | 0 to enable account creation, 1 to disable | -| 14 | linear-fee | LinearFee | coefficients for fee calculations | -| 15 | proposal-expiration | u32 | number of epochs until an update proposal expires | -| 16 | kes-update-speed | u32 | maximum number of seconds per update for KES keys known by the system after start time | +| tag | name | value type | description | +| :--- | :----------------------------------- | :--------- | :------------------------------------------------------------------------------------- | +| 1 | discrimination | u8 | address discrimination; 1 for production, 2 for testing | +| 2 | block0-date | u64 | the official start time of the blockchain, in seconds since the Unix epoch | +| 3 | consensus | u16 | consensus version; 1 for BFT, 2 for Genesis Praos | +| 4 | slots-per-epoch | u32 | number of slots in an epoch | +| 5 | slot-duration | u8 | slot duration in seconds | +| 6 | epoch-stability-depth | u32 | the length of the suffix of the chain (in blocks) considered unstable | +| 8 | genesis-praos-param-f | Milli | determines maximum probability of a stakeholder being elected as leader in a slot | +| 9 | max-number-of-transactions-per-block | u32 | maximum number of transactions in a block | +| 10 | bft-slots-ratio | Milli | fraction of blocks to be created by BFT leaders | +| 11 | add-bft-leader | LeaderId | add a BFT leader | +| 12 | remove-bft-leader | LeaderId | remove a BFT leader | +| 13 | allow-account-creation | bool (u8) | 0 to enable account creation, 1 to disable | +| 14 | linear-fee | LinearFee | coefficients for fee calculations | +| 15 | proposal-expiration | u32 | number of epochs until an update proposal expires | +| 16 | kes-update-speed | u32 | maximum number of seconds per update for KES keys known by the system after start time | `Milli` is a 64-bit entity that encoded a non-negative, fixed-point number with a scaling factor of 1000. That is, the number 1.234 is diff --git a/chain-impl-mockchain/src/block/builder.rs b/chain-impl-mockchain/src/block/builder.rs index 5d52cd1ef..7826d89bf 100644 --- a/chain-impl-mockchain/src/block/builder.rs +++ b/chain-impl-mockchain/src/block/builder.rs @@ -11,7 +11,7 @@ use crate::stake; use crate::transaction::{AuthenticatedTransaction, NoExtra}; use chain_addr::Address; use chain_crypto::{ - Curve25519_2HashDH, Ed25519Extended, FakeMMM, SecretKey, VerifiableRandomFunction, + Curve25519_2HashDH, Ed25519Extended, SecretKey, SumEd25519_12, VerifiableRandomFunction, }; pub struct BlockBuilder { @@ -136,7 +136,7 @@ impl BlockBuilder { pub fn make_genesis_praos_block( mut self, node_id: &stake::StakePoolId, - kes_signing_key: &mut SecretKey, + kes_signing_key: &mut SecretKey, vrf_proof: ::VerifiedRandomOutput, ) -> Block { assert_ne!(self.common.chain_length, ChainLength(0)); diff --git a/chain-impl-mockchain/src/block/header.rs b/chain-impl-mockchain/src/block/header.rs index 747f34ff5..dcce1b47b 100644 --- a/chain-impl-mockchain/src/block/header.rs +++ b/chain-impl-mockchain/src/block/header.rs @@ -13,7 +13,7 @@ use chain_core::{ property, }; use chain_crypto::{ - self, Curve25519_2HashDH, Ed25519Extended, FakeMMM, Signature, VerifiableRandomFunction, + self, Curve25519_2HashDH, Ed25519Extended, Signature, SumEd25519_12, VerifiableRandomFunction, }; pub type HeaderHash = Hash; @@ -54,7 +54,7 @@ pub struct GenesisPraosProof { } #[derive(Debug, Clone)] -pub struct KESSignature(pub(crate) Signature); +pub struct KESSignature(pub(crate) Signature); #[derive(Debug, Clone, PartialEq, Eq)] pub enum Proof { @@ -295,7 +295,8 @@ impl property::Header for Header { mod test { use super::*; use crate::block::ConsensusVersion; - use chain_crypto::AsymmetricKey; + use chain_crypto::{AsymmetricKey, SecretKey, SumEd25519_12}; + use lazy_static::lazy_static; use num_traits::FromPrimitive; use quickcheck::{Arbitrary, Gen, TestResult}; @@ -359,7 +360,11 @@ mod test { }; let kes_proof = { - let mut sk = Arbitrary::arbitrary(g); + lazy_static! { + static ref SK_FIRST: SecretKey = + { SecretKey::generate(&mut ChaChaRng::from_seed([0; 32])) }; + } + let mut sk = SK_FIRST.clone(); // Arbitrary::arbitrary(g); let signature = Signature::generate_update(&mut sk, &[0u8, 1, 2, 3]); KESSignature(signature) }; diff --git a/chain-impl-mockchain/src/certificate.rs b/chain-impl-mockchain/src/certificate.rs index b07741d0a..bf94b8903 100644 --- a/chain-impl-mockchain/src/certificate.rs +++ b/chain-impl-mockchain/src/certificate.rs @@ -386,7 +386,8 @@ impl Readable for StakePoolRetirement { mod test { use super::*; use crate::leadership::genesis::GenesisPraosLeader; - use chain_crypto::SecretKey; + use chain_crypto::{PublicKey, SecretKey, SumEd25519_12}; + use lazy_static::lazy_static; use quickcheck::{Arbitrary, Gen}; impl Arbitrary for Certificate { @@ -444,13 +445,19 @@ mod test { for byte in seed.iter_mut() { *byte = Arbitrary::arbitrary(g); } + lazy_static! { + static ref PK_KES: PublicKey = { + let sk = SecretKey::generate(&mut rand_chacha::ChaChaRng::from_seed([0; 32])); + sk.to_public() + }; + } let mut rng = rand_chacha::ChaChaRng::from_seed(seed); StakePoolInfo { serial: Arbitrary::arbitrary(g), owners: vec![Arbitrary::arbitrary(g)], initial_key: GenesisPraosLeader { vrf_public_key: SecretKey::generate(&mut rng).to_public(), - kes_public_key: SecretKey::generate(&mut rng).to_public(), + kes_public_key: PK_KES.clone(), }, } } diff --git a/chain-impl-mockchain/src/key.rs b/chain-impl-mockchain/src/key.rs index 7cd336f41..69ac76cae 100644 --- a/chain-impl-mockchain/src/key.rs +++ b/chain-impl-mockchain/src/key.rs @@ -30,9 +30,9 @@ fn chain_crypto_pub_err(e: crypto::PublicKeyError) -> ReadError { } fn chain_crypto_sig_err(e: crypto::SignatureError) -> ReadError { match e { - crypto::SignatureError::SizeInvalid => { - ReadError::StructureInvalid("signature size invalid".to_string()) - } + crypto::SignatureError::SizeInvalid { expected, got } => ReadError::StructureInvalid( + format!("signature size invalid, expected {} got {}", expected, got), + ), crypto::SignatureError::StructureInvalid => { ReadError::StructureInvalid("signature structure invalid".to_string()) } diff --git a/chain-impl-mockchain/src/leadership/genesis/mod.rs b/chain-impl-mockchain/src/leadership/genesis/mod.rs index 37f433fe7..31c33739d 100644 --- a/chain-impl-mockchain/src/leadership/genesis/mod.rs +++ b/chain-impl-mockchain/src/leadership/genesis/mod.rs @@ -10,14 +10,14 @@ use crate::{ value::Value, }; use chain_crypto::Verification as SigningVerification; -use chain_crypto::{Curve25519_2HashDH, FakeMMM, PublicKey, SecretKey}; +use chain_crypto::{Curve25519_2HashDH, PublicKey, SecretKey, SumEd25519_12}; pub use vrfeval::{ActiveSlotsCoeff, ActiveSlotsCoeffError, Witness}; use vrfeval::{Nonce, PercentStake, VrfEvaluator}; /// Praos Leader consisting of the KES public key and VRF public key #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenesisPraosLeader { - pub kes_public_key: PublicKey, + pub kes_public_key: PublicKey, pub vrf_public_key: PublicKey, } @@ -154,7 +154,7 @@ mod test { use chain_core::property::Transaction as T; use chain_core::property::{BlockId, HasTransaction}; use chain_crypto::{ - algorithms::{Ed25519, Ed25519Extended, FakeMMM}, + algorithms::{Ed25519, Ed25519Extended, SumEd25519_12}, SecretKey, }; use quickcheck::{Arbitrary, StdGen}; diff --git a/chain-impl-mockchain/src/leadership/mod.rs b/chain-impl-mockchain/src/leadership/mod.rs index d075cd1c7..21f799e6a 100644 --- a/chain-impl-mockchain/src/leadership/mod.rs +++ b/chain-impl-mockchain/src/leadership/mod.rs @@ -4,7 +4,7 @@ use crate::{ ledger::Ledger, stake::StakePoolId, }; -use chain_crypto::{Curve25519_2HashDH, Ed25519Extended, FakeMMM, SecretKey}; +use chain_crypto::{Curve25519_2HashDH, Ed25519Extended, SecretKey, SumEd25519_12}; use chain_time::era::TimeEra; pub mod bft; @@ -50,7 +50,7 @@ pub struct BftLeader { pub struct GenesisLeader { pub node_id: StakePoolId, - pub sig_key: SecretKey, + pub sig_key: SecretKey, pub vrf_key: SecretKey, }