ironfish_primitives/extensions/
transparent.rs

1//! Core traits and structs for Transparent Zcash Extensions.
2
3use std::fmt;
4
5use crate::transaction::components::{
6    tze::{self, TzeOut},
7    Amount,
8};
9
10/// A typesafe wrapper for witness payloads
11#[derive(Debug, Clone, PartialEq)]
12pub struct AuthData(pub Vec<u8>);
13
14/// Binary parsing capability for TZE preconditions & witnesses.
15///
16/// Serialization formats interpreted by implementations of this trait become consensus-critical
17/// upon activation of of the extension that uses them.
18pub trait FromPayload: Sized {
19    type Error;
20
21    /// Parses an extension-specific witness or precondition from a mode and payload.
22    fn from_payload(mode: u32, payload: &[u8]) -> Result<Self, Self::Error>;
23}
24
25/// Binary serialization capability for TZE preconditions & witnesses.
26///
27/// Serialization formats used by implementations of this trait become consensus-critical upon
28/// activation of of the extension that uses them.
29pub trait ToPayload {
30    /// Returns a serialized payload and its corresponding mode.
31    fn to_payload(&self) -> (u32, Vec<u8>);
32}
33
34/// A condition that can be used to encumber transparent funds.
35///
36/// This struct is an intermediate representation between the serialized binary format which is
37/// used inside of a transaction, and extension-specific types. The payload field of this struct is
38/// treated as opaque to all but the extension corresponding to the encapsulated `extension_id`
39/// value.
40#[derive(Clone, Debug, PartialEq)]
41pub struct Precondition {
42    pub extension_id: u32,
43    pub mode: u32,
44    pub payload: Vec<u8>,
45}
46
47impl Precondition {
48    /// Produce the intermediate format for an extension-specific precondition
49    /// type.
50    pub fn from<P: ToPayload>(extension_id: u32, value: &P) -> Precondition {
51        let (mode, payload) = value.to_payload();
52        Precondition {
53            extension_id,
54            mode,
55            payload,
56        }
57    }
58
59    /// Attempt to parse an extension-specific precondition value from the
60    /// intermediate representation.
61    pub fn try_to<P: FromPayload>(&self) -> Result<P, P::Error> {
62        P::from_payload(self.mode, &self.payload)
63    }
64}
65
66/// Data that satisfies the precondition for prior encumbered funds, enabling them to be spent.
67///
68/// This struct is an intermediate representation between the serialized binary format which is
69/// used inside of a transaction, and extension-specific types. The payload field of this struct is
70/// treated as opaque to all but the extension corresponding to the encapsulated `extension_id`
71/// value.
72#[derive(Clone, Debug, PartialEq)]
73pub struct Witness<T> {
74    pub extension_id: u32,
75    pub mode: u32,
76    pub payload: T,
77}
78
79impl<T> Witness<T> {
80    pub fn map_payload<U, F: FnOnce(T) -> U>(self, f: F) -> Witness<U> {
81        Witness {
82            extension_id: self.extension_id,
83            mode: self.mode,
84            payload: f(self.payload),
85        }
86    }
87}
88
89impl Witness<AuthData> {
90    /// Produce the intermediate format for an extension-specific witness
91    /// type.
92    pub fn from<P: ToPayload>(extension_id: u32, value: &P) -> Witness<AuthData> {
93        let (mode, payload) = value.to_payload();
94        Witness {
95            extension_id,
96            mode,
97            payload: AuthData(payload),
98        }
99    }
100
101    /// Attempt to parse an extension-specific witness value from the
102    /// intermediate representation.
103    pub fn try_to<P: FromPayload>(&self) -> Result<P, P::Error> {
104        P::from_payload(self.mode, &self.payload.0)
105    }
106}
107
108#[derive(Debug, PartialEq)]
109pub enum Error<E> {
110    InvalidExtensionId(u32),
111    ProgramError(E),
112}
113
114impl<E: fmt::Display> fmt::Display for Error<E> {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        match self {
117            Error::InvalidExtensionId(extension_id) => {
118                write!(f, "Unrecognized program type id {}", extension_id)
119            }
120
121            Error::ProgramError(err) => write!(f, "Program error: {}", err),
122        }
123    }
124}
125
126/// This is the primary trait which must be implemented by an extension type for that type to be
127/// eligible for inclusion in Zcash consensus rules.
128pub trait Extension<C> {
129    /// Extension-specific precondition type. The extension will need to implement
130    /// [`FromPayload<Error = Self::Error>`] for this type in order for their extension to be
131    /// eligible for integration into consensus rules.
132    type Precondition;
133
134    /// Extension-specific witness type. The extension will need to implement [`FromPayload<Error =
135    /// Self::Error>`] for this type in order for their extension to be eligible for integration
136    /// into consensus rules.
137    type Witness;
138
139    /// Extension-specific error type. This should encompass both parsing and verification errors.
140    type Error;
141
142    /// This is the primary method that an extension must implement. Implementations should return
143    /// [`Ok(())`] if verification of the witness succeeds against the supplied precondition, and
144    /// an error in any other case.
145    fn verify_inner(
146        &self,
147        precondition: &Self::Precondition,
148        witness: &Self::Witness,
149        context: &C,
150    ) -> Result<(), Self::Error>;
151
152    /// This is a convenience method intended for use by consensus nodes at the integration point
153    /// to provide easy interoperation with the opaque, cross-extension `Precondition` and
154    /// `Witness` types.
155    fn verify(
156        &self,
157        precondition: &Precondition,
158        witness: &Witness<AuthData>,
159        context: &C,
160    ) -> Result<(), Self::Error>
161    where
162        Self::Precondition: FromPayload<Error = Self::Error>,
163        Self::Witness: FromPayload<Error = Self::Error>,
164    {
165        self.verify_inner(
166            &Self::Precondition::from_payload(precondition.mode, &precondition.payload)?,
167            &Self::Witness::from_payload(witness.mode, &witness.payload.0)?,
168            context,
169        )
170    }
171}
172
173/// An interface for transaction builders which support addition of TZE inputs and outputs.
174///
175/// This extension trait is satisfied by [`transaction::builder::Builder`]. It provides a minimal
176/// contract for interacting with the transaction builder, that extension library authors can use
177/// to add extension-specific builder traits that may be used to interact with the transaction
178/// builder. This may make it simpler for projects that include transaction-builder functionality
179/// to integrate with third-party extensions without those extensions being coupled to a particular
180/// transaction or builder representation.
181///
182/// [`transaction::builder::Builder`]: crate::transaction::builder::Builder
183pub trait ExtensionTxBuilder<'a> {
184    type BuildCtx;
185    type BuildError;
186
187    /// Adds a TZE input to the transaction by providing a witness to a precondition identified by a
188    /// prior outpoint.
189    ///
190    /// The `witness_builder` function allows the transaction builder to provide extra contextual
191    /// information from the transaction under construction to be used in the production of this
192    /// witness (for example, so that the witness may internally make commitments based upon this
193    /// information.) For the standard transaction builder, the value provided here is the
194    /// transaction under construction.
195    fn add_tze_input<WBuilder, W: ToPayload>(
196        &mut self,
197        extension_id: u32,
198        mode: u32,
199        prevout: (tze::OutPoint, TzeOut),
200        witness_builder: WBuilder,
201    ) -> Result<(), Self::BuildError>
202    where
203        WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result<W, Self::BuildError>);
204
205    /// Adds a TZE precondition to the transaction which must be satisfied by a future transaction's
206    /// witness in order to spend the specified `amount`.
207    fn add_tze_output<Precondition: ToPayload>(
208        &mut self,
209        extension_id: u32,
210        value: Amount,
211        guarded_by: &Precondition,
212    ) -> Result<(), Self::BuildError>;
213}