use tezos_data_encoding::enc::BinWriter;
use tezos_data_encoding::encoding::HasEncoding;
use tezos_data_encoding::nom::NomReader;
use crate::contract::Contract;
use crate::entrypoint::Entrypoint;
use crate::michelson::Michelson;
#[derive(Debug, PartialEq, Eq, HasEncoding, NomReader, BinWriter)]
pub enum OutboxMessage<Expr: Michelson> {
AtomicTransactionBatch(OutboxMessageTransactionBatch<Expr>),
}
#[derive(Debug, PartialEq, Eq, HasEncoding, BinWriter, NomReader)]
pub struct OutboxMessageTransactionBatch<Expr: Michelson> {
#[encoding(dynamic, list)]
batch: Vec<OutboxMessageTransaction<Expr>>,
}
impl<Expr: Michelson> OutboxMessageTransactionBatch<Expr> {
pub fn len(&self) -> usize {
self.batch.len()
}
pub fn is_empty(&self) -> bool {
self.batch.is_empty()
}
}
impl<Expr: Michelson> core::ops::Index<usize> for OutboxMessageTransactionBatch<Expr> {
type Output = OutboxMessageTransaction<Expr>;
fn index(&self, index: usize) -> &Self::Output {
self.batch.index(index)
}
}
impl<Expr: Michelson> From<Vec<OutboxMessageTransaction<Expr>>>
for OutboxMessageTransactionBatch<Expr>
{
fn from(batch: Vec<OutboxMessageTransaction<Expr>>) -> Self {
Self { batch }
}
}
#[derive(Debug, PartialEq, Eq, HasEncoding, BinWriter, NomReader)]
pub struct OutboxMessageTransaction<Expr: Michelson> {
pub parameters: Expr,
pub destination: Contract,
pub entrypoint: Entrypoint,
}
#[cfg(test)]
mod test {
use crate::michelson::ticket::StringTicket;
use super::*;
const ENCODED_OUTBOX_MESSAGE_PREFIX: [u8; 5] = [0, 0, 0, 0, 152];
const ENCODED_TRANSACTION_ONE: [u8; 74] = [
7, 7, b'\n', 0, 0, 0, 22, 1, 209, 163, b'|', 8, 138, 18, b'!', 182, b'6', 187, b'_',
204, 179, b'^', 5, 24, 16, b'8', 186, b'|', 0, 7, 7, 1, 0, 0, 0, 3, b'r', b'e', b'd', 0, 1, 1, 36, 102, 103, 169, 49, 254, 11, 210, 251, 28, 182, 4, 247, 20, 96, 30, 136, 40,
69, 80, 0, 0, 0, 0, 7, b'd', b'e', b'f', b'a', b'u', b'l', b't',
];
const ENCODED_TRANSACTION_TWO: [u8; 78] = [
7, 7, b'\n', 0, 0, 0, 22, 1, b'$', b'f', b'g', 169, b'1', 254, 11, 210, 251, 28, 182, 4,
247, 20, b'`', 30, 136, b'(', b'E', b'P', 0, 7, 7, 1, 0, 0, 0, 6, b'y', b'e', b'l', b'l', b'o', b'w', 0, 137, 5, 1, 21, 237, 173, b'\'', 159, b'U', 226, 254, b'@', 17, 222, b'm', b',', b'$', 253,
245, 27, 242, b'%', 197, 0, 0, 0, 0, 7, b'a', b'n', b'o', b't', b'h', b'e', b'r', ];
#[test]
fn encode_transaction() {
let mut bin = vec![];
transaction_one().bin_write(&mut bin).unwrap();
assert_eq!(&ENCODED_TRANSACTION_ONE, bin.as_slice());
}
#[test]
fn decode_transaction() {
let (remaining, decoded) =
OutboxMessageTransaction::nom_read(ENCODED_TRANSACTION_TWO.as_slice())
.unwrap();
assert!(remaining.is_empty());
assert_eq!(transaction_two(), decoded);
}
#[test]
fn encode_outbox_message() {
let mut expected = ENCODED_OUTBOX_MESSAGE_PREFIX.to_vec();
expected.extend_from_slice(ENCODED_TRANSACTION_ONE.as_slice());
expected.extend_from_slice(ENCODED_TRANSACTION_TWO.as_slice());
let message = OutboxMessage::AtomicTransactionBatch(
vec![transaction_one(), transaction_two()].into(),
);
let mut bin = vec![];
message.bin_write(&mut bin).unwrap();
assert_eq!(expected, bin);
}
#[test]
fn decode_outbox_message() {
let mut bytes = ENCODED_OUTBOX_MESSAGE_PREFIX.to_vec();
bytes.extend_from_slice(ENCODED_TRANSACTION_TWO.as_slice());
bytes.extend_from_slice(ENCODED_TRANSACTION_ONE.as_slice());
let expected = OutboxMessage::AtomicTransactionBatch(
vec![transaction_two(), transaction_one()].into(),
);
let (remaining, message) = OutboxMessage::nom_read(bytes.as_slice()).unwrap();
assert!(remaining.is_empty());
assert_eq!(expected, message);
}
#[test]
fn decode_outbox_message_err_on_invalid_prefix() {
let mut bytes = ENCODED_OUTBOX_MESSAGE_PREFIX.to_vec();
bytes.extend_from_slice(ENCODED_TRANSACTION_ONE.as_slice());
bytes.extend_from_slice([10; 1000].as_slice());
assert!(matches!(
OutboxMessage::<StringTicket>::nom_read(bytes.as_slice()),
Err(_)
));
}
fn transaction_one() -> OutboxMessageTransaction<StringTicket> {
let ticket = StringTicket::new(
Contract::from_b58check("KT1ThEdxfUcWUwqsdergy3QnbCWGHSUHeHJq").unwrap(),
"red".to_string(),
1_u64,
)
.unwrap();
make_transaction(ticket, "KT1BuEZtb68c1Q4yjtckcNjGELqWt56Xyesc", "default")
}
fn transaction_two() -> OutboxMessageTransaction<StringTicket> {
let ticket = StringTicket::new(
Contract::from_b58check("KT1BuEZtb68c1Q4yjtckcNjGELqWt56Xyesc").unwrap(),
"yellow".to_string(),
329_u64,
)
.unwrap();
make_transaction(ticket, "KT1AaiUqbT3NmQts2w7ofY4vJviVchztiW4y", "another")
}
fn make_transaction(
ticket: StringTicket,
destination: &str,
entrypoint: &str,
) -> OutboxMessageTransaction<StringTicket> {
let parameters = ticket;
let destination = Contract::from_b58check(destination).unwrap();
let entrypoint = Entrypoint::try_from(entrypoint.to_string()).unwrap();
OutboxMessageTransaction {
parameters,
destination,
entrypoint,
}
}
}