use alloc::vec::Vec;
use core::fmt;
use crate::transaction::components::tze::{self, TzeOut};
use zcash_protocol::value::Zatoshis;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthData(pub Vec<u8>);
pub trait FromPayload: Sized {
type Error;
fn from_payload(mode: u32, payload: &[u8]) -> Result<Self, Self::Error>;
}
pub trait ToPayload {
fn to_payload(&self) -> (u32, Vec<u8>);
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Precondition {
pub extension_id: u32,
pub mode: u32,
pub payload: Vec<u8>,
}
impl Precondition {
pub fn from<P: ToPayload>(extension_id: u32, value: &P) -> Precondition {
let (mode, payload) = value.to_payload();
Precondition {
extension_id,
mode,
payload,
}
}
pub fn try_to<P: FromPayload>(&self) -> Result<P, P::Error> {
P::from_payload(self.mode, &self.payload)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Witness<T> {
pub extension_id: u32,
pub mode: u32,
pub payload: T,
}
impl<T> Witness<T> {
pub fn map_payload<U, F: FnOnce(T) -> U>(self, f: F) -> Witness<U> {
Witness {
extension_id: self.extension_id,
mode: self.mode,
payload: f(self.payload),
}
}
}
impl Witness<AuthData> {
pub fn from<P: ToPayload>(extension_id: u32, value: &P) -> Witness<AuthData> {
let (mode, payload) = value.to_payload();
Witness {
extension_id,
mode,
payload: AuthData(payload),
}
}
pub fn try_to<P: FromPayload>(&self) -> Result<P, P::Error> {
P::from_payload(self.mode, &self.payload.0)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Error<E> {
InvalidExtensionId(u32),
ProgramError(E),
}
impl<E: fmt::Display> fmt::Display for Error<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidExtensionId(extension_id) => {
write!(f, "Unrecognized program type id {}", extension_id)
}
Error::ProgramError(err) => write!(f, "Program error: {}", err),
}
}
}
pub trait Extension<C> {
type Precondition;
type Witness;
type Error;
fn verify_inner(
&self,
precondition: &Self::Precondition,
witness: &Self::Witness,
context: &C,
) -> Result<(), Self::Error>;
fn verify(
&self,
precondition: &Precondition,
witness: &Witness<AuthData>,
context: &C,
) -> Result<(), Self::Error>
where
Self::Precondition: FromPayload<Error = Self::Error>,
Self::Witness: FromPayload<Error = Self::Error>,
{
self.verify_inner(
&Self::Precondition::from_payload(precondition.mode, &precondition.payload)?,
&Self::Witness::from_payload(witness.mode, &witness.payload.0)?,
context,
)
}
}
pub trait ExtensionTxBuilder<'a> {
type BuildCtx;
type BuildError;
fn add_tze_input<WBuilder, W: ToPayload>(
&mut self,
extension_id: u32,
mode: u32,
prevout: (tze::OutPoint, TzeOut),
witness_builder: WBuilder,
) -> Result<(), Self::BuildError>
where
WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result<W, Self::BuildError>);
fn add_tze_output<Precondition: ToPayload>(
&mut self,
extension_id: u32,
value: Zatoshis,
guarded_by: &Precondition,
) -> Result<(), Self::BuildError>;
}