message.rs
// SPDX-FileCopyrightText: 2023 Marigold <contact@marigold.dev>
//
// SPDX-License-Identifier: MIT
use nom::{
branch::alt,
bytes::complete::tag,
combinator::{all_consuming, map},
sequence::preceded,
};
#[cfg(test)]
use tezos_crypto_rs::hash::SecretKeyEd25519;
use tezos_crypto_rs::PublicKeySignatureVerifier;
use tezos_crypto_rs::{blake2b, hash::Signature};
use tezos_data_encoding::{
enc::{self, BinResult, BinWriter},
nom::{NomReader, NomResult},
};
use tezos_smart_rollup_encoding::public_key::PublicKey;
use tezos_smart_rollup_encoding::smart_rollup::SmartRollupAddress;
use tezos_smart_rollup_host::runtime::RuntimeError;
#[derive(Debug, Clone, PartialEq, Eq, BinWriter, NomReader)]
pub struct UnverifiedSigned<A>
where
A: NomReader + BinWriter,
{
body: A,
signature: Signature,
}
impl<A> UnverifiedSigned<A>
where
A: NomReader + BinWriter,
{
/// Returns the hash of the body.
fn hash_body(body: &A) -> Result<Vec<u8>, RuntimeError> {
// Get the bytes of the body.
let mut bytes = Vec::new();
body.bin_write(&mut bytes)
.map_err(|_| RuntimeError::DecodingError)?;
// Returns the hash of the body.
blake2b::digest_256(&bytes).map_err(|_| RuntimeError::DecodingError)
}
/// Returns the hash of the signed message.
///
/// It's equivalent of the body's hash.
pub fn hash(&self) -> Result<Vec<u8>, RuntimeError> {
UnverifiedSigned::hash_body(&self.body)
}
/// Returns the body of the message and verifies the signature.
pub fn body(self, public_key: &PublicKey) -> Result<A, RuntimeError> {
let hash = &self.hash()?;
let is_correct = public_key.verify_signature(&self.signature, hash);
match is_correct {
Ok(true) => Ok(self.body),
_ => Err(RuntimeError::DecodingError),
}
}
/// Return a signed body
#[cfg(test)]
fn sign_ed25519(body: A, secret: &SecretKeyEd25519) -> Result<Self, RuntimeError> {
use tezos_smart_rollup_host::Error;
let hash = UnverifiedSigned::hash_body(&body)?;
let signature = secret
.sign(hash)
.map_err(|_| RuntimeError::HostErr(Error::GenericInvalidAccess))?;
Ok(UnverifiedSigned { body, signature })
}
}
/// Framing protocol v0
///
/// The framing protocol starts with a 0, then the address of the rollup, then the message
/// The message should start by a tag, provided by the Tag trait
///
/// [0x00, smart rollup address, tag, message]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Framed<P> {
pub destination: SmartRollupAddress,
pub payload: P,
}
/// Messages sent by the user to the sequencer
#[derive(NomReader, BinWriter, Clone, Debug, PartialEq, Eq)]
pub struct Bytes {
#[encoding(dynamic, list)]
inner: Vec<u8>,
}
/// Sequence of messages sent by the sequencer
///
/// The sequence contains the number of messages
/// that should be processed from the delayed inbox
/// and the messages from the sequencer
///
/// The number of messages that should be processed
/// from the delayed inbox is divided into two parts
/// delayed_messages_prefix and delayed_messages_suffix
///
/// Then the messages are processed in the following order:
/// First, the number specified by `delayed_messages_prefix` will be
/// processed from the delayed inbox.
/// Then the `l2_messages` will be processed
/// Finally, the number specified by `delayed delayed_messages_suffix`
/// will be processed at the end
#[derive(NomReader, BinWriter, Clone, Debug, PartialEq, Eq)]
pub struct Sequence {
nonce: u32,
delayed_messages_prefix: u32,
delayed_messages_suffix: u32,
#[encoding(dynamic, list)]
messages: Vec<Bytes>,
}
/// Message to set the appropriate sequencer
///
/// This message should be sent by the admin public key
/// This admin key should sign the new sequencer public key
#[derive(NomReader, BinWriter, Clone, Debug, PartialEq, Eq)]
pub struct SetSequencer {
nonce: u32,
admin_public_key: PublicKey,
sequencer_public_key: PublicKey,
}
#[derive(NomReader, BinWriter, Debug, Clone, Eq, PartialEq)]
pub enum SequencerMsg {
Sequence(Sequence),
SetSequencer(SetSequencer),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KernelMessage {
Sequencer(UnverifiedSigned<Framed<SequencerMsg>>),
DelayedMessage(Vec<u8>),
}
impl<P> NomReader for Framed<P>
where
P: NomReader,
{
fn nom_read(input: &[u8]) -> NomResult<Self> {
// Extract the rollup address from the framing protocol
// 0x00 is the version of the framing protocol
let (input, destination) = preceded(tag([0]), SmartRollupAddress::nom_read)(input)?;
// Extract the payload
let (remaining, payload) = P::nom_read(input)?;
Ok((
remaining,
Framed {
destination,
payload,
},
))
}
}
impl<P> BinWriter for Framed<P>
where
P: BinWriter,
{
fn bin_write(&self, output: &mut Vec<u8>) -> BinResult {
// bytes of the framing protocol
// 0x00 is the version of the framing protocol
enc::put_byte(&0x00, output);
// bytes of the rollup address
self.destination.bin_write(output)?;
// bytes of the payload
self.payload.bin_write(output)
}
}
impl NomReader for KernelMessage {
fn nom_read(input: &[u8]) -> NomResult<Self> {
all_consuming(alt((
all_consuming(map(
preceded(tag([1]), UnverifiedSigned::<Framed<SequencerMsg>>::nom_read),
KernelMessage::Sequencer,
)),
map(
|bytes: &[u8]| Ok(([].as_slice(), bytes.to_vec())),
KernelMessage::DelayedMessage,
),
)))(input)
}
}
impl BinWriter for KernelMessage {
fn bin_write(&self, output: &mut Vec<u8>) -> enc::BinResult {
match self {
KernelMessage::Sequencer(sequencer_framed_msg) => {
// external message tag
enc::put_byte(&0x01, output);
sequencer_framed_msg.bin_write(output)?;
}
KernelMessage::DelayedMessage(message) => enc::put_bytes(message, output),
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::message::{Bytes, Framed, SequencerMsg, UnverifiedSigned};
use super::{KernelMessage, Sequence};
use crate::message::SetSequencer;
use tezos_crypto_rs::hash::{PublicKeyEd25519, SecretKeyEd25519, SeedEd25519, Signature};
use tezos_crypto_rs::PublicKeySignatureVerifier;
use tezos_data_encoding::enc::{self, BinWriter};
use tezos_data_encoding::nom::NomReader;
use tezos_smart_rollup_encoding::public_key::PublicKey;
use tezos_smart_rollup_encoding::smart_rollup::SmartRollupAddress;
/// Generate a public key and a secret key
fn key_pair(seed: &str) -> (PublicKey, SecretKeyEd25519) {
let (public_key, secret) = SeedEd25519::from_base58_check(seed)
.expect("seed parsing should work")
.keypair()
.expect("make key pair should work");
let public_key = PublicKey::Ed25519(public_key);
(public_key, secret)
}
#[test]
fn test_sequence_serialization() {
let (_, secret) = key_pair("edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB");
let signature = secret.sign([0x0]).expect("sign should work");
let body = Framed {
destination: SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb")
.expect("decoding should work"),
payload: SequencerMsg::Sequence(Sequence {
nonce: 0,
delayed_messages_prefix: 0,
delayed_messages_suffix: 0,
messages: Vec::default(),
}),
};
let sequence = KernelMessage::Sequencer(UnverifiedSigned { body, signature });
// Serializing
let mut bin: Vec<u8> = Vec::new();
sequence.bin_write(&mut bin).unwrap();
// Deserializing
let (_, msg_read) = KernelMessage::nom_read(&bin).expect("deserialization should work");
assert_eq!(msg_read, sequence);
}
#[test]
fn test_set_sequencer_serialization() {
let (public_key, secret) =
key_pair("edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB");
let signature = secret.sign([0x0]).expect("sign should work");
let body = Framed {
destination: SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb")
.expect("decoding should work"),
payload: SequencerMsg::SetSequencer(SetSequencer {
nonce: 0,
admin_public_key: public_key.clone(),
sequencer_public_key: public_key,
}),
};
let sequence = KernelMessage::Sequencer(UnverifiedSigned { body, signature });
// Serializing
let mut bin: Vec<u8> = Vec::new();
sequence.bin_write(&mut bin).unwrap();
// Deserializing
let (_, msg_read) = KernelMessage::nom_read(&bin).expect("deserialization should work");
assert_eq!(msg_read, sequence);
}
#[test]
fn test_user_message_serialization() {
let sequence = KernelMessage::DelayedMessage(vec![0x01, 0x0, 0x01, 0x02, 0x02]);
// Serializing
let mut bin: Vec<u8> = Vec::new();
sequence.bin_write(&mut bin).unwrap();
// Deserializing
let (_, msg_read) = KernelMessage::nom_read(&bin).expect("deserialization should work");
assert_eq!(msg_read, sequence);
}
#[test]
fn test_message_default() {
let (_, secret) = key_pair("edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB");
let signature = secret.sign([0x0]).expect("sign should work");
let body = Framed {
destination: SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb")
.expect("decoding should work"),
payload: SequencerMsg::Sequence(Sequence {
nonce: 0,
delayed_messages_prefix: 5,
delayed_messages_suffix: 5,
messages: Vec::default(),
}),
};
let sequence = KernelMessage::Sequencer(UnverifiedSigned { body, signature });
// Serializing
let mut bin: Vec<u8> = Vec::new();
sequence.bin_write(&mut bin).unwrap();
enc::put_bytes(&[0x01, 0x02, 0x03, 0x04], &mut bin);
// Deserializing
let (remaining, msg_read) =
KernelMessage::nom_read(&bin).expect("deserialization should work");
assert!(remaining.is_empty());
assert_eq!(msg_read, KernelMessage::DelayedMessage(bin))
}
/// Deserialize a string to a KernelMessage
fn from_string(hex: &str) -> KernelMessage {
let hex = hex::decode(hex).expect("valid hexadecimal");
let (_, msg) = KernelMessage::nom_read(&hex).expect("deserialization should work");
msg
}
#[test]
fn test_deserialization_empty_sequence() {
let kernel_message = from_string("01006227a8721213bd7ddb9b56227e3acb01161b1e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
let rollup_address =
SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap();
let sign = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap();
match kernel_message {
KernelMessage::Sequencer(UnverifiedSigned {
body:
Framed {
destination,
payload:
SequencerMsg::Sequence(Sequence {
nonce,
delayed_messages_prefix,
delayed_messages_suffix,
messages,
}),
},
signature,
}) => {
assert_eq!(destination, rollup_address);
assert_eq!(nonce, 0);
assert_eq!(delayed_messages_prefix, 0);
assert_eq!(delayed_messages_suffix, 0);
assert_eq!(messages, vec![]);
assert_eq!(signature, sign)
}
_ => panic!("Wrong message encoding"),
}
}
#[test]
fn test_deserialization_one_empty_message_sequence() {
let kernel_message = from_string("01006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
let destination =
SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap();
let signature = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap();
let expected = KernelMessage::Sequencer(UnverifiedSigned {
body: Framed {
destination,
payload: SequencerMsg::Sequence(Sequence {
nonce: 0,
delayed_messages_prefix: 0,
delayed_messages_suffix: 0,
messages: vec![Bytes {
inner: b"".to_vec(),
}],
}),
},
signature,
});
assert_eq!(kernel_message, expected);
}
#[test]
fn test_deserialization_one_message_sequence() {
let kernel_message = from_string("01006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000090000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
let destination =
SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap();
let signature = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap();
let expected = KernelMessage::Sequencer(UnverifiedSigned {
body: Framed {
destination,
payload: SequencerMsg::Sequence(Sequence {
nonce: 0,
delayed_messages_prefix: 0,
delayed_messages_suffix: 0,
messages: vec![Bytes {
inner: b"hello".to_vec(),
}],
}),
},
signature,
});
assert_eq!(kernel_message, expected);
}
#[test]
fn test_deserialization_two_messages_sequence() {
let kernel_message = from_string("01006227a8721213bd7ddb9b56227e3acb01161b1e6700000000000000000000000000000000120000000568656c6c6f00000005776f726c6400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
let destination =
SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap();
let signature = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap();
let expected = KernelMessage::Sequencer(UnverifiedSigned {
body: Framed {
destination,
payload: SequencerMsg::Sequence(Sequence {
nonce: 0,
delayed_messages_prefix: 0,
delayed_messages_suffix: 0,
messages: vec![
Bytes {
inner: b"hello".to_vec(),
},
Bytes {
inner: b"world".to_vec(),
},
],
}),
},
signature,
});
assert_eq!(kernel_message, expected);
}
#[test]
fn test_incorrect_sequence_signature() {
let destination =
SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap();
let nonce = 0;
let delayed_messages_prefix = 0;
let delayed_messages_suffix = 0;
let messages = vec![];
let signature = Signature::from_base58_check("edsigtXomBKi5CTRf5cjATJWSyaRvhfYNHqSUGrn4SdbYRcGwQrUGjzEfQDTuqHhuA8b2d8NarZjz8TRf65WkpQmo423BtomS8Q").unwrap();
let message = UnverifiedSigned {
body: Framed {
destination,
payload: SequencerMsg::Sequence(Sequence {
nonce,
delayed_messages_prefix,
delayed_messages_suffix,
messages,
}),
},
signature: signature.clone(),
};
let hash = message.hash().expect("hash failure");
let public_key = PublicKey::Ed25519(
PublicKeyEd25519::from_base58_check(
"edpkuDMUm7Y53wp4gxeLBXuiAhXZrLn8XB1R83ksvvesH8Lp8bmCfK",
)
.unwrap(),
);
let is_correct = public_key.verify_signature(&signature, &hash);
assert!(matches!(is_correct, Ok(false) | Err(_)));
}
#[test]
fn test_correct_sequence_signature() {
let destination =
SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap();
let seed = SeedEd25519::from_base58_check(
"edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB",
)
.unwrap();
let (public_key, secret_key) = seed.keypair().unwrap();
let body = Framed {
destination,
payload: SequencerMsg::Sequence(Sequence {
nonce: 0,
delayed_messages_prefix: 0,
delayed_messages_suffix: 0,
messages: vec![],
}),
};
let sequence =
UnverifiedSigned::sign_ed25519(body, &secret_key).expect("error when signing body");
let hash = sequence.hash().unwrap();
let is_correct = public_key.verify_signature(&sequence.signature, &hash);
assert!(matches!(is_correct, Ok(true)));
}
#[test]
fn test_correct_non_empty_sequence_signature() {
let destination =
SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap();
let seed = SeedEd25519::from_base58_check(
"edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB",
)
.unwrap();
let (public_key, secret_key) = seed.keypair().unwrap();
let body = Framed {
destination,
payload: SequencerMsg::Sequence(Sequence {
nonce: 0,
delayed_messages_prefix: 0,
delayed_messages_suffix: 0,
messages: vec![Bytes {
inner: b"hello".to_vec(),
}],
}),
};
let sequence =
UnverifiedSigned::sign_ed25519(body, &secret_key).expect("error when signing body");
let hash = sequence.hash().unwrap();
let is_correct = public_key.verify_signature(&sequence.signature, &hash);
assert!(matches!(is_correct, Ok(true)));
}
#[test]
fn test_correct_signature_encoding() {
let signed_msg = hex::decode("006227a8721213bd7ddb9b56227e3acb01161b1e67000000000000000000000000000000000f0000000b68656c6c6f20776f726c64bc682e5a009f3ee1dc1280d6b538aa53c626c29c5d5c528c8dd92092915aa24d414cc4f7ac81be8a4eb6a6046f9beea09c3c5a54374d68e6bfbad6cb51edc306").unwrap();
let (_, signed_msg): (&[u8], UnverifiedSigned<Framed<SequencerMsg>>) =
UnverifiedSigned::nom_read(&signed_msg).unwrap();
let destination =
SmartRollupAddress::from_b58check("sr1EzLeJYWrvch2Mhvrk1nUVYrnjGQ8A4qdb").unwrap();
let seed = SeedEd25519::from_base58_check(
"edsk3a5SDDdMWw3Q5hPiJwDXUosmZMTuKQkriPqY6UqtSfdLifpZbB",
)
.unwrap();
let (public_key, _) = seed.keypair().unwrap();
let body = Framed {
destination,
payload: SequencerMsg::Sequence(Sequence {
nonce: 0,
delayed_messages_prefix: 0,
delayed_messages_suffix: 0,
messages: vec![Bytes {
inner: b"hello world".to_vec(),
}],
}),
};
let hash = UnverifiedSigned::hash_body(&body).unwrap();
let is_correct = public_key.verify_signature(&signed_msg.signature, &hash);
assert!(matches!(is_correct, Ok(true)));
}
}