diff --git a/base_layer/core/src/covenants/arguments.rs b/base_layer/core/src/covenants/arguments.rs index f48fdbba4f..f28164b7e5 100644 --- a/base_layer/core/src/covenants/arguments.rs +++ b/base_layer/core/src/covenants/arguments.rs @@ -52,6 +52,7 @@ const MAX_COVENANT_ARG_SIZE: usize = 4096; const MAX_BYTES_ARG_SIZE: usize = 4096; #[derive(Debug, Clone, PartialEq, Eq)] +/// Covenant arguments pub enum CovenantArg { Hash(FixedHash), PublicKey(PublicKey), @@ -66,10 +67,12 @@ pub enum CovenantArg { } impl CovenantArg { + /// Checks if a stream of bytes results in valid argument code pub fn is_valid_code(code: u8) -> bool { byte_codes::is_valid_arg_code(code) } + /// Reads a `CovenantArg` from a buffer of bytes pub fn read_from(reader: &mut &[u8], code: u8) -> Result { use byte_codes::*; match code { @@ -126,6 +129,7 @@ impl CovenantArg { } } + /// Parses the `CovenantArg` data to bytes and writes it to an IO writer pub fn write_to(&self, writer: &mut W) -> Result<(), io::Error> { use byte_codes::*; #[allow(clippy::enum_glob_use)] @@ -180,8 +184,17 @@ impl CovenantArg { } } +/// `require_x_impl!` is a helper macro that generates an implementation of a function with a specific signature +/// based on the provided input parameters. Functionality: +/// The macro expects to receive either three or four arguments. +/// $name, represents the name of the function to be generated. +/// $output, represents the name of the enum variant that the function will match against. +/// $expected, represents an expression that will be used in the error message when the provided argument +/// does not match the expected variant. +/// (optional) $output_type, represents the type that the function will return. If +/// not provided, it defaults to the same as $output. macro_rules! require_x_impl { - ($name:ident, $output:ident, $expected: expr, $output_type:ident) => { + ($name:ident, $output:ident, $expected: expr, $output_type:ty) => { #[allow(dead_code)] pub(super) fn $name(self) -> Result<$output_type, CovenantError> { match self { @@ -215,25 +228,9 @@ impl CovenantArg { require_x_impl!(require_outputfields, OutputFields, "outputfields"); - pub fn require_bytes(self) -> Result, CovenantError> { - match self { - CovenantArg::Bytes(val) => Ok(val), - got => Err(CovenantError::UnexpectedArgument { - expected: "bytes", - got: got.to_string(), - }), - } - } + require_x_impl!(require_bytes, Bytes, "bytes", Vec); - pub fn require_uint(self) -> Result { - match self { - CovenantArg::Uint(val) => Ok(val), - got => Err(CovenantError::UnexpectedArgument { - expected: "uint", - got: got.to_string(), - }), - } - } + require_x_impl!(require_uint, Uint, "u64", u64); } impl Display for CovenantArg { diff --git a/base_layer/core/src/covenants/byte_codes.rs b/base_layer/core/src/covenants/byte_codes.rs index 686b4af0df..23b9f2a475 100644 --- a/base_layer/core/src/covenants/byte_codes.rs +++ b/base_layer/core/src/covenants/byte_codes.rs @@ -25,6 +25,7 @@ pub(super) fn is_valid_arg_code(code: u8) -> bool { ALL_ARGS.contains(&code) } +/// Array with all possible covenant arg byte codes. pub(super) const ALL_ARGS: [u8; 10] = [ ARG_HASH, ARG_PUBLIC_KEY, @@ -38,23 +39,34 @@ pub(super) const ALL_ARGS: [u8; 10] = [ ARG_OUTPUT_TYPE, ]; +/// Covenant arg hash byte code. pub const ARG_HASH: u8 = 0x01; +/// Covenant arg public key byte code. pub const ARG_PUBLIC_KEY: u8 = 0x02; +/// Covenant arg commitment byte code. pub const ARG_COMMITMENT: u8 = 0x03; +/// Covenant arg tari script byte code. pub const ARG_TARI_SCRIPT: u8 = 0x04; +/// Covenant arg covenant byte code. pub const ARG_COVENANT: u8 = 0x05; +/// Covenant arg uint byte code. pub const ARG_UINT: u8 = 0x06; +/// Covenant arg output field byte code. pub const ARG_OUTPUT_FIELD: u8 = 0x07; +/// Covenant arg output fields byte code. pub const ARG_OUTPUT_FIELDS: u8 = 0x08; +/// Covenant arg bytes byte code. pub const ARG_BYTES: u8 = 0x09; +/// Covenant arg output type byte code. pub const ARG_OUTPUT_TYPE: u8 = 0x0a; //---------------------------------- FILTER byte codes --------------------------------------------// - +/// Checks if a byte value results in a valid argument byte code pub(super) fn is_valid_filter_code(code: u8) -> bool { ALL_FILTERS.contains(&code) } +/// Array with all possible covenant filter bytecodes. pub(super) const ALL_FILTERS: [u8; 10] = [ FILTER_IDENTITY, FILTER_AND, @@ -68,28 +80,48 @@ pub(super) const ALL_FILTERS: [u8; 10] = [ FILTER_ABSOLUTE_HEIGHT, ]; +/// Identity filter. pub const FILTER_IDENTITY: u8 = 0x20; +/// And filter. pub const FILTER_AND: u8 = 0x21; +/// Or filter. pub const FILTER_OR: u8 = 0x22; +/// Xor Filter. pub const FILTER_XOR: u8 = 0x23; +/// Not filter. pub const FILTER_NOT: u8 = 0x24; +/// Output hash equality filter. pub const FILTER_OUTPUT_HASH_EQ: u8 = 0x30; +/// Fields preserved filter. pub const FILTER_FIELDS_PRESERVED: u8 = 0x31; +/// Fields hashed equality filter. pub const FILTER_FIELDS_HASHED_EQ: u8 = 0x32; +/// Field equality filter. pub const FILTER_FIELD_EQ: u8 = 0x33; +/// Absolute height filter. pub const FILTER_ABSOLUTE_HEIGHT: u8 = 0x34; //---------------------------------- FIELD byte codes --------------------------------------------// +/// Field commitment. pub const FIELD_COMMITMENT: u8 = 0x00; +/// Field script. pub const FIELD_SCRIPT: u8 = 0x01; +/// Field sender offset public key. pub const FIELD_SENDER_OFFSET_PUBLIC_KEY: u8 = 0x02; +/// Field covenant. pub const FIELD_COVENANT: u8 = 0x03; +/// Field features. pub const FIELD_FEATURES: u8 = 0x04; +/// Field features output type. pub const FIELD_FEATURES_OUTPUT_TYPE: u8 = 0x05; +/// Field features maturity. pub const FIELD_FEATURES_MATURITY: u8 = 0x06; +/// Field features side chain features. pub const FIELD_FEATURES_SIDE_CHAIN_FEATURES: u8 = 0x07; +/// Field features range proof type. pub const FIELD_FEATURES_RANGE_PROOF_TYPE: u8 = 0x08; +/// Field minimum value promise. pub const MINIMUM_VALUE_PROMISE: u8 = 0x09; #[cfg(test)] diff --git a/base_layer/core/src/covenants/context.rs b/base_layer/core/src/covenants/context.rs index 44b558ec93..6069bbf809 100644 --- a/base_layer/core/src/covenants/context.rs +++ b/base_layer/core/src/covenants/context.rs @@ -30,6 +30,8 @@ use crate::{ transactions::transaction_components::TransactionInput, }; +/// The covenant execution context provides a reference to the transaction input being verified, the tokenized covenant +/// and other relevant context e.g current block height pub struct CovenantContext<'a> { input: &'a TransactionInput, tokens: CovenantTokenCollection, @@ -45,10 +47,12 @@ impl<'a> CovenantContext<'a> { } } + /// Returns true if there are more tokens to consume, otherwise false pub fn has_more_tokens(&self) -> bool { !self.tokens.is_empty() } + /// Outputs the next token argument pub fn next_arg(&mut self) -> Result { match self.tokens.next().ok_or(CovenantError::UnexpectedEndOfTokens)? { CovenantToken::Arg(arg) => Ok(*arg), @@ -65,6 +69,8 @@ impl<'a> CovenantContext<'a> { } } + /// Outputs next `CovenantFilter`, if it happens to be the next token in the current instance, + /// otherwise it errors pub fn require_next_filter(&mut self) -> Result { match self.tokens.next().ok_or(CovenantError::UnexpectedEndOfTokens)? { CovenantToken::Filter(filter) => Ok(filter), @@ -72,10 +78,12 @@ impl<'a> CovenantContext<'a> { } } + /// Block height pub fn block_height(&self) -> u64 { self.block_height } + /// Transaction input pub fn input(&self) -> &TransactionInput { self.input } diff --git a/base_layer/core/src/covenants/covenant.rs b/base_layer/core/src/covenants/covenant.rs index e6eb8ec2de..0b0b1c1d84 100644 --- a/base_layer/core/src/covenants/covenant.rs +++ b/base_layer/core/src/covenants/covenant.rs @@ -46,6 +46,8 @@ use crate::{ const MAX_COVENANT_BYTES: usize = 4096; #[derive(Debug, Clone, PartialEq, Eq, Default)] +/// A covenant allows a UTXO to specify some restrictions on how it is spent in a future transaction. +/// See https://rfc.tari.com/RFC-0250_Covenants.html for details. pub struct Covenant { tokens: Vec, } @@ -79,6 +81,8 @@ impl Covenant { Self { tokens: Vec::new() } } + /// Produces a new `Covenant` instance, out of a byte buffer. It errors + /// if the byte buffer length is higher than `MAX_COVENANT_BYTES`. pub fn from_bytes(bytes: &mut &[u8]) -> Result { if bytes.is_empty() { return Ok(Self::new()); @@ -89,22 +93,28 @@ impl Covenant { CovenantTokenDecoder::new(bytes).collect() } + /// Given a `Covenant` instance, it writes its bytes content to a + /// new byte buffer. pub fn to_bytes(&self) -> Vec { let mut buf = Vec::with_capacity(self.get_byte_length()); self.write_to(&mut buf).unwrap(); buf } + /// Writes a `Covenant` instance byte to a writer. pub(super) fn write_to(&self, writer: &mut W) -> Result<(), io::Error> { CovenantTokenEncoder::new(self.tokens.as_slice()).write_to(writer) } + /// Gets the byte lenght of the underlying byte buffer pub(super) fn get_byte_length(&self) -> usize { let mut counter = ByteCounter::new(); self.write_to(&mut counter).unwrap(); counter.get() } + /// It executes the covenant on the transaction input being spent, it filters the transaction outputs which should + /// generate at least one match. An empty covenant is an identity and matches all outputs. pub fn execute<'a>( &self, block_height: u64, @@ -131,25 +141,30 @@ impl Covenant { Ok(output_set.len()) } + /// Adds a new `CovenantToken` to the current `tokens` vector field. pub fn push_token(&mut self, token: CovenantToken) { self.tokens.push(token); } #[cfg(test)] + /// Outputs a slice of the instance existing `CovenantToken`'s. pub(super) fn tokens(&self) -> &[CovenantToken] { &self.tokens } + /// Outputs the length of `tokens` field. pub fn num_tokens(&self) -> usize { self.tokens.len() } + /// Checks if the `tokens` field is empty. pub fn is_empty(&self) -> bool { self.tokens.is_empty() } } impl FromIterator for Covenant { + /// Creates a new `CovenantToken` instance from an iterator with `Item = CovenantToken`. fn from_iter>(iter: T) -> Self { Self { tokens: iter.into_iter().collect(), diff --git a/base_layer/core/src/covenants/decoder.rs b/base_layer/core/src/covenants/decoder.rs index e498e465f8..c4b7e54386 100644 --- a/base_layer/core/src/covenants/decoder.rs +++ b/base_layer/core/src/covenants/decoder.rs @@ -27,12 +27,14 @@ use tari_script::ScriptError; use crate::covenants::token::CovenantToken; +/// Covenant Token decoder. pub struct CovenantTokenDecoder<'a, R> { buf: &'a mut R, is_complete: bool, } impl<'a, R: io::Read> CovenantTokenDecoder<'a, R> { + /// Given a read buffer, it creates a new instance of `CovenantTokenDecoder`. pub fn new(buf: &'a mut R) -> Self { Self { buf, @@ -44,6 +46,8 @@ impl<'a, R: io::Read> CovenantTokenDecoder<'a, R> { impl Iterator for CovenantTokenDecoder<'_, &[u8]> { type Item = Result; + /// Returns the next item in `CovenantTokenDecoder`'s buffer. If it is complete, + /// it returns `None`. fn next(&mut self) -> Option { if self.is_complete { return None; @@ -64,6 +68,7 @@ impl Iterator for CovenantTokenDecoder<'_, &[u8]> { } #[derive(Debug, thiserror::Error)] +/// Error enum for covenant decoding possible failure scenarios. pub enum CovenantDecodeError { #[error("Unknown filter byte code {code}")] UnknownFilterByteCode { code: u8 }, @@ -81,12 +86,15 @@ pub enum CovenantDecodeError { Io(#[from] io::Error), } +/// Trait `CovenantReadExt`. Contains two interface methods, `read_next_byte_code` +/// and `read_variable_length_bytes`. pub(super) trait CovenantReadExt: io::Read { fn read_next_byte_code(&mut self) -> Result, io::Error>; fn read_variable_length_bytes(&mut self, size: usize) -> Result, io::Error>; } impl CovenantReadExt for R { + /// Reads next byte code fn read_next_byte_code(&mut self) -> Result, io::Error> { let mut buf = [0u8; 1]; loop { @@ -102,6 +110,7 @@ impl CovenantReadExt for R { } } + /// Reads a variable length byte array fn read_variable_length_bytes(&mut self, max_size: usize) -> Result, io::Error> { let len = self.read_varint::()? as usize; if len > max_size { diff --git a/base_layer/core/src/covenants/fields.rs b/base_layer/core/src/covenants/fields.rs index d92ca379ca..8d91e9ad19 100644 --- a/base_layer/core/src/covenants/fields.rs +++ b/base_layer/core/src/covenants/fields.rs @@ -45,6 +45,7 @@ use crate::{ #[derive(Debug, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)] #[repr(u8)] +/// Output field pub enum OutputField { Commitment = byte_codes::FIELD_COMMITMENT, Script = byte_codes::FIELD_SCRIPT, @@ -83,6 +84,7 @@ impl OutputField { self as u8 } + /// Gets a reference for the field value pub(super) fn get_field_value_ref(self, output: &TransactionOutput) -> Option<&T> { #[allow(clippy::enum_glob_use)] use OutputField::*; @@ -101,6 +103,7 @@ impl OutputField { val.downcast_ref::() } + /// Borsh serializes self to field value bytes pub fn get_field_value_bytes(self, output: &TransactionOutput) -> Vec { #[allow(clippy::enum_glob_use)] use OutputField::*; @@ -122,6 +125,8 @@ impl OutputField { writer } + /// Given an `OutputField` instance, it checks if the corresponding input field value + /// matches that of the output pub fn is_eq_input(self, input: &TransactionInput, output: &TransactionOutput) -> bool { #[allow(clippy::enum_glob_use)] use OutputField::*; @@ -166,6 +171,8 @@ impl OutputField { } } + /// Given an `OutputField` instance, it checks if the corresponding `transaction output` + /// field value matches that of `val` pub fn is_eq( self, output: &TransactionOutput, @@ -274,6 +281,7 @@ impl Display for OutputField { } #[derive(Debug, Clone, PartialEq, Eq, Default, BorshSerialize, BorshDeserialize)] +/// Wraps a collection of `OutputField` pub struct OutputFields { fields: Vec, } @@ -282,20 +290,24 @@ impl OutputFields { /// The number of unique fields available. This always matches the number of variants in `OutputField`. pub const NUM_FIELDS: usize = 10; + /// Returns a new empty instance of `OutputFields`. pub fn new() -> Self { Default::default() } + /// Pushes a new output field to the underlying `OutputFields` data. pub fn push(&mut self, field: OutputField) { self.fields.push(field); } + /// Reads from a read buffer. Errors if the reader has too many field elements. pub fn read_from(reader: &mut R) -> Result { // Each field is a byte let buf = reader.read_variable_length_bytes(Self::NUM_FIELDS)?; buf.iter().map(|byte| OutputField::from_byte(*byte)).collect() } + /// Writes an instance `OutputFields` data to a new writer. pub fn write_to(&self, writer: &mut W) -> Result { let len = self.fields.len(); if len > Self::NUM_FIELDS { @@ -311,18 +323,23 @@ impl OutputFields { Ok(written) } + /// Returns the underlying iterator of `OutputFields`. pub fn iter(&self) -> impl Iterator + '_ { self.fields.iter() } + /// Returns the length of the underlying `OutputFields` length. pub fn len(&self) -> usize { self.fields.len() } + /// Checks if `OutputFields` fields is empty. pub fn is_empty(&self) -> bool { self.fields.is_empty() } + /// Given a `TransactionOutput` it iteratively hashes the field value for a + /// `TransactionOutput`, over the underlying list of field values pub fn construct_challenge_from(&self, output: &TransactionOutput) -> Blake256 { let mut challenge = Blake256::new(); BaseLayerCovenantsDomain::add_domain_separation_tag(&mut challenge, COVENANTS_FIELD_HASHER_LABEL); @@ -332,17 +349,20 @@ impl OutputFields { challenge } + /// Produces a slice of the underlying fields of `OutputFields`. pub fn fields(&self) -> &[OutputField] { &self.fields } } impl From> for OutputFields { + /// Produces a new `OutputFields` instance out of a vector of `OutputField`. fn from(fields: Vec) -> Self { OutputFields { fields } } } impl FromIterator for OutputFields { + /// Produces a new `OutputFields` instance out of an iterator of `OutputField`. fn from_iter>(iter: T) -> Self { Self { fields: iter.into_iter().collect(), diff --git a/base_layer/core/src/covenants/macros.rs b/base_layer/core/src/covenants/macros.rs index c62019b746..7c4e81ff6c 100644 --- a/base_layer/core/src/covenants/macros.rs +++ b/base_layer/core/src/covenants/macros.rs @@ -20,7 +20,16 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// Simple syntax for expressing covenants. +/// This macro has three different patterns that it can match against based on the syntax provided. +/// +/// The first pattern matches when the macro is called with an identifier followed by parentheses and optional arguments +/// ($token($($args:tt)*)). This pattern is useful when you want to create a covenant with some specific arguments. +/// +/// The second pattern matches when the macro is called with just an identifier followed by empty parentheses +/// ($token()). This pattern is useful when you want to create a covenant without any arguments. +/// +/// The third pattern matches when the macro is called with empty parentheses (()). This pattern is used when you want +/// to create a covenant with no additional arguments. Simple syntax for expressing covenants. /// /// ```rust,ignore /// // Before height 42, this may only be spent into an output with flag 8 (NON_FUNGIBLE) @@ -45,6 +54,33 @@ macro_rules! covenant { } #[macro_export] +/// Macro for different pattern matching rules: +/// +/// 1. @ { $covenant:ident } => {}: This rule matches an empty input and does nothing. +/// +/// 2. @ { $covenant:ident } $token:ident() $(,)? => { ... }: This rule matches a token followed by empty +/// parentheses. It invokes a method push_token on the covenant object with the generated +/// CovenantToken::$token(). +/// +/// 3. @ { $covenant:ident } @field::$field:ident, $($tail:tt)* => { ... }: This rule matches a token @field::$field +/// followed by a comma-separated list of tokens. It invokes a method push_token on the covenant object with the +/// generated CovenantToken::field($crate::covenants::OutputField::$field()). Then it recursively calls +/// __covenant_inner! with the remaining tokens. +/// +/// 4. @ { $covenant:ident } @field::$field:ident $(,)? => { ... }: This rule matches a single @field::$field token +/// followed by an optional comma. It delegates to the previous rule to handle the token. +/// +/// 5. @ { $covenant:ident } @fields($(@field::$field:ident),+ $(,)?)) => { ... }: This rule matches @fields +/// followed by a comma-separated list of @field::$field tokens wrapped in parentheses. It invokes a method +/// push_token on the covenant object with the generated CovenantToken::fields vector containing +/// OutputField::$field instances. It then recursively calls __covenant_inner! with the remaining tokens. +/// +/// 6. @ { $covenant:ident } @fields($(@field::$field:ident),+ $(,)?), $($tail:tt)* => { ... }: This rule is similar +/// to the previous one but allows for additional tokens after the @fields list. It behaves similarly by +/// generating the CovenantToken::fields vector and recursively calling __covenant_inner! with the remaining +/// tokens. +/// +/// This macro pattern is called a tt-muncher (tee hee) macro_rules! __covenant_inner { (@ { $covenant:ident }) => {}; diff --git a/base_layer/core/src/covenants/output_set.rs b/base_layer/core/src/covenants/output_set.rs index 8192418f04..caf5f18e12 100644 --- a/base_layer/core/src/covenants/output_set.rs +++ b/base_layer/core/src/covenants/output_set.rs @@ -30,27 +30,33 @@ use std::{ use crate::{covenants::error::CovenantError, transactions::transaction_components::TransactionOutput}; #[derive(Debug, Clone)] +/// Structure wrapping a set of `TransactionOutput` references. pub struct OutputSet<'a>(BTreeSet>); impl<'a> OutputSet<'a> { + /// Produces a new `OutputSet` from a slice of `TransactionOutput`. pub fn new(outputs: &'a [TransactionOutput]) -> Self { // This sets the internal index for each output // Note there is no publicly accessible way to modify the indexes outputs.iter().enumerate().collect() } + /// Returns the length of the underlying data slice of `OutputSet`. pub fn len(&self) -> usize { self.0.len() } + /// Checks if the underlying data slice is empty. pub fn is_empty(&self) -> bool { self.0.is_empty() } + /// Sets the current instance data of another instance. pub fn set(&mut self, new_set: Self) { *self = new_set; } + /// Retains all outputs for which the given predicate f return true. pub fn retain(&mut self, mut f: F) -> Result<(), CovenantError> where F: FnMut(&'a TransactionOutput) -> Result { let mut err = None; @@ -68,18 +74,23 @@ impl<'a> OutputSet<'a> { } } + /// Union of two instances. pub fn union(&self, other: &Self) -> Self { self.0.union(&other.0).copied().collect() } + /// Difference of two instances. pub fn difference(&self, other: &Self) -> Self { self.0.difference(&other.0).copied().collect() } + /// Symmetric difference of two instances. pub fn symmetric_difference(&self, other: &Self) -> Self { self.0.symmetric_difference(&other.0).copied().collect() } + /// Finds a single matching output, modifying the output set in place. If no matching output is found, the output + /// set will be empty. pub fn find_inplace(&mut self, mut pred: F) where F: FnMut(&TransactionOutput) -> bool { match self.0.iter().find(|indexed| pred(indexed)) { @@ -94,11 +105,13 @@ impl<'a> OutputSet<'a> { } } + /// Clears an instance. pub fn clear(&mut self) { self.0.clear(); } #[cfg(test)] + /// Get the `TransactionOutput` at `index`. Returns `None`, if there is none such `TransactionOutput`. pub(super) fn get(&self, index: usize) -> Option<&TransactionOutput> { self.0 .iter() @@ -107,18 +120,21 @@ impl<'a> OutputSet<'a> { } #[cfg(test)] + /// Gets vector of corresponding indexes. pub(super) fn get_selected_indexes(&self) -> Vec { self.0.iter().map(|idx| idx.index).collect() } } impl<'a> FromIterator<(usize, &'a TransactionOutput)> for OutputSet<'a> { + /// Produces a new `OutputSet` out of an appropriate iterator. fn from_iter>(iter: T) -> Self { iter.into_iter().map(|(i, output)| Indexed::new(i, output)).collect() } } impl<'a> FromIterator> for OutputSet<'a> { + /// Produces a new `OutputSet` out of an appropriate iterator. fn from_iter>>(iter: T) -> Self { Self(iter.into_iter().collect()) } diff --git a/base_layer/core/src/covenants/token.rs b/base_layer/core/src/covenants/token.rs index 8de3041bcd..43d979eceb 100644 --- a/base_layer/core/src/covenants/token.rs +++ b/base_layer/core/src/covenants/token.rs @@ -49,12 +49,14 @@ use crate::{ }; #[derive(Debug, Clone, PartialEq, Eq)] +/// Covenant token. Either a filter covenant or an argument covenant. pub enum CovenantToken { Filter(CovenantFilter), Arg(Box), } impl CovenantToken { + /// Reads from a byte buffer. pub fn read_from(reader: &mut &[u8]) -> Result, CovenantDecodeError> { let code = match reader.read_next_byte_code()? { Some(c) => c, @@ -74,6 +76,7 @@ impl CovenantToken { } } + /// Writes instance data content to a `W: io::Write` instance. pub fn write_to(&self, writer: &mut W) -> Result<(), io::Error> { match self { CovenantToken::Filter(filter) => filter.write_to(writer), @@ -81,6 +84,7 @@ impl CovenantToken { } } + /// Outputs the underlying `CovenantFilter`, if appropriate. Otherwise, outputs `None`. pub fn as_filter(&self) -> Option<&CovenantFilter> { match self { CovenantToken::Filter(filter) => Some(filter), @@ -88,6 +92,7 @@ impl CovenantToken { } } + /// Outputs the underlying `CovenantArg`, if appropriate. Otherwise, outputs `None`. pub fn as_arg(&self) -> Option<&CovenantArg> { match self { CovenantToken::Filter(_) => None, @@ -98,101 +103,121 @@ impl CovenantToken { //---------------------------------- Macro helper functions --------------------------------------------// #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `IdentityFilter`. pub fn identity() -> Self { CovenantFilter::Identity(IdentityFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `AndFilter`. pub fn and() -> Self { CovenantFilter::And(AndFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `OrFilter`. pub fn or() -> Self { CovenantFilter::Or(OrFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `XorFilter`. pub fn xor() -> Self { CovenantFilter::Xor(XorFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `NotFilter`. pub fn not() -> Self { CovenantFilter::Not(NotFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `OutputHashEqFilter`. pub fn output_hash_eq() -> Self { CovenantFilter::OutputHashEq(OutputHashEqFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `FieldsPreservedFilter`. pub fn fields_preserved() -> Self { CovenantFilter::FieldsPreserved(FieldsPreservedFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `FieldEqFilter`. pub fn field_eq() -> Self { CovenantFilter::FieldEq(FieldEqFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `FieldsHashedEqFilter`. pub fn fields_hashed_eq() -> Self { CovenantFilter::FieldsHashedEq(FieldsHashedEqFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `AbsoluteHeightFilter`. pub fn absolute_height() -> Self { CovenantFilter::AbsoluteHeight(AbsoluteHeightFilter).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `HashFilter`. pub fn hash(hash: FixedHash) -> Self { CovenantArg::Hash(hash).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `PublicKeyFilter`. pub fn public_key(public_key: PublicKey) -> Self { CovenantArg::PublicKey(public_key).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `CommitmentFilter`. pub fn commitment(commitment: Commitment) -> Self { CovenantArg::Commitment(commitment).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `ScriptFilter`. pub fn script(script: TariScript) -> Self { CovenantArg::TariScript(script).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `CovenantFilter`. pub fn covenant(covenant: Covenant) -> Self { CovenantArg::Covenant(covenant).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `UintFilter`. pub fn uint(val: u64) -> Self { CovenantArg::Uint(val).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `OutputTypeFilter`. pub fn output_type(output_type: OutputType) -> Self { CovenantArg::OutputType(output_type).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `FieldFilter`. pub fn field(field: OutputField) -> Self { CovenantArg::OutputField(field).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `FieldsFilter`. pub fn fields(fields: Vec) -> Self { CovenantArg::OutputFields(fields.into()).into() } #[allow(dead_code)] + /// Helper for creating a new instance wrapping an `BytesFilter`. pub fn bytes(bytes: Vec) -> Self { CovenantArg::Bytes(bytes).into() } @@ -211,15 +236,18 @@ impl From for CovenantToken { } #[derive(Debug, Clone, Default)] +/// `CovenantTokenCollection` structure. It wraps a collection of `CovenantToken`'s. pub struct CovenantTokenCollection { tokens: VecDeque, } impl CovenantTokenCollection { + /// Checks if `CovenantTokenCollection` underlying data is empty. pub fn is_empty(&self) -> bool { self.tokens.is_empty() } + /// Outputs the front value in the underlying data of current instance. pub fn next(&mut self) -> Option { self.tokens.pop_front() } diff --git a/base_layer/wallet_ffi/wallet.h b/base_layer/wallet_ffi/wallet.h index dd51a36922..0450662bce 100644 --- a/base_layer/wallet_ffi/wallet.h +++ b/base_layer/wallet_ffi/wallet.h @@ -72,6 +72,10 @@ struct Contact; struct ContactsLivenessData; +/** + * A covenant allows a UTXO to specify some restrictions on how it is spent in a future transaction. + * See https://rfc.tari.com/RFC-0250_Covenants.html for details. + */ struct Covenant; struct EmojiSet;