mod await_;
pub use await_::*;
mod branch;
pub use branch::*;
mod contains;
pub use contains::*;
mod get;
pub use get::*;
mod get_or_use;
pub use get_or_use::*;
mod rand_chacha;
pub use crate::command::rand_chacha::*;
mod remove;
pub use remove::*;
mod position;
pub use position::*;
mod set;
pub use set::*;
use crate::{
traits::{
CommandTrait,
FinalizeStoreTrait,
InstructionTrait,
RegistersLoad,
RegistersStore,
StackMatches,
StackProgram,
},
CastType,
FinalizeOperation,
FinalizeRegistersState,
Instruction,
};
use console::{
network::prelude::*,
program::{Identifier, Register},
};
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum Command<N: Network> {
Instruction(Instruction<N>),
Await(Await<N>),
Contains(Contains<N>),
Get(Get<N>),
GetOrUse(GetOrUse<N>),
RandChaCha(RandChaCha<N>),
Remove(Remove<N>),
Set(Set<N>),
BranchEq(BranchEq<N>),
BranchNeq(BranchNeq<N>),
Position(Position<N>),
}
impl<N: Network> CommandTrait<N> for Command<N> {
#[inline]
fn destinations(&self) -> Vec<Register<N>> {
match self {
Command::Instruction(instruction) => instruction.destinations(),
Command::Contains(contains) => vec![contains.destination().clone()],
Command::Get(get) => vec![get.destination().clone()],
Command::GetOrUse(get_or_use) => vec![get_or_use.destination().clone()],
Command::RandChaCha(rand_chacha) => vec![rand_chacha.destination().clone()],
Command::Await(_)
| Command::BranchEq(_)
| Command::BranchNeq(_)
| Command::Position(_)
| Command::Remove(_)
| Command::Set(_) => vec![],
}
}
#[inline]
fn branch_to(&self) -> Option<&Identifier<N>> {
match self {
Command::BranchEq(branch_eq) => Some(branch_eq.position()),
Command::BranchNeq(branch_neq) => Some(branch_neq.position()),
_ => None,
}
}
#[inline]
fn position(&self) -> Option<&Identifier<N>> {
match self {
Command::Position(position) => Some(position.name()),
_ => None,
}
}
#[inline]
fn is_call(&self) -> bool {
matches!(self, Command::Instruction(Instruction::Call(_)))
}
fn is_cast_to_record(&self) -> bool {
matches!(self, Command::Instruction(Instruction::Cast(cast)) if matches!(cast.cast_type(), CastType::Record(_) | CastType::ExternalRecord(_)))
}
#[inline]
fn is_write(&self) -> bool {
matches!(self, Command::Set(_) | Command::Remove(_))
}
}
impl<N: Network> Command<N> {
#[inline]
pub fn finalize(
&self,
stack: &(impl StackMatches<N> + StackProgram<N>),
store: &impl FinalizeStoreTrait<N>,
registers: &mut (impl RegistersLoad<N> + RegistersStore<N> + FinalizeRegistersState<N>),
) -> Result<Option<FinalizeOperation<N>>> {
match self {
Command::Instruction(instruction) => instruction.finalize(stack, registers).map(|_| None),
Command::Await(_) => bail!("`await` commands cannot be finalized directly."),
Command::Contains(contains) => contains.finalize(stack, store, registers).map(|_| None),
Command::Get(get) => get.finalize(stack, store, registers).map(|_| None),
Command::GetOrUse(get_or_use) => get_or_use.finalize(stack, store, registers).map(|_| None),
Command::RandChaCha(rand_chacha) => rand_chacha.finalize(stack, registers).map(|_| None),
Command::Remove(remove) => remove.finalize(stack, store, registers),
Command::Set(set) => set.finalize(stack, store, registers).map(Some),
Command::BranchEq(_) | Command::BranchNeq(_) => {
bail!("`branch` commands cannot be finalized directly.")
}
Command::Position(position) => position.finalize().map(|_| None),
}
}
}
impl<N: Network> FromBytes for Command<N> {
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
let variant = u8::read_le(&mut reader)?;
match variant {
0 => Ok(Self::Instruction(Instruction::read_le(&mut reader)?)),
1 => Ok(Self::Await(Await::read_le(&mut reader)?)),
2 => Ok(Self::Contains(Contains::read_le(&mut reader)?)),
3 => Ok(Self::Get(Get::read_le(&mut reader)?)),
4 => Ok(Self::GetOrUse(GetOrUse::read_le(&mut reader)?)),
5 => Ok(Self::RandChaCha(RandChaCha::read_le(&mut reader)?)),
6 => Ok(Self::Remove(Remove::read_le(&mut reader)?)),
7 => Ok(Self::Set(Set::read_le(&mut reader)?)),
8 => Ok(Self::BranchEq(BranchEq::read_le(&mut reader)?)),
9 => Ok(Self::BranchNeq(BranchNeq::read_le(&mut reader)?)),
10 => Ok(Self::Position(Position::read_le(&mut reader)?)),
11.. => Err(error(format!("Invalid command variant: {variant}"))),
}
}
}
impl<N: Network> ToBytes for Command<N> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
match self {
Self::Instruction(instruction) => {
0u8.write_le(&mut writer)?;
instruction.write_le(&mut writer)
}
Self::Await(await_) => {
1u8.write_le(&mut writer)?;
await_.write_le(&mut writer)
}
Self::Contains(contains) => {
2u8.write_le(&mut writer)?;
contains.write_le(&mut writer)
}
Self::Get(get) => {
3u8.write_le(&mut writer)?;
get.write_le(&mut writer)
}
Self::GetOrUse(get_or_use) => {
4u8.write_le(&mut writer)?;
get_or_use.write_le(&mut writer)
}
Self::RandChaCha(rand_chacha) => {
5u8.write_le(&mut writer)?;
rand_chacha.write_le(&mut writer)
}
Self::Remove(remove) => {
6u8.write_le(&mut writer)?;
remove.write_le(&mut writer)
}
Self::Set(set) => {
7u8.write_le(&mut writer)?;
set.write_le(&mut writer)
}
Self::BranchEq(branch_eq) => {
8u8.write_le(&mut writer)?;
branch_eq.write_le(&mut writer)
}
Self::BranchNeq(branch_neq) => {
9u8.write_le(&mut writer)?;
branch_neq.write_le(&mut writer)
}
Self::Position(position) => {
10u8.write_le(&mut writer)?;
position.write_le(&mut writer)
}
}
}
}
impl<N: Network> Parser for Command<N> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
alt((
map(Await::parse, |await_| Self::Await(await_)),
map(Contains::parse, |contains| Self::Contains(contains)),
map(GetOrUse::parse, |get_or_use| Self::GetOrUse(get_or_use)),
map(Get::parse, |get| Self::Get(get)),
map(RandChaCha::parse, |rand_chacha| Self::RandChaCha(rand_chacha)),
map(Remove::parse, |remove| Self::Remove(remove)),
map(Set::parse, |set| Self::Set(set)),
map(BranchEq::parse, |branch_eq| Self::BranchEq(branch_eq)),
map(BranchNeq::parse, |branch_neq| Self::BranchNeq(branch_neq)),
map(Position::parse, |position| Self::Position(position)),
map(Instruction::parse, |instruction| Self::Instruction(instruction)),
))(string)
}
}
impl<N: Network> FromStr for Command<N> {
type Err = Error;
#[inline]
fn from_str(string: &str) -> Result<Self> {
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
impl<N: Network> Debug for Command<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network> Display for Command<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::Instruction(instruction) => Display::fmt(instruction, f),
Self::Await(await_) => Display::fmt(await_, f),
Self::Contains(contains) => Display::fmt(contains, f),
Self::Get(get) => Display::fmt(get, f),
Self::GetOrUse(get_or_use) => Display::fmt(get_or_use, f),
Self::RandChaCha(rand_chacha) => Display::fmt(rand_chacha, f),
Self::Remove(remove) => Display::fmt(remove, f),
Self::Set(set) => Display::fmt(set, f),
Self::BranchEq(branch_eq) => Display::fmt(branch_eq, f),
Self::BranchNeq(branch_neq) => Display::fmt(branch_neq, f),
Self::Position(position) => Display::fmt(position, f),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use console::network::Testnet3;
type CurrentNetwork = Testnet3;
#[test]
fn test_command_bytes() {
let expected = "decrement object[r0] by r1;";
Command::<CurrentNetwork>::parse(expected).unwrap_err();
let expected = "add r0 r1 into r2;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "increment object[r0] by r1;";
Command::<CurrentNetwork>::parse(expected).unwrap_err();
let expected = "await r1;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "contains object[r0] into r1;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "get object[r0] into r1;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "get.or_use object[r0] r1 into r2;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "rand.chacha into r1 as field;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "rand.chacha r0 r1 into r2 as group;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "remove object[r0];";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "set r0 into object[r1];";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "branch.eq r0 r1 to exit;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "branch.neq r2 r3 to start;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
let expected = "position exit;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
let bytes = command.to_bytes_le().unwrap();
assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
}
#[test]
fn test_command_parse() {
let expected = "decrement object[r0] by r1;";
Command::<CurrentNetwork>::parse(expected).unwrap_err();
let expected = "add r0 r1 into r2;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Instruction(Instruction::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "increment object[r0] by r1;";
Command::<CurrentNetwork>::parse(expected).unwrap_err();
let expected = "contains object[r0] into r1;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Contains(Contains::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "get object[r0] into r1;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Get(Get::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "get.or_use object[r0] r1 into r2;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::GetOrUse(GetOrUse::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "rand.chacha into r1 as field;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::RandChaCha(RandChaCha::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "rand.chacha r0 r1 into r2 as group;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::RandChaCha(RandChaCha::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "remove object[r0];";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Remove(Remove::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "set r0 into object[r1];";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Set(Set::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "branch.eq r0 r1 to exit;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::BranchEq(BranchEq::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "branch.neq r2 r3 to start;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::BranchNeq(BranchNeq::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
let expected = "position exit;";
let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
assert_eq!(Command::Position(Position::from_str(expected).unwrap()), command);
assert_eq!(expected, command.to_string());
}
}