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}