Skip to content

Commit

Permalink
Merge pull request #95 from datachainlab/message-aggregation
Browse files Browse the repository at this point in the history
Message aggregation support

Signed-off-by: Jun Kimura <[email protected]>
  • Loading branch information
bluele authored Dec 28, 2023
2 parents b30213a + a9cc4ca commit 3bd77b9
Show file tree
Hide file tree
Showing 20 changed files with 920 additions and 108 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use crate::light_client::Error;
use crate::prelude::*;
use context::Context;
use crypto::{EnclavePublicKey, Signer, Verifier};
use ecall_commands::{AggregateMessagesInput, AggregateMessagesResult, LightClientResult};
use light_client::{
commitments::{self, prove_commitment, Message, UpdateClientMessage},
HostContext, LightClientResolver,
};
use store::KVStore;

pub fn aggregate_messages<R: LightClientResolver, S: KVStore, K: Signer>(
ctx: &mut Context<R, S, K>,
input: AggregateMessagesInput,
) -> Result<LightClientResult, Error> {
ctx.set_timestamp(input.current_timestamp);

if input.messages.len() < 2 {
return Err(Error::invalid_argument(
"messages and signatures must have at least 2 elements".into(),
));
}
if input.messages.len() != input.signatures.len() {
return Err(Error::invalid_argument(
"messages and signatures must have the same length".into(),
));
}

let ek = ctx.get_enclave_key();
let pk = ek.pubkey().map_err(Error::crypto)?;

let messages = input
.messages
.into_iter()
.map(|c| Message::from_bytes(&c)?.try_into())
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.zip(input.signatures.iter())
.map(|(c, s)| -> Result<_, Error> {
verify_commitment(&pk, &c, s)?;
c.context.validate(ctx.host_timestamp())?;
Ok(c)
})
.collect::<Result<Vec<_>, _>>()?;

let message = Message::from(commitments::aggregate_messages(messages)?);
let proof = prove_commitment(ek, input.signer, message)?;

Ok(LightClientResult::AggregateMessages(
AggregateMessagesResult(proof),
))
}

fn verify_commitment(
verifier: &EnclavePublicKey,
commitment: &UpdateClientMessage,
signature: &[u8],
) -> Result<(), Error> {
let message_bytes = Message::UpdateClient(commitment.clone()).to_bytes();
verifier
.verify(&message_bytes, signature)
.map_err(Error::crypto)?;
Ok(())
}
12 changes: 12 additions & 0 deletions enclave-modules/ecall-handler/src/light_client/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ use flex_error::*;
define_error! {
#[derive(Debug, PartialEq, Eq)]
Error {
InvalidArgument
{
descr: String
}
|e| {
format_args!("invalid argument: descr={}", e.descr)
},

SealedEnclaveKeyNotFound
|_| { "Sealed EnclaveKey not found" },

Expand All @@ -19,6 +27,10 @@ define_error! {
[light_client::commitments::Error]
|_| { "Commitment error" },

Crypto
[crypto::Error]
|_| { "Crypto error" },

LcpType
{}
[lcp_types::TypeError]
Expand Down
2 changes: 2 additions & 0 deletions enclave-modules/ecall-handler/src/light_client/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
pub use aggregate_messages::aggregate_messages;
pub use errors::Error;
pub use init_client::init_client;
pub use query::query_client;
pub use router::dispatch;
pub use update_client::update_client;
pub use verify_state::{verify_membership, verify_non_membership};

mod aggregate_messages;
mod errors;
mod init_client;
mod query;
Expand Down
4 changes: 3 additions & 1 deletion enclave-modules/ecall-handler/src/light_client/router.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::light_client::{
init_client, query_client, update_client, verify_membership, verify_non_membership, Error,
aggregate_messages, init_client, query_client, update_client, verify_membership,
verify_non_membership, Error,
};
use context::Context;
use crypto::NopSigner;
Expand All @@ -25,6 +26,7 @@ pub fn dispatch<E: Env>(
match cmd {
InitClient(input) => init_client(&mut ctx, input)?,
UpdateClient(input) => update_client(&mut ctx, input)?,
AggregateMessages(input) => aggregate_messages(&mut ctx, input)?,
VerifyMembership(input) => verify_membership(&mut ctx, input)?,
VerifyNonMembership(input) => verify_non_membership(&mut ctx, input)?,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ pub fn update_client<R: LightClientResolver, S: KVStore, K: Signer>(

let message: Message = {
let mut msg = UpdateClientMessage::try_from(res.message)?;
if input.include_state && !msg.emitted_states.is_empty() {
msg.emitted_states = vec![EmittedState(
res.height,
res.new_any_consensus_state.clone(),
)];
if input.include_state && msg.emitted_states.is_empty() {
msg.emitted_states = vec![EmittedState(res.height, res.new_any_client_state.clone())];
}
msg.into()
};
Expand Down
192 changes: 189 additions & 3 deletions modules/commitments/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub const VALIDATION_CONTEXT_TYPE_EMPTY_EMPTY: u16 = 0;
pub const VALIDATION_CONTEXT_TYPE_EMPTY_WITHIN_TRUSTING_PERIOD: u16 = 1;
pub const VALIDATION_CONTEXT_HEADER_SIZE: usize = 32;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ValidationContext {
Empty,
TrustingPeriod(TrustingPeriodContext),
Expand Down Expand Up @@ -41,6 +41,42 @@ impl ValidationContext {
header
}

pub fn aggregate(self, other: Self) -> Result<Self, Error> {
match (self, other) {
(Self::Empty, Self::Empty) => Ok(Self::Empty),
(Self::Empty, Self::TrustingPeriod(ctx)) => Ok(Self::TrustingPeriod(ctx)),
(Self::TrustingPeriod(ctx), Self::Empty) => Ok(Self::TrustingPeriod(ctx)),
(Self::TrustingPeriod(ctx1), Self::TrustingPeriod(ctx2)) => {
if ctx1.trusting_period != ctx2.trusting_period {
return Err(Error::context_aggregation_failed(format!(
"trusting_period mismatch: ctx1={:?} ctx2={:?}",
ctx1.trusting_period, ctx2.trusting_period,
)));
}
if ctx1.clock_drift != ctx2.clock_drift {
return Err(Error::context_aggregation_failed(format!(
"clock_drift mismatch: ctx1={:?} ctx2={:?}",
ctx1.clock_drift, ctx2.clock_drift
)));
}
Ok(Self::TrustingPeriod(TrustingPeriodContext::new(
ctx1.trusting_period,
ctx1.clock_drift,
if ctx1.untrusted_header_timestamp > ctx2.untrusted_header_timestamp {
ctx1.untrusted_header_timestamp
} else {
ctx2.untrusted_header_timestamp
},
if ctx1.trusted_state_timestamp < ctx2.trusted_state_timestamp {
ctx1.trusted_state_timestamp
} else {
ctx2.trusted_state_timestamp
},
)))
}
}
}

fn parse_context_type_from_header(header_bytes: &[u8]) -> Result<u16, Error> {
if header_bytes.len() != VALIDATION_CONTEXT_HEADER_SIZE {
return Err(Error::invalid_validation_context_header(format!(
Expand Down Expand Up @@ -150,7 +186,7 @@ impl Display for ValidationContext {
}

/// NOTE: time precision is in seconds (i.e. nanoseconds are truncated)
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TrustingPeriodContext {
/// How long a validator set is trusted for (must be shorter than the chain's
/// unbonding period)
Expand Down Expand Up @@ -236,7 +272,7 @@ impl Display for TrustingPeriodContext {
write!(
f,
"trusting_period={} clock_drift={} untrusted_header_timestamp={} trusted_state_timestamp={}",
self.trusting_period.as_secs(), self.clock_drift.as_secs(), self.untrusted_header_timestamp, self.trusted_state_timestamp
self.trusting_period.as_nanos(), self.clock_drift.as_nanos(), self.untrusted_header_timestamp.as_unix_timestamp_nanos(), self.trusted_state_timestamp.as_unix_timestamp_nanos()
)
}
}
Expand Down Expand Up @@ -537,4 +573,154 @@ mod tests {
validate_and_assert_no_error(ctx, current_timestamp);
}
}

#[test]
fn test_validation_context_aggregation() {
{
let ctx0 = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-19 0:00 UTC),
datetime!(2023-08-19 0:00 UTC),
));
let ctx1 = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let expected = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-19 0:00 UTC),
));
let res = ctx0.aggregate(ctx1);
if let Ok(ctx) = res {
assert_eq!(ctx, expected);
} else {
panic!("{:?}", res);
}
}

{
let ctx0 = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let ctx1 = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-19 0:00 UTC),
datetime!(2023-08-19 0:00 UTC),
));
let expected = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-19 0:00 UTC),
));
let res = ctx0.aggregate(ctx1);
if let Ok(ctx) = res {
assert_eq!(ctx, expected);
} else {
panic!("{:?}", res);
}
}

{
let ctx0 = ValidationContext::from(build_trusting_period_context(
2,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let ctx1 = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let res = ctx0.aggregate(ctx1);
assert!(res.is_err());
}

{
let ctx0 = ValidationContext::from(build_trusting_period_context(
1,
2,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let ctx1 = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let res = ctx0.aggregate(ctx1);
assert!(res.is_err());
}
}

#[test]
fn test_validation_context_and_empty_aggregation() {
{
let ctx0 = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let ctx1 = ValidationContext::Empty;
let expected = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let res = ctx0.aggregate(ctx1);
if let Ok(ctx) = res {
assert_eq!(ctx, expected);
} else {
panic!("{:?}", res);
}
}

{
let ctx0 = ValidationContext::Empty;
let ctx1 = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let expected = ValidationContext::from(build_trusting_period_context(
1,
1,
datetime!(2023-08-20 0:00 UTC),
datetime!(2023-08-20 0:00 UTC),
));
let res = ctx0.aggregate(ctx1);
if let Ok(ctx) = res {
assert_eq!(ctx, expected);
} else {
panic!("{:?}", res);
}
}
}

#[test]
fn test_empty_context_aggregation() {
let ctx0 = ValidationContext::Empty;
let ctx1 = ValidationContext::Empty;
let res = ctx0.aggregate(ctx1);
if let Ok(ctx) = res {
assert_eq!(ctx, ValidationContext::Empty);
} else {
panic!("{:?}", res);
}
}
}
16 changes: 16 additions & 0 deletions modules/commitments/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,22 @@ define_error! {
format_args!("not truncated timestamp: timestamp_nanos={}", e.timestamp_nanos)
},

MessageAggregationFailed
{
descr: String
}
|e| {
format_args!("message aggregation failed: descr={}", e.descr)
},

ContextAggregationFailed
{
descr: String
}
|e| {
format_args!("context aggregation failed: descr={}", e.descr)
},

LcpType
{}
[lcp_types::TypeError]
Expand Down
3 changes: 2 additions & 1 deletion modules/commitments/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ pub use context::{TrustingPeriodContext, ValidationContext};
pub use encoder::EthABIEncoder;
pub use errors::Error;
pub use message::{
CommitmentPrefix, EmittedState, Message, UpdateClientMessage, VerifyMembershipMessage,
aggregate_messages, CommitmentPrefix, EmittedState, Message, UpdateClientMessage,
VerifyMembershipMessage,
};
pub use proof::CommitmentProof;
pub use prover::prove_commitment;
Expand Down
2 changes: 1 addition & 1 deletion modules/commitments/src/message.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pub use self::update_client::{EmittedState, UpdateClientMessage};
pub use self::update_client::{aggregate_messages, EmittedState, UpdateClientMessage};
pub use self::verify_membership::{CommitmentPrefix, VerifyMembershipMessage};
use crate::encoder::EthABIEncoder;
use crate::prelude::*;
Expand Down
Loading

0 comments on commit 3bd77b9

Please sign in to comment.