Skip to content

Commit

Permalink
feat(data_structures): implement protocol versions controller
Browse files Browse the repository at this point in the history
also: update secp256k1 deps

close witnet#2418
  • Loading branch information
aesedepece committed Jan 19, 2024
1 parent a677962 commit 2add583
Show file tree
Hide file tree
Showing 16 changed files with 400 additions and 303 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ hmac = "0.7.1"
memzero = "0.1.0"
rand = "0.7.3"
ring = "0.16.11"
secp256k1 = { version = "0.22.2", features = ["global-context", "recovery"] }
secp256k1 = { version = "0.28.1", features = ["global-context", "recovery"] }
serde = { version = "1.0.104", optional = true }
sha2 = "0.8.1"
tiny-bip39 = "0.7.0"
Expand Down
10 changes: 8 additions & 2 deletions crypto/src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ pub enum KeyDerivationError {
/// Invalid seed length
#[fail(display = "The length of the seed is invalid, must be between 128/512 bits")]
InvalidSeedLength,
/// A secret key is greater than the curve order
#[fail(display = "The secret key is greater than the curve order")]
SecretLargerThanCurveOrder,
/// Secp256k1 internal error
#[fail(display = "Error in secp256k1 crate")]
Secp256k1Error(secp256k1::Error),
Expand Down Expand Up @@ -300,8 +303,11 @@ impl ExtendedSK {

let (chain_code, mut secret_key) = get_chain_code_and_secret(&index_bytes, hmac512)?;

secret_key
.add_assign(&self.secret_key[..])
let scalar = secp256k1::Scalar::from_be_bytes(self.secret_key.secret_bytes())
.map_err(|_| KeyDerivationError::SecretLargerThanCurveOrder)?;

secret_key = secret_key
.add_tweak(&scalar)
.map_err(KeyDerivationError::Secp256k1Error)?;

Ok(ExtendedSK {
Expand Down
4 changes: 2 additions & 2 deletions crypto/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ pub type PublicKey = secp256k1::PublicKey;
/// secure hash function, otherwise this function is not secure.
/// - Returns an Error if data is not a 32-byte array
pub fn sign(secret_key: SecretKey, data: &[u8]) -> Result<Signature, Error> {
let msg = Message::from_slice(data)?;
let msg = Message::from_digest_slice(data)?;

Ok(secret_key.sign_ecdsa(msg))
}
/// Verify signature with a provided public key.
/// - Returns an Error if data is not a 32-byte array
pub fn verify(public_key: &PublicKey, data: &[u8], sig: &Signature) -> Result<(), Error> {
let msg = Message::from_slice(data)?;
let msg = Message::from_digest_slice(data)?;

sig.verify(&msg, public_key)
}
Expand Down
48 changes: 32 additions & 16 deletions data_structures/src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::{

use bech32::{FromBase32, ToBase32};
use bls_signatures_rs::{bn256, bn256::Bn256, MultiSignature};
use ethereum_types::{U256, U512};
use failure::Fail;
use futures::future::BoxFuture;
use ordered_float::OrderedFloat;
Expand All @@ -23,19 +24,15 @@ use witnet_crypto::{
merkle::merkle_tree_root as crypto_merkle_tree_root,
secp256k1::{
self,
PublicKey as Secp256k1_PublicKey,
ecdsa::{
RecoverableSignature, RecoveryId,
Signature as Secp256k1_Signature,
},
SecretKey as Secp256k1_SecretKey,
ecdsa::{RecoverableSignature, RecoveryId, Signature as Secp256k1_Signature},
PublicKey as Secp256k1_PublicKey, SecretKey as Secp256k1_SecretKey,
},
};
use witnet_protected::Protected;
use witnet_reputation::{ActiveReputationSet, TotalReputationSet};

use crate::{
chain::{Signature::Secp256k1, tapi::TapiEngine},
chain::{tapi::TapiEngine, Signature::Secp256k1},
data_request::{calculate_reward_collateral_ratio, DataRequestPool},
error::{
DataRequestError, EpochCalculationError, OutputPointerParseError, Secp256k1ConversionError,
Expand All @@ -53,7 +50,7 @@ use crate::{
UnstakeTransaction, VTTransaction,
},
transaction::{
BETA, COMMIT_WEIGHT, MemoHash, MemoizedHashable, OUTPUT_SIZE, REVEAL_WEIGHT, TALLY_WEIGHT,
MemoHash, MemoizedHashable, BETA, COMMIT_WEIGHT, OUTPUT_SIZE, REVEAL_WEIGHT, TALLY_WEIGHT,
},
utxo_pool::{OldUnspentOutputsPool, OwnUnspentOutputsPool, UnspentOutputsPool},
vrf::{BlockEligibilityClaim, DataRequestEligibilityClaim},
Expand Down Expand Up @@ -1188,8 +1185,6 @@ impl Hash {
///
/// If n is 0 because of a division by zero.
pub fn div_mod(&self, n: u64) -> (Hash, u64) {
use ethereum_types::U256;

let hash_u256 = U256::from_big_endian(self.as_ref());
let n_u256 = U256::from(n);
let (d, m) = hash_u256.div_mod(n_u256);
Expand Down Expand Up @@ -1575,12 +1570,14 @@ pub struct KeyedSignature {

impl KeyedSignature {
pub fn from_recoverable_hex(string: &str, msg: &[u8]) -> Self {
// FIXME: make this safe by using a `Result` instead of unwrapping
let bytes = hex::decode(string).unwrap();

Self::from_recoverable_slice(&bytes, msg)
}
pub fn from_recoverable(recoverable: &RecoverableSignature, message: &[u8]) -> Self {
let msg = secp256k1::Message::from_slice(message).unwrap();
// FIXME: make this safe by using a `Result` instead of unwrapping
let msg = secp256k1::Message::from_digest_slice(message).unwrap();
let signature = recoverable.to_standard();
let public_key = recoverable.recover(&msg).unwrap();

Expand All @@ -1592,12 +1589,31 @@ impl KeyedSignature {

// Recovers a keyed signature from its serialized form and a known message.
pub fn from_recoverable_slice(compact: &[u8], message: &[u8]) -> Self {
let recid = RecoveryId::from_i32(0).unwrap();
let recoverable =
secp256k1::ecdsa::RecoverableSignature::from_compact(compact, recid).unwrap();
// FIXME: make this safe by using a `Result` instead of unwrapping
let recid = RecoveryId::from_i32(compact[0] as i32).unwrap();
let recoverable = RecoverableSignature::from_compact(&compact[1..], recid).unwrap();

Self::from_recoverable(&recoverable, message)
}

/// Serializes a `KeyedSignature` into a compact encoding form that contains the public key recovery ID as a prefix.
pub fn to_recoverable_bytes(self, message: &[u8]) -> [u8; 65] {
// FIXME: make this safe by using a `Result` instead of unwrapping
let mut recoverable_bytes = [0; 65];
recoverable_bytes[1..].clone_from_slice(&self.signature.to_bytes().unwrap());

for i in 0..4 {
recoverable_bytes[0] = i;

let recovered = KeyedSignature::from_recoverable_slice(&recoverable_bytes, message);

if recovered.public_key == self.public_key {
break;
}
}

recoverable_bytes
}
}

/// Public Key data structure
Expand Down Expand Up @@ -4508,14 +4524,14 @@ pub fn block_example() -> Block {
#[cfg(test)]
mod tests {
use witnet_crypto::{
merkle::{InclusionProof, merkle_tree_root},
merkle::{merkle_tree_root, InclusionProof},
secp256k1::{PublicKey as Secp256k1_PublicKey, SecretKey as Secp256k1_SecretKey},
signature::sign,
};

use crate::{
proto::versioning::{ProtocolVersion, VersionedHashable},
superblock::{ARSIdentities, mining_build_superblock},
superblock::{mining_build_superblock, ARSIdentities},
transaction::{CommitTransactionBody, RevealTransactionBody, VTTransactionBody},
};

Expand Down
4 changes: 2 additions & 2 deletions data_structures/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,12 +291,12 @@ pub enum TransactionError {
/// Stake amount below minimum
#[fail(
display = "The amount of coins in stake ({}) is less than the minimum allowed ({})",
min_stake, stake
stake, min_stake
)]
StakeBelowMinimum { min_stake: u64, stake: u64 },
/// Unstaking more than the total staked
#[fail(
display = "Unstaking ({}) more than the total staked ({})",
display = "Tried to unstake more coins than the current stake ({} > {})",
unstake, stake
)]
UnstakingMoreThanStaked { stake: u64, unstake: u64 },
Expand Down
41 changes: 35 additions & 6 deletions data_structures/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ use std::sync::RwLock;

use lazy_static::lazy_static;

use crate::{chain::{Environment, Epoch}, proto::versioning::ProtocolVersion};
use crate::proto::versioning::ProtocolInfo;
use crate::{
chain::{Environment, Epoch},
proto::versioning::ProtocolVersion,
};

/// Module containing functions to generate Witnet's protocol messages
pub mod builders;
Expand Down Expand Up @@ -129,22 +132,22 @@ pub fn get_protocol_version(epoch: Option<Epoch>) -> ProtocolVersion {
if let Some(epoch) = epoch {
protocol_info.all_versions.version_for_epoch(epoch)
} else {
*protocol_info.current_version
protocol_info.current_version
}
}

pub fn register_protocol_version(epoch: Epoch, protocol_version: ProtocolVersion) {
// This unwrap is safe as long as the lock is not poisoned.
// The lock can only become poisoned when a writer panics.
let mut protocol_info = PROTOCOL.write().unwrap();
*protocol_info.register(epoch, protocol_version);
protocol_info.register(epoch, protocol_version);
}

/// Set the protocol version that we are running.
/// #[cfg(not(test))]
pub fn set_protocol_version(protocol_version: ProtocolVersion) {
let mut protocol = PROTOCOL.write().unwrap();
*protocol.current_version = protocol_version;
protocol.current_version = protocol_version;
}

#[cfg(test)]
Expand All @@ -159,10 +162,36 @@ mod tests {
}

#[test]
fn default_protocol_version() {
fn protocol_versions() {
// If this default changes before the transition to V2 is complete, almost everything will
// break because data structures change schema and, serialization changes and hash
// derivation breaks too
assert_eq!(get_protocol_version(), ProtocolVersion::V1_7);
let version = get_protocol_version(None);
assert_eq!(version, ProtocolVersion::V1_7);

// Register the different protocol versions
register_protocol_version(100, ProtocolVersion::V1_7);
register_protocol_version(200, ProtocolVersion::V1_8);
register_protocol_version(300, ProtocolVersion::V2_0);

// The initial protocol version should be the default one
let version = get_protocol_version(Some(0));
assert_eq!(version, ProtocolVersion::V1_7);

// Right after the
let version = get_protocol_version(Some(100));
assert_eq!(version, ProtocolVersion::V1_7);
let version = get_protocol_version(Some(200));
assert_eq!(version, ProtocolVersion::V1_8);
let version = get_protocol_version(Some(300));
assert_eq!(version, ProtocolVersion::V2_0);

let version = get_protocol_version(None);
assert_eq!(version, ProtocolVersion::V1_7);

set_protocol_version(ProtocolVersion::V2_0);

let version = get_protocol_version(None);
assert_eq!(version, ProtocolVersion::V2_0);
}
}
8 changes: 4 additions & 4 deletions data_structures/src/proto/versioning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
types::Message,
};

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct ProtocolInfo {
pub current_version: ProtocolVersion,
pub all_versions: VersionsMap,
Expand All @@ -48,12 +48,12 @@ impl VersionsMap {
}

pub fn version_for_epoch(&self, queried_epoch: Epoch) -> ProtocolVersion {
*self
.vfe
self.vfe
.iter()
.rev()
.find(|(epoch, _)| **epoch < queried_epoch)
.find(|(epoch, _)| **epoch <= queried_epoch)
.map(|(_, version)| version)
.copied()
.unwrap_or_default()
}
}
Expand Down
2 changes: 1 addition & 1 deletion node/src/actors/chain_manager/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1296,7 +1296,7 @@ impl Handler<BuildStake> for ChainManager {
type Result = ResponseActFuture<Self, <BuildStake as Message>::Result>;

fn handle(&mut self, msg: BuildStake, _ctx: &mut Self::Context) -> Self::Result {
if self.sm_state != StateMachine::Synced {
if !msg.dry_run && self.sm_state != StateMachine::Synced {
return Box::pin(actix::fut::err(
ChainManagerError::NotSynced {
current_state: self.sm_state,
Expand Down
2 changes: 1 addition & 1 deletion node/src/actors/chain_manager/mining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ mod tests {
// Validate block signature
let mut signatures_to_verify = vec![];
assert!(validate_block_signature(&block, &mut signatures_to_verify).is_ok());
matches!(verify_signatures(signatures_to_verify, vrf), Ok(_));
assert!(verify_signatures(signatures_to_verify, vrf).is_ok());
}

static MILLION_TX_OUTPUT: &str =
Expand Down
11 changes: 3 additions & 8 deletions node/src/actors/chain_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,8 +682,6 @@ impl ChainManager {
block_epoch: block.block_header.beacon.checkpoint,
};

println!("1");

let mut transaction_visitor = PriorityVisitor::default();

let utxo_diff = process_validations(
Expand All @@ -703,14 +701,12 @@ impl ChainManager {
Some(&mut transaction_visitor),
)?;

println!("2");
// Extract the collected priorities from the internal state of the visitor
let priorities = transaction_visitor.take_state();

// Persist block and update ChainState
self.consolidate_block(ctx, block, utxo_diff, priorities, resynchronizing);

println!("3");
Ok(())
} else {
Err(ChainManagerError::ChainNotReady.into())
Expand Down Expand Up @@ -2778,7 +2774,6 @@ pub fn process_validations(
active_wips: &ActiveWips,
transaction_visitor: Option<&mut dyn Visitor<Visitable = (Transaction, u64, u32)>>,
) -> Result<Diff, failure::Error> {
println!("pv1");
if !resynchronizing {
let mut signatures_to_verify = vec![];
validate_block(
Expand All @@ -2793,7 +2788,7 @@ pub fn process_validations(
)?;
verify_signatures(signatures_to_verify, vrf_ctx)?;
}
println!("pv2");

let mut signatures_to_verify = vec![];
let utxo_dif = validate_block_transactions(
utxo_set,
Expand All @@ -2808,11 +2803,11 @@ pub fn process_validations(
active_wips,
transaction_visitor,
)?;
println!("pv3");

if !resynchronizing {
verify_signatures(signatures_to_verify, vrf_ctx)?;
}
println!("pv4");

Ok(utxo_dif)
}

Expand Down
Loading

0 comments on commit 2add583

Please sign in to comment.