Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: add covenants to txos and tx/block validation #1

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion base_layer/core/src/blocks/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use crate::{
},
};

#[derive(Clone, Debug, PartialEq, Error)]
#[derive(Clone, Debug, Error)]
pub enum BlockValidationError {
#[error("A transaction in the block failed to validate: `{0}`")]
TransactionError(#[from] TransactionError),
Expand Down
3 changes: 3 additions & 0 deletions base_layer/core/src/blocks/genesis_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use tari_crypto::{

use crate::{
blocks::{block::Block, BlockHeader, BlockHeaderAccumulatedData, ChainBlock},
covenants::Covenant,
proof_of_work::{PowAlgorithm, ProofOfWork},
transactions::{
aggregated_body::AggregateBody,
Expand Down Expand Up @@ -100,6 +101,7 @@ fn get_igor_genesis_block_raw() -> Block {
sender_offset_public_key: Default::default(),
// For genesis block: Metadata signature will never be checked
metadata_signature: Default::default(),
covenant: Covenant::default(),
}],
vec![TransactionKernel {
features: KernelFeatures::COINBASE_KERNEL,
Expand Down Expand Up @@ -186,6 +188,7 @@ fn get_dibbler_genesis_block_raw() -> Block {
sender_offset_public_key: Default::default(),
// For genesis block: Metadata signature will never be checked
metadata_signature: Default::default(),
covenant: Default::default()
}],
vec![TransactionKernel {
features: KernelFeatures::COINBASE_KERNEL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,7 @@ mod add_block {
lock_height: 0,
features: Default::default(),
script: tari_crypto::script![Nop],
covenant: Default::default(),
input_data: None,
}]);

Expand Down Expand Up @@ -435,6 +436,7 @@ mod add_block {
lock_height: 0,
features: Default::default(),
script: tari_crypto::script![Nop],
covenant: Default::default(),
input_data: None,
}]);

Expand Down
51 changes: 45 additions & 6 deletions base_layer/core/src/consensus/consensus_encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ mod impls {
use std::io::Read;

use tari_common_types::types::{Commitment, PrivateKey, PublicKey, Signature};
use tari_crypto::script::TariScript;
use tari_crypto::{
keys::{PublicKey as PublicKeyTrait, SecretKey as SecretKeyTrait},
script::{ExecutionStack, TariScript},
};
use tari_utilities::ByteArray;

use super::*;
Expand All @@ -125,15 +128,13 @@ mod impls {
}
}

//---------------------------------- PublicKey --------------------------------------------//

impl ConsensusEncoding for PublicKey {
impl ConsensusEncoding for ExecutionStack {
fn consensus_encode<W: io::Write>(&self, writer: &mut W) -> Result<usize, io::Error> {
writer.write(self.as_bytes())
self.as_bytes().consensus_encode(writer)
}
}

impl ConsensusEncodingSized for PublicKey {
impl ConsensusEncodingSized for ExecutionStack {
fn consensus_encode_exact_size(&self) -> usize {
let mut counter = ByteCounter::new();
// TODO: consensus_encode_exact_size must be cheap to run
Expand All @@ -143,6 +144,20 @@ mod impls {
}
}

//---------------------------------- PublicKey --------------------------------------------//

impl ConsensusEncoding for PublicKey {
fn consensus_encode<W: io::Write>(&self, writer: &mut W) -> Result<usize, io::Error> {
writer.write(self.as_bytes())
}
}

impl ConsensusEncodingSized for PublicKey {
fn consensus_encode_exact_size(&self) -> usize {
PublicKey::key_length()
}
}

impl ConsensusDecoding for PublicKey {
fn consensus_decode<R: Read>(reader: &mut R) -> Result<Self, io::Error> {
let mut buf = [0u8; 32];
Expand All @@ -152,6 +167,30 @@ mod impls {
}
}

//---------------------------------- PrivateKey --------------------------------------------//

impl ConsensusEncoding for PrivateKey {
fn consensus_encode<W: io::Write>(&self, writer: &mut W) -> Result<usize, io::Error> {
writer.write(self.as_bytes())
}
}

impl ConsensusEncodingSized for PrivateKey {
fn consensus_encode_exact_size(&self) -> usize {
PrivateKey::key_length()
}
}

impl ConsensusDecoding for PrivateKey {
fn consensus_decode<R: Read>(reader: &mut R) -> Result<Self, io::Error> {
let mut buf = [0u8; 32];
reader.read_exact(&mut buf)?;
let sk =
PrivateKey::from_bytes(&buf[..]).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
Ok(sk)
}
}

//---------------------------------- Commitment --------------------------------------------//

impl ConsensusEncoding for Commitment {
Expand Down
30 changes: 27 additions & 3 deletions base_layer/core/src/covenants/covenant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ impl Covenant {
}

pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, CovenantDecodeError> {
if bytes.is_empty() {
return Ok(Self::new());
}
CovenantTokenDecoder::new(&mut bytes).collect()
}

Expand Down Expand Up @@ -131,7 +128,9 @@ impl FromIterator<CovenantToken> for Covenant {

#[cfg(test)]
mod test {
use super::*;
use crate::{
consensus::ToConsensusBytes,
covenant,
covenants::test::{create_input, create_outputs},
};
Expand All @@ -157,4 +156,29 @@ mod test {
let num_matching_outputs = covenant.execute(0, &input, &outputs).unwrap();
assert_eq!(num_matching_outputs, 3);
}

mod consensus_encoding {
use super::*;

#[test]
fn it_encodes_to_empty_bytes() {
let bytes = Covenant::new().to_consensus_bytes();
assert_eq!(bytes.len(), 0);
}
}

mod consensus_decoding {
use super::*;

#[test]
fn it_is_identity_if_empty_bytes() {
let empty_buf = &[] as &[u8; 0];
let covenant = Covenant::consensus_decode(&mut &empty_buf[..]).unwrap();

let outputs = create_outputs(10, Default::default());
let input = create_input();
let num_selected = covenant.execute(0, &input, &outputs).unwrap();
assert_eq!(num_selected, 10);
}
}
}
6 changes: 6 additions & 0 deletions base_layer/core/src/covenants/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ mod test {
covenants::{arguments::CovenantArg, fields::OutputField, filters::CovenantFilter},
};

#[test]
fn it_immediately_ends_iterator_given_empty_bytes() {
let buf = &[] as &[u8; 0];
assert!(CovenantTokenDecoder::new(&mut &buf[..]).next().is_none());
}

#[test]
fn it_decodes_from_well_formed_bytes() {
let hash = from_hex("53563b674ba8e5166adb57afa8355bcf2ee759941eef8f8959b802367c2558bd").unwrap();
Expand Down
49 changes: 26 additions & 23 deletions base_layer/core/src/covenants/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl OutputField {
Commitment => &output.commitment as &dyn Any,
Script => &output.script as &dyn Any,
SenderOffsetPublicKey => &output.sender_offset_public_key as &dyn Any,
Covenant => unimplemented!(),
Covenant => &output.covenant as &dyn Any,
Features => &output.features as &dyn Any,
FeaturesFlags => &output.features.flags as &dyn Any,
FeaturesMaturity => &output.features.maturity as &dyn Any,
Expand All @@ -104,7 +104,7 @@ impl OutputField {
Commitment => output.commitment.to_consensus_bytes(),
Script => output.script.to_consensus_bytes(),
SenderOffsetPublicKey => output.sender_offset_public_key.to_consensus_bytes(),
Covenant => unimplemented!(),
Covenant => output.covenant.to_consensus_bytes(),
Features => output.features.to_consensus_bytes(),
FeaturesFlags => output.features.flags.to_consensus_bytes(),
FeaturesMaturity => output.features.maturity.to_consensus_bytes(),
Expand All @@ -130,7 +130,7 @@ impl OutputField {
Commitment => input.commitment == output.commitment,
Script => input.script == output.script,
SenderOffsetPublicKey => input.sender_offset_public_key == output.sender_offset_public_key,
Covenant => unimplemented!(),
Covenant => input.covenant == output.covenant,
Features => input.features == output.features,
FeaturesFlags => input.features.flags == output.features.flags,
FeaturesMaturity => input.features.maturity == output.features.maturity,
Expand Down Expand Up @@ -164,52 +164,52 @@ impl OutputField {

//---------------------------------- Macro helpers --------------------------------------------//
#[allow(dead_code)]
pub(super) fn commitment() -> Self {
pub fn commitment() -> Self {
OutputField::Commitment
}

#[allow(dead_code)]
pub(super) fn script() -> Self {
pub fn script() -> Self {
OutputField::Script
}

#[allow(dead_code)]
pub(super) fn sender_offset_public_key() -> Self {
pub fn sender_offset_public_key() -> Self {
OutputField::SenderOffsetPublicKey
}

#[allow(dead_code)]
pub(super) fn covenant() -> Self {
pub fn covenant() -> Self {
OutputField::Covenant
}

#[allow(dead_code)]
pub(super) fn features() -> Self {
pub fn features() -> Self {
OutputField::Features
}

#[allow(dead_code)]
pub(super) fn features_flags() -> Self {
pub fn features_flags() -> Self {
OutputField::FeaturesFlags
}

#[allow(dead_code)]
pub(super) fn features_maturity() -> Self {
pub fn features_maturity() -> Self {
OutputField::FeaturesMaturity
}

#[allow(dead_code)]
pub(super) fn features_unique_id() -> Self {
pub fn features_unique_id() -> Self {
OutputField::FeaturesUniqueId
}

#[allow(dead_code)]
pub(super) fn features_parent_public_key() -> Self {
pub fn features_parent_public_key() -> Self {
OutputField::FeaturesParentPublicKey
}

#[allow(dead_code)]
pub(super) fn features_metadata() -> Self {
pub fn features_metadata() -> Self {
OutputField::FeaturesMetadata
}
}
Expand Down Expand Up @@ -311,19 +311,22 @@ impl FromIterator<OutputField> for OutputFields {
#[cfg(test)]
mod test {
use super::*;
use crate::transactions::transaction::OutputFeatures;
use crate::{
covenants::test::create_outputs,
transactions::{test_helpers::UtxoTestParams, transaction::OutputFeatures},
};

#[test]
fn get_field_value_ref() {
let output = TransactionOutput::new(
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
);
let mut features = OutputFeatures::default();
features.maturity = 42;
let output = create_outputs(1, UtxoTestParams {
output_features: features.clone(),
..Default::default()
})
.pop()
.unwrap();
let r = OutputField::Features.get_field_value_ref::<OutputFeatures>(&output);
assert_eq!(*r.unwrap(), OutputFeatures::default());
assert_eq!(*r.unwrap(), features);
}
}
6 changes: 4 additions & 2 deletions base_layer/core/src/covenants/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,16 @@ mod error;
mod fields;
mod filters;
mod output_set;
mod serde;
mod token;

pub use covenant::Covenant;
pub use error::CovenantError;
// Used in macro
#[allow(unused_imports)]
pub(self) use fields::OutputField;
pub(crate) use fields::OutputField;
#[allow(unused_imports)]
pub(self) use token::CovenantToken;
pub(crate) use token::CovenantToken;

#[macro_use]
mod macros;
Expand Down
Loading