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!: covenants integration #3681

Merged
Merged
Show file tree
Hide file tree
Changes from 17 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
7 changes: 6 additions & 1 deletion applications/tari_app_grpc/proto/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ message TransactionInput {
bytes sender_offset_public_key = 8;
// The hash of the output this input is spending
bytes output_hash = 9;
// Covenant
bytes covenant = 10;
}

// Output for a transaction, defining the new ownership of coins that are being transferred. The commitment is a
Expand All @@ -192,7 +194,8 @@ message TransactionOutput {
// Metadata signature with the homomorphic commitment private values (amount and blinding factor) and the sender
// offset private key
ComSignature metadata_signature = 7;
// Tari script offset pubkey, K_O
// Covenant
bytes covenant = 8;
}

// Options for UTXO's
Expand Down Expand Up @@ -328,6 +331,8 @@ message UnblindedOutput {
ComSignature metadata_signature = 9;
// The minimum height the script allows this output to be spent
uint64 script_lock_height = 10;
// Covenant
bytes covenant = 11;
}

// ----------------------------- Network Types ----------------------------- //
Expand Down
20 changes: 12 additions & 8 deletions applications/tari_app_grpc/src/conversions/transaction_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
use std::convert::{TryFrom, TryInto};

use tari_common_types::types::{Commitment, PublicKey};
use tari_core::transactions::transaction::TransactionInput;
use tari_core::{
consensus::{ConsensusDecoding, ToConsensusBytes},
covenants::Covenant,
transactions::transaction::TransactionInput,
};
use tari_crypto::{
script::{ExecutionStack, TariScript},
tari_utilities::ByteArray,
Expand Down Expand Up @@ -51,6 +55,7 @@ impl TryFrom<grpc::TransactionInput> for TransactionInput {

let sender_offset_public_key =
PublicKey::from_bytes(input.sender_offset_public_key.as_bytes()).map_err(|err| format!("{:?}", err))?;
let covenant = Covenant::consensus_decode(&mut input.covenant.as_slice()).map_err(|err| err.to_string())?;

Ok(TransactionInput::new_with_output_data(
features,
Expand All @@ -59,6 +64,7 @@ impl TryFrom<grpc::TransactionInput> for TransactionInput {
ExecutionStack::from_bytes(input.input_data.as_slice()).map_err(|err| format!("{:?}", err))?,
script_signature,
sender_offset_public_key,
covenant,
))
} else {
if input.output_hash.is_empty() {
Expand All @@ -85,14 +91,9 @@ impl TryFrom<TransactionInput> for grpc::TransactionInput {
if input.is_compact() {
let output_hash = input.output_hash();
Ok(Self {
features: None,
commitment: Vec::new(),
hash: Vec::new(),
script: Vec::new(),
input_data: Vec::new(),
script_signature,
sender_offset_public_key: Vec::new(),
output_hash,
..Default::default()
})
} else {
let features = input
Expand All @@ -104,7 +105,6 @@ impl TryFrom<TransactionInput> for grpc::TransactionInput {
commitment: input
.commitment()
.map_err(|_| "Non-compact Transaction input should contain commitment".to_string())?
.clone()
.as_bytes()
.to_vec(),
hash: input
Expand All @@ -123,6 +123,10 @@ impl TryFrom<TransactionInput> for grpc::TransactionInput {
.as_bytes()
.to_vec(),
output_hash: Vec::new(),
covenant: input
.covenant()
.map_err(|_| "Non-compact Transaction input should contain covenant".to_string())?
.to_consensus_bytes(),
})
}
}
Expand Down
10 changes: 8 additions & 2 deletions applications/tari_app_grpc/src/conversions/transaction_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
use std::convert::{TryFrom, TryInto};

use tari_common_types::types::{BulletRangeProof, Commitment, PublicKey};
use tari_core::transactions::transaction::TransactionOutput;
use tari_core::{
consensus::{ConsensusDecoding, ToConsensusBytes},
covenants::Covenant,
transactions::transaction::TransactionOutput,
};
use tari_crypto::script::TariScript;
use tari_utilities::{ByteArray, Hashable};

Expand Down Expand Up @@ -51,14 +55,15 @@ impl TryFrom<grpc::TransactionOutput> for TransactionOutput {
.ok_or_else(|| "Metadata signature not provided".to_string())?
.try_into()
.map_err(|_| "Metadata signature could not be converted".to_string())?;

let covenant = Covenant::consensus_decode(&mut output.covenant.as_slice()).map_err(|err| err.to_string())?;
Ok(Self {
features,
commitment,
proof: BulletRangeProof(output.range_proof),
script,
sender_offset_public_key,
metadata_signature,
covenant,
})
}
}
Expand All @@ -78,6 +83,7 @@ impl From<TransactionOutput> for grpc::TransactionOutput {
signature_u: Vec::from(output.metadata_signature.u().as_bytes()),
signature_v: Vec::from(output.metadata_signature.v().as_bytes()),
}),
covenant: output.covenant.to_consensus_bytes(),
}
}
}
10 changes: 9 additions & 1 deletion applications/tari_app_grpc/src/conversions/unblinded_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
use std::convert::{TryFrom, TryInto};

use tari_common_types::types::{PrivateKey, PublicKey};
use tari_core::transactions::{tari_amount::MicroTari, transaction::UnblindedOutput};
use tari_core::{
consensus::{ConsensusDecoding, ToConsensusBytes},
covenants::Covenant,
transactions::{tari_amount::MicroTari, transaction::UnblindedOutput},
};
use tari_crypto::script::{ExecutionStack, TariScript};
use tari_utilities::ByteArray;

Expand All @@ -45,6 +49,7 @@ impl From<UnblindedOutput> for grpc::UnblindedOutput {
signature_v: Vec::from(output.metadata_signature.v().as_bytes()),
}),
script_lock_height: output.script_lock_height,
covenant: output.covenant.to_consensus_bytes(),
}
}
}
Expand Down Expand Up @@ -78,6 +83,8 @@ impl TryFrom<grpc::UnblindedOutput> for UnblindedOutput {
.try_into()
.map_err(|_| "Metadata signature could not be converted".to_string())?;

let covenant = Covenant::consensus_decode(&mut output.covenant.as_slice()).map_err(|err| err.to_string())?;

Ok(Self {
value: MicroTari::from(output.value),
spending_key,
Expand All @@ -88,6 +95,7 @@ impl TryFrom<grpc::UnblindedOutput> for UnblindedOutput {
sender_offset_public_key,
metadata_signature,
script_lock_height: output.script_lock_height,
covenant,
})
}
}
2 changes: 1 addition & 1 deletion applications/test_faucet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
task::spawn(async move {
let result = task::spawn_blocking(move || {
let script = script!(Nop);
let (utxo, key, _) = test_helpers::create_utxo(value, &fc, feature, &script);
let (utxo, key, _) = test_helpers::create_utxo(value, &fc, feature, &script, &Default::default());
print!(".");
(utxo, key, value)
})
Expand Down
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 @@ -231,6 +233,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
1 change: 1 addition & 0 deletions base_layer/core/src/chain_storage/blockchain_database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,7 @@ fn fetch_block<T: BlockchainBackend>(db: &T, height: u64) -> Result<HistoricalBl
output.commitment,
output.script,
output.sender_offset_public_key,
output.covenant,
);
Ok(compact_input)
},
Expand Down
1 change: 1 addition & 0 deletions base_layer/core/src/chain_storage/lmdb_db/lmdb_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,7 @@ impl LMDBDatabase {
output.commitment,
output.script,
output.sender_offset_public_key,
output.covenant,
);
},
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,7 @@ mod add_block {
lock_height: 0,
features,
script: tari_crypto::script![Nop],
covenant: Default::default(),
input_data: None,
}]);
let commitment_hex = txns[0]
Expand Down Expand Up @@ -446,6 +447,7 @@ mod add_block {
lock_height: 0,
features: Default::default(),
script: tari_crypto::script![Nop],
covenant: Default::default(),
input_data: None,
}]);

Expand Down
55 changes: 47 additions & 8 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 Expand Up @@ -195,7 +234,7 @@ mod impls {

impl ConsensusEncodingSized for Signature {
fn consensus_encode_exact_size(&self) -> usize {
96
self.get_signature().consensus_encode_exact_size() + self.get_public_nonce().consensus_encode_exact_size()
}
}

Expand All @@ -205,7 +244,7 @@ mod impls {
reader.read_exact(&mut buf)?;
let pub_nonce =
PublicKey::from_bytes(&buf[..]).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
let mut buf = [0u8; 64];
let mut buf = [0u8; 32];
reader.read_exact(&mut buf)?;
let sig =
PrivateKey::from_bytes(&buf[..]).map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
Expand Down
27 changes: 27 additions & 0 deletions base_layer/core/src/covenants/covenant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,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 @@ -152,4 +154,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
Loading