Skip to content

Commit

Permalink
feat: threshold raw vote extensions sign_hash
Browse files Browse the repository at this point in the history
  • Loading branch information
lklimek committed Jan 31, 2024
1 parent 34be58b commit 712c33c
Showing 1 changed file with 107 additions and 50 deletions.
157 changes: 107 additions & 50 deletions abci/src/signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,32 @@ const VOTE_EXTENSION_REQUEST_ID_PREFIX: &str = "dpevote";
/// SignDigest returns message digest that should be provided directly to a
/// signing/verification function (aka Sign ID)
pub trait SignDigest {
#[deprecated = "replaced with sign_hash() to unify naming between core, platform and tenderdash"]
fn sign_digest(
&self,
chain_id: &str,
quorum_type: u8,
quorum_hash: &[u8; 32],
height: i64,
round: i32,
) -> Result<Vec<u8>, Error> {
self.sign_hash(chain_id, quorum_type, quorum_hash, height, round)
}

/// Returns message digest that should be provided directly to a
/// signing/verification function.
fn sign_hash(
&self,
chain_id: &str,
quorum_type: u8,
quorum_hash: &[u8; 32],
height: i64,
round: i32,
) -> Result<Vec<u8>, Error>;
}

impl SignDigest for Commit {
fn sign_digest(
fn sign_hash(
&self,
chain_id: &str,
quorum_type: u8,
Expand All @@ -60,7 +74,7 @@ impl SignDigest for Commit {
let request_id = sign_request_id(VOTE_REQUEST_ID_PREFIX, height, round);
let sign_bytes_hash = self.sha256(chain_id, height, round)?;

let digest = sign_digest(
let digest = sign_hash(
quorum_type,
quorum_hash,
request_id[..]
Expand All @@ -81,7 +95,7 @@ impl SignDigest for Commit {
}

impl SignDigest for CanonicalVote {
fn sign_digest(
fn sign_hash(
&self,
chain_id: &str,
quorum_type: u8,
Expand All @@ -93,7 +107,7 @@ impl SignDigest for CanonicalVote {
let request_id = sign_request_id(VOTE_REQUEST_ID_PREFIX, height, round);
let sign_bytes_hash = self.sha256(chain_id, height, round)?;

let digest = sign_digest(
let digest = sign_hash(
quorum_type,
quorum_hash,
request_id[..]
Expand All @@ -114,23 +128,61 @@ impl SignDigest for CanonicalVote {
}

impl SignDigest for VoteExtension {
fn sign_digest(
fn sign_hash(
&self,
chain_id: &str,
quorum_type: u8,
quorum_hash: &[u8; 32],
height: i64,
round: i32,
) -> Result<Vec<u8>, Error> {
let request_id = sign_request_id(VOTE_EXTENSION_REQUEST_ID_PREFIX, height, round);
let sign_bytes_hash = self.sha256(chain_id, height, round)?;
let (request_id, sign_bytes_hash) = match self.r#type() {
VoteExtensionType::ThresholdRecover => {
let request_id = sign_request_id(VOTE_EXTENSION_REQUEST_ID_PREFIX, height, round);
let sign_bytes_hash = self.sha256(chain_id, height, round)?;

Ok(sign_digest(
(request_id, sign_bytes_hash)
},

VoteExtensionType::ThresholdRecoverRaw => {
let mut sign_bytes_hash = self.extension.clone();
sign_bytes_hash.reverse();

let request_id = self.sign_request_id.clone().unwrap_or_default();
let request_id = if request_id.is_empty() {
sign_request_id(VOTE_EXTENSION_REQUEST_ID_PREFIX, height, round)
} else {
// we do double-sha256, and then reverse bytes
let mut request_id = lhash::sha256(&lhash::sha256(&request_id));
request_id.reverse();
request_id.to_vec()
};

(request_id, sign_bytes_hash)
},

VoteExtensionType::Default => unimplemented!(
"vote extension of type {:?} cannot be signed",
self.r#type()
),
};
let sign_hash = sign_hash(
quorum_type,
quorum_hash,
request_id[..].try_into().unwrap(),
request_id[..]
.try_into()
.expect("invalid request ID length"),
&sign_bytes_hash,
))
);

tracing::trace!(
digest=hex::encode(&sign_hash),
?quorum_type,
quorum_hash=hex::encode(quorum_hash),
request_id=hex::encode(request_id),
vote_extension=?self, "vote extension sign hash");

Ok(sign_hash)
}
}

Expand All @@ -142,7 +194,7 @@ fn sign_request_id(prefix: &str, height: i64, round: i32) -> Vec<u8> {
lhash::sha256(&buf).to_vec()
}

fn sign_digest(
fn sign_hash(
quorum_type: u8,
quorum_hash: &[u8; 32],
request_id: &[u8; 32],
Expand Down Expand Up @@ -344,8 +396,11 @@ pub mod tests {
use std::{string::ToString, vec::Vec};

use super::SignBytes;
use crate::proto::types::{
Commit, PartSetHeader, SignedMsgType, Vote, VoteExtension, VoteExtensionType,
use crate::{
proto::types::{
Commit, PartSetHeader, SignedMsgType, Vote, VoteExtension, VoteExtensionType,
},
signatures::SignDigest,
};

#[test]
Expand Down Expand Up @@ -442,25 +497,38 @@ pub mod tests {
assert_eq!(expect_sign_bytes, actual);
}

#[test]
fn vote_extension_threshold_raw_sign_bytes() {
const EXTENSION: &[u8] = &[1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8];
/// test vector for threshold-raw vote extensions
///
/// Returns expected sig hash and vote extension
fn ve_threshold_raw() -> ([u8; 32], VoteExtension) {
let ve = VoteExtension {
extension: EXTENSION.to_vec(),
extension: [1, 2, 3, 4, 5, 6, 7, 8].repeat(4),
r#type: VoteExtensionType::ThresholdRecoverRaw.into(),
signature: Default::default(),
sign_request_id: Some("dpe-sign-request-id".as_bytes().to_vec()),
sign_request_id: Some("dpevote-someSignRequestID".as_bytes().to_vec()),
};
let expected_sign_hash: [u8; 32] = [
0xe, 0x88, 0x8d, 0xa8, 0x97, 0xf1, 0xc0, 0xfd, 0x6a, 0xe8, 0x3b, 0x77, 0x9b, 0x5, 0xdd,
0x28, 0xc, 0xe2, 0x58, 0xf6, 0x4c, 0x86, 0x1, 0x34, 0xfa, 0x4, 0x27, 0xe1, 0xaa, 0xab,
0x1a, 0xde,
];

let chain_id = "some-chain".to_string();
let height = 1;
let round = 2;
(expected_sign_hash, ve)
}

let expect_sign_bytes = EXTENSION.to_vec();
#[test]
fn test_ve_threshold_raw_sign_bytes() {
let (_, ve) = ve_threshold_raw();
let expected_sign_bytes = ve.extension.clone();

// chain_id, height and round are unused
let chain_id = String::new();
let height = -1;
let round = -1;

let actual = ve.sign_bytes(&chain_id, height, round).unwrap();

assert_eq!(expect_sign_bytes, actual);
assert_eq!(expected_sign_bytes, actual);
}

#[test]
Expand All @@ -478,39 +546,28 @@ pub mod tests {
hex::decode("0CA3D5F42BDFED0C4FDE7E6DE0F046CC76CDA6CEE734D65E8B2EE0E375D4C57D")
.unwrap();

let expect_sign_id =
let expect_sign_hash =
hex::decode("DA25B746781DDF47B5D736F30B1D9D0CC86981EEC67CBE255265C4361DEF8C2E")
.unwrap();

let sign_id = super::sign_digest(100, &quorum_hash, request_id, &sign_bytes_hash);
assert_eq!(expect_sign_id, sign_id); // 194,4
let sign_hash = super::sign_hash(100, &quorum_hash, request_id, &sign_bytes_hash);
assert_eq!(expect_sign_hash, sign_hash); // 194,4
}

#[test]
fn test_raw_extension_sign_digest() {
fn test_ve_threshold_raw_sign_digest() {
const QUORUM_TYPE: u8 = 106;

let quorum_hash: [u8; 32] =
hex::decode("dddabfe1c883dd8a2c71c4281a4212c3715a61f87d62a99aaed0f65a0506c053")
.unwrap()
.try_into()
.unwrap();

let request_id =
hex::decode("922a8fc39b6e265ca761eaaf863387a5e2019f4795a42260805f5562699fd9fa")
.unwrap();
let request_id = request_id[..].try_into().unwrap();

let sign_bytes_hash =
hex::decode("7dfb2432d37f004c4eb2b9aebf601ba4ad59889b81d2e8c7029dce3e0bf8381c")
.unwrap();

let mut expect_sign_id =
hex::decode("6d98f773cef8484432c4946c6b96e04aab39fd119c77de2f21d668dd17d5d2f6")
.unwrap();
expect_sign_id.reverse();

let sign_id = super::sign_digest(QUORUM_TYPE, &quorum_hash, request_id, &sign_bytes_hash);
assert_eq!(expect_sign_id, sign_id);
let quorum_hash: [u8; 32] = [8u8, 7, 6, 5, 4, 3, 2, 1]
.repeat(4)
.try_into()
.expect("invalid quorum hash length");
let (expected_sign_hash, ve) = ve_threshold_raw();

// height, round, chain id are not used in sign digest for threshold-raw
let sign_hash = ve
.sign_hash("", QUORUM_TYPE, &quorum_hash, -1, -1)
.expect("sign digest failed");

assert_eq!(sign_hash, expected_sign_hash);
}
}

0 comments on commit 712c33c

Please sign in to comment.