Skip to content

Commit

Permalink
feat: add an encrypted value to the TransactionOutput (#4148)
Browse files Browse the repository at this point in the history
Description
---
The PR adds the `encrypted_value` field (of `EncryptedValue` type) to the `TransactionOutput` struct.
The same field also added to the `TransactionInput` for a case when input was produced from an output.

The changes also affected the genesis block, dibbler's faucet data, hashes and integration tests.

The value is not actually encrypted and now it's stored in the following format: u64le + 16 empty bytes.

Motivation and Context
---
Part of the BP+ features.

How Has This Been Tested?
---
CI tests (updated)
  • Loading branch information
therustmonk committed Jun 17, 2022
1 parent 50ce20a commit 01b600a
Show file tree
Hide file tree
Showing 34 changed files with 4,439 additions and 4,076 deletions.
4 changes: 4 additions & 0 deletions applications/tari_app_grpc/proto/types.proto
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ message TransactionInput {
bytes covenant = 10;
// Version
uint32 version = 11;
// The encrypted value
bytes encrypted_value = 12;
}

// Output for a transaction, defining the new ownership of coins that are being transferred. The commitment is a
Expand All @@ -202,6 +204,8 @@ message TransactionOutput {
bytes covenant = 8;
// Version
uint32 version = 9;
// The encrypted value
bytes encrypted_value = 10;
}

// Options for UTXOs
Expand Down
11 changes: 7 additions & 4 deletions applications/tari_app_grpc/src/conversions/transaction_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use std::convert::{TryFrom, TryInto};
use tari_common_types::types::{Commitment, PublicKey};
use tari_core::{
covenants::Covenant,
transactions::transaction_components::{TransactionInput, TransactionInputVersion},
transactions::transaction_components::{EncryptedValue, TransactionInput, TransactionInputVersion},
};
use tari_script::{ExecutionStack, TariScript};
use tari_utilities::ByteArray;
Expand Down Expand Up @@ -62,7 +62,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::from_bytes(&input.covenant).map_err(|err| err.to_string())?;

let encrypted_value = EncryptedValue::from_bytes(&input.encrypted_value).map_err(|err| err.to_string())?;
Ok(TransactionInput::new_with_output_data(
TransactionInputVersion::try_from(
u8::try_from(input.version).map_err(|_| "Invalid version: overflowed u8")?,
Expand All @@ -74,6 +74,7 @@ impl TryFrom<grpc::TransactionInput> for TransactionInput {
script_signature,
sender_offset_public_key,
covenant,
encrypted_value,
))
}
}
Expand Down Expand Up @@ -105,7 +106,6 @@ impl TryFrom<TransactionInput> for grpc::TransactionInput {
commitment: input
.commitment()
.map_err(|_| "Non-compact Transaction input should contain commitment".to_string())?
.as_bytes()
.to_vec(),
hash: input
.canonical_hash()
Expand All @@ -120,14 +120,17 @@ impl TryFrom<TransactionInput> for grpc::TransactionInput {
sender_offset_public_key: input
.sender_offset_public_key()
.map_err(|_| "Non-compact Transaction input should contain sender_offset_public_key".to_string())?
.as_bytes()
.to_vec(),
output_hash: Vec::new(),
covenant: input
.covenant()
.map_err(|_| "Non-compact Transaction input should contain covenant".to_string())?
.to_bytes(),
version: input.version as u32,
encrypted_value: input
.encrypted_value()
.map_err(|_| "Non-compact Transaction input should contain encrypted value".to_string())?
.to_vec(),
})
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use std::convert::{TryFrom, TryInto};
use tari_common_types::types::{BulletRangeProof, Commitment, PublicKey};
use tari_core::{
covenants::Covenant,
transactions::transaction_components::{TransactionOutput, TransactionOutputVersion},
transactions::transaction_components::{EncryptedValue, TransactionOutput, TransactionOutputVersion},
};
use tari_script::TariScript;
use tari_utilities::{ByteArray, Hashable};
Expand Down Expand Up @@ -55,6 +55,7 @@ impl TryFrom<grpc::TransactionOutput> for TransactionOutput {
.try_into()
.map_err(|_| "Metadata signature could not be converted".to_string())?;
let covenant = Covenant::from_bytes(&output.covenant).map_err(|err| err.to_string())?;
let encrypted_value = EncryptedValue::from_bytes(&output.encrypted_value).map_err(|err| err.to_string())?;
Ok(Self::new(
TransactionOutputVersion::try_from(
u8::try_from(output.version).map_err(|_| "Invalid version: overflowed u8")?,
Expand All @@ -66,6 +67,7 @@ impl TryFrom<grpc::TransactionOutput> for TransactionOutput {
sender_offset_public_key,
metadata_signature,
covenant,
encrypted_value,
))
}
}
Expand All @@ -87,6 +89,7 @@ impl From<TransactionOutput> for grpc::TransactionOutput {
}),
covenant: output.covenant.to_bytes(),
version: output.version as u32,
encrypted_value: output.encrypted_value.to_vec(),
}
}
}
1 change: 1 addition & 0 deletions applications/test_faucet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ fn create_utxo(
offset_keys.pk,
metadata_sig,
covenant,
encrypted_value,
);
(utxo, keys.k, offset_keys.k)
}
8,000 changes: 4,000 additions & 4,000 deletions base_layer/core/src/blocks/faucets/dibbler_faucet.json

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions base_layer/core/src/blocks/genesis_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ use crate::{
aggregated_body::AggregateBody,
tari_amount::MicroTari,
transaction_components::{
EncryptedValue,
KernelFeatures,
OutputFeatures,
OutputFeaturesVersion,
Expand Down Expand Up @@ -115,6 +116,7 @@ fn get_igor_genesis_block_raw() -> Block {
// For genesis block: Metadata signature will never be checked
Default::default(),
Covenant::default(),
EncryptedValue::default(),
)],
vec![TransactionKernel::new_current_version(
KernelFeatures::COINBASE_KERNEL,
Expand Down Expand Up @@ -214,7 +216,7 @@ pub fn get_dibbler_genesis_block() -> ChainBlock {
// hardcode the Merkle roots once they've been computed above
block.header.kernel_mr = from_hex("5b91bebd33e18798e03e9c5d831d161ee9c3d12560f50b987e1a8c3ec53146df").unwrap();
block.header.witness_mr = from_hex("11227f6ce9ff34349d7dcab606b633f55234d5c8a73696a68c6e9ddc7cd3bc40").unwrap();
block.header.output_mr = from_hex("5e69274e72f8590e1cf91c189e24368527414aed966de62135d9273a6c14c3ef").unwrap();
block.header.output_mr = from_hex("e3d8e137e8efb476d0ef0149ec5f82441daec91847bd910c8d102d6432ce3278").unwrap();

let accumulated_data = BlockHeaderAccumulatedData {
hash: block.hash(),
Expand Down Expand Up @@ -259,7 +261,8 @@ fn get_dibbler_genesis_block_raw() -> Block {
// For genesis block: Metadata signature will never be checked
ComSignature::default(),
// Covenant
Covenant::default()
Covenant::default(),
EncryptedValue::default(),
);
let kernel = TransactionKernel::new(
TransactionKernelVersion::V0,
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 @@ -1510,6 +1510,7 @@ fn fetch_block<T: BlockchainBackend>(db: &T, height: u64) -> Result<HistoricalBl
output.script,
output.sender_offset_public_key,
output.covenant,
output.encrypted_value,
);
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 @@ -1056,6 +1056,7 @@ impl LMDBDatabase {
output.script,
output.sender_offset_public_key,
output.covenant,
output.encrypted_value,
);
},
}
Expand Down
4 changes: 4 additions & 0 deletions base_layer/core/src/proto/transaction.proto
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ message TransactionInput {
bytes covenant = 9;
// Version
uint32 version = 10;
// The encrypted value
bytes encrypted_value = 11;
}

// Output for a transaction, defining the new ownership of coins that are being transferred. The commitment is a
Expand All @@ -75,6 +77,8 @@ message TransactionOutput {
bytes covenant = 7;
// Version
uint32 version = 8;
// The encrypted value
bytes encrypted_value = 9;
}

// Options for UTXOs
Expand Down
10 changes: 10 additions & 0 deletions base_layer/core/src/proto/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use crate::{
ContractSpecification,
ContractUpdateProposal,
ContractUpdateProposalAcceptance,
EncryptedValue,
FunctionRef,
KernelFeatures,
MintNonFungibleFeatures,
Expand Down Expand Up @@ -160,6 +161,7 @@ impl TryFrom<proto::types::TransactionInput> for TransactionInput {
script_signature,
sender_offset_public_key,
Covenant::from_bytes(&input.covenant).map_err(|err| err.to_string())?,
EncryptedValue::from_bytes(&input.encrypted_value).map_err(|err| err.to_string())?,
))
} else {
if input.output_hash.is_empty() {
Expand Down Expand Up @@ -219,6 +221,10 @@ impl TryFrom<TransactionInput> for proto::types::TransactionInput {
.map_err(|_| "Non-compact Transaction input should contain covenant".to_string())?
.to_bytes(),
version: input.version as u32,
encrypted_value: input
.encrypted_value()
.map_err(|_| "Non-compact Transaction input should contain encrypted value".to_string())?
.to_vec(),
})
}
}
Expand Down Expand Up @@ -254,6 +260,8 @@ impl TryFrom<proto::types::TransactionOutput> for TransactionOutput {

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

let encrypted_value = EncryptedValue::from_bytes(&output.encrypted_value).map_err(|err| err.to_string())?;

Ok(Self::new(
TransactionOutputVersion::try_from(
u8::try_from(output.version).map_err(|_| "Invalid version: overflowed u8")?,
Expand All @@ -265,6 +273,7 @@ impl TryFrom<proto::types::TransactionOutput> for TransactionOutput {
sender_offset_public_key,
metadata_signature,
covenant,
encrypted_value,
))
}
}
Expand All @@ -280,6 +289,7 @@ impl From<TransactionOutput> for proto::types::TransactionOutput {
metadata_signature: Some(output.metadata_signature.into()),
covenant: output.covenant.to_bytes(),
version: output.version as u32,
encrypted_value: output.encrypted_value.to_vec(),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions base_layer/core/src/transactions/test_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,6 @@ pub fn create_stx_protocol(schema: TransactionSchema) -> (SenderTransactionProto
let commitment = factories
.commitment
.commit_value(&utxo.spending_key, utxo.value.as_u64());
let encrypted_value = EncryptedValue::todo_encrypt_from(utxo.value.as_u64());
let recovery_byte = OutputFeatures::create_unique_recovery_byte(&commitment, None);
utxo.features.set_recovery_byte(recovery_byte);
utxo.metadata_signature = TransactionOutput::create_final_metadata_signature(
Expand All @@ -715,7 +714,7 @@ pub fn create_stx_protocol(schema: TransactionSchema) -> (SenderTransactionProto
&utxo.features,
&test_params.sender_offset_private_key,
&utxo.covenant,
&encrypted_value,
&utxo.encrypted_value,
)
.unwrap();
utxo.sender_offset_public_key = test_params.sender_offset_public_key;
Expand Down Expand Up @@ -833,6 +832,7 @@ pub fn create_utxo(
offset_keys.pk,
metadata_sig,
covenant.clone(),
encrypted_value,
);
(utxo, keys.k, offset_keys.k)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,35 +28,27 @@ use std::io::{self, Read, Write};
use serde::{Deserialize, Serialize};
use tari_utilities::{ByteArray, ByteArrayError};

use crate::consensus::{ConsensusDecoding, ConsensusEncoding};
use crate::consensus::{ConsensusDecoding, ConsensusEncoding, ConsensusEncodingSized};

const SIZE: usize = 24;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct EncryptedValue {
/// value: u64 + tag: [u8; 16]
pub data: [u8; SIZE],
}
/// value: u64 + tag: [u8; 16]
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
pub struct EncryptedValue(#[serde(with = "tari_utilities::serde::hex")] pub [u8; SIZE]);

impl Default for EncryptedValue {
fn default() -> Self {
Self { data: [0; SIZE] }
Self([0; SIZE])
}
}

impl ByteArray for EncryptedValue {
fn from_bytes(bytes: &[u8]) -> Result<Self, ByteArrayError> {
if bytes.len() == SIZE {
let mut this = Self::default();
this.data.copy_from_slice(bytes);
Ok(this)
} else {
Err(ByteArrayError::IncorrectLength)
}
ByteArray::from_bytes(bytes).map(Self)
}

fn as_bytes(&self) -> &[u8] {
&self.data
self.0.as_bytes()
}
}

Expand All @@ -67,20 +59,54 @@ impl EncryptedValue {
let mut data: [u8; SIZE] = [0; SIZE];
let value = amount.into().to_le_bytes();
data[0..8].copy_from_slice(&value);
Self { data }
Self(data)
}

/// TODO: Replace this method with a real call of decryption service
/// that will produce a decrypted value from self.
pub fn todo_decrypt(&self) -> u64 {
let mut buffer = [0u8; 8];
(&mut buffer[0..8]).copy_from_slice(&self.0.as_slice()[0..8]);
u64::from_le_bytes(buffer)
}
}

impl ConsensusEncoding for EncryptedValue {
fn consensus_encode<W: Write>(&self, writer: &mut W) -> Result<(), io::Error> {
self.data.consensus_encode(writer)?;
self.0.consensus_encode(writer)?;
Ok(())
}
}

impl ConsensusEncodingSized for EncryptedValue {
fn consensus_encode_exact_size(&self) -> usize {
self.0.len()
}
}

impl ConsensusDecoding for EncryptedValue {
fn consensus_decode<R: Read>(reader: &mut R) -> Result<Self, io::Error> {
let data = <[u8; 24]>::consensus_decode(reader)?;
Ok(Self { data })
Ok(Self(data))
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::consensus::ToConsensusBytes;

#[test]
fn it_encodes_to_bytes() {
let bytes = EncryptedValue::todo_encrypt_from(123u64).to_consensus_bytes();
assert_eq!(&bytes[0..8], &123u64.to_le_bytes());
assert_eq!(bytes.len(), SIZE);
}

#[test]
fn it_decodes_from_bytes() {
let value = &[0; 24];
let encrypted_value = EncryptedValue::consensus_decode(&mut &value[..]).unwrap();
assert_eq!(encrypted_value, EncryptedValue::default());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ pub(super) fn hash_output(
commitment: &Commitment,
script: &TariScript,
covenant: &Covenant,
encrypted_value: &EncryptedValue,
) -> [u8; 32] {
match version {
TransactionOutputVersion::V0 | TransactionOutputVersion::V1 => ConsensusHashWriter::default()
Expand All @@ -111,6 +112,7 @@ pub(super) fn hash_output(
.chain(commitment)
.chain(script)
.chain(covenant)
.chain(encrypted_value)
.finalize(),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ fn range_proof_verification() {
)
.unwrap(),
Covenant::default(),
encrypted_value,
);
tx_output3.verify_range_proof(&factories.range_proof).unwrap_err();
}
Expand Down Expand Up @@ -248,6 +249,7 @@ fn check_timelocks() {
script_signature,
offset_pub_key,
Covenant::default(),
EncryptedValue::default(),
);

let mut kernel = test_helpers::create_test_kernel(0.into(), 0);
Expand Down
Loading

0 comments on commit 01b600a

Please sign in to comment.