From f79d383533c2e9c4db95d4a13973992c9a8739ef Mon Sep 17 00:00:00 2001 From: Denis Kolodin Date: Mon, 30 May 2022 15:35:28 +0300 Subject: [PATCH] feat: add encrypted_value to the UnblindedOutput (#4142) Description --- The PR adds extra encrypted field to `UnblindedOutput` structs. What was done: - Added `EncryptedValue` struct - Implemented `ConsensusEncode`, `ConsensusDecode` and `ByteArray` for it - Added the `encrypted_value` field of that type to the `UnblindedOutput` struct - Added an extra parameter to metadata signature calculation methods - Added an extra field to the `GRPC` protocol (`UnblindedOutput` type) - Added an extra column and migrations for the `outputs` table - The types `OutputSql` and `NewOutputSql` updated to store and restore the `EncryptedValue` Important notes: 1. The value of `encrypted_value` passed as a parameter to the metadata signature evaluation but has not taken into account yet, because it will break the verification process of the `TransactionOutput` and we can complete that when the same `encrypted_value` field will be added to the `TransactionOutput` struct in the further PRs. 2. Real encryption process is not implemented yet. Instead of that I've added the method `todo_encrypt_from` that I used to mark the places where we have to put an encryption service and call it. Motivation and Context --- Add an encrypted amount to the `TransactionOutput`. How Has This Been Tested? --- CI --- applications/tari_app_grpc/proto/types.proto | 2 + .../src/conversions/unblinded_output.rs | 6 +- applications/test_faucet/src/main.rs | 3 + .../core/src/transactions/coinbase_builder.rs | 4 + .../core/src/transactions/test_helpers.rs | 16 +++- .../transaction_components/encrypted_value.rs | 86 +++++++++++++++++++ .../transaction_components/mod.rs | 2 + .../transaction_components/test.rs | 4 +- .../transaction_output.rs | 20 ++++- .../unblinded_output.rs | 6 ++ .../unblinded_output_builder.rs | 8 ++ .../transaction_protocol/sender.rs | 3 + .../transaction_protocol/single_receiver.rs | 4 +- .../transaction_initializer.rs | 5 ++ base_layer/core/tests/node_comms_interface.rs | 24 +++--- .../down.sql | 1 + .../up.sql | 47 ++++++++++ .../recovery/standard_outputs_recoverer.rs | 4 +- .../src/output_manager_service/service.rs | 14 +++ .../storage/sqlite_db/new_output_sql.rs | 3 + .../storage/sqlite_db/output_sql.rs | 5 +- base_layer/wallet/src/schema.rs | 1 + .../wallet/src/transaction_service/service.rs | 4 +- base_layer/wallet/src/wallet.rs | 4 +- .../output_manager_service_tests/service.rs | 12 +-- common/src/configuration/network.rs | 14 +-- 26 files changed, 269 insertions(+), 33 deletions(-) create mode 100644 base_layer/core/src/transactions/transaction_components/encrypted_value.rs create mode 100644 base_layer/wallet/migrations/2022-05-25-154612_add_encrypted_value/down.sql create mode 100644 base_layer/wallet/migrations/2022-05-25-154612_add_encrypted_value/up.sql diff --git a/applications/tari_app_grpc/proto/types.proto b/applications/tari_app_grpc/proto/types.proto index 45dd38a4da..6bb4ed89ad 100644 --- a/applications/tari_app_grpc/proto/types.proto +++ b/applications/tari_app_grpc/proto/types.proto @@ -419,6 +419,8 @@ message UnblindedOutput { uint64 script_lock_height = 10; // Covenant bytes covenant = 11; + // Encrypted Value + bytes encrypted_value = 12; } // ----------------------------- Network Types ----------------------------- // diff --git a/applications/tari_app_grpc/src/conversions/unblinded_output.rs b/applications/tari_app_grpc/src/conversions/unblinded_output.rs index 9b4d86342f..3e3f3b9e92 100644 --- a/applications/tari_app_grpc/src/conversions/unblinded_output.rs +++ b/applications/tari_app_grpc/src/conversions/unblinded_output.rs @@ -27,7 +27,7 @@ use tari_core::{ covenants::Covenant, transactions::{ tari_amount::MicroTari, - transaction_components::{TransactionOutputVersion, UnblindedOutput}, + transaction_components::{EncryptedValue, TransactionOutputVersion, UnblindedOutput}, }, }; use tari_script::{ExecutionStack, TariScript}; @@ -52,6 +52,7 @@ impl From for grpc::UnblindedOutput { }), script_lock_height: output.script_lock_height, covenant: output.covenant.to_bytes(), + encrypted_value: output.encrypted_value.to_vec(), } } } @@ -87,6 +88,8 @@ impl TryFrom for UnblindedOutput { 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(0u8)?, MicroTari::from(output.value), @@ -99,6 +102,7 @@ impl TryFrom for UnblindedOutput { metadata_signature, output.script_lock_height, covenant, + encrypted_value, )) } } diff --git a/applications/test_faucet/src/main.rs b/applications/test_faucet/src/main.rs index 743b516e2d..20a0fb6aec 100644 --- a/applications/test_faucet/src/main.rs +++ b/applications/test_faucet/src/main.rs @@ -15,6 +15,7 @@ use tari_core::{ test_helpers, test_helpers::generate_keys, transaction_components::{ + EncryptedValue, KernelFeatures, OutputFeatures, TransactionKernel, @@ -160,6 +161,7 @@ fn create_utxo( let offset_keys = generate_keys(); let commitment = factories.commitment.commit_value(&keys.k, value.into()); let proof = factories.range_proof.construct_proof(&keys.k, value.into()).unwrap(); + let encrypted_value = EncryptedValue::todo_encrypt_from(value); let metadata_sig = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), value, @@ -168,6 +170,7 @@ fn create_utxo( &features, &offset_keys.k, &covenant, + &encrypted_value, ) .unwrap(); diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index cf324b9363..97fd23938f 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -40,6 +40,7 @@ use crate::{ crypto_factories::CryptoFactories, tari_amount::{uT, MicroTari}, transaction_components::{ + EncryptedValue, KernelBuilder, KernelFeatures, OutputFeatures, @@ -205,6 +206,7 @@ impl CoinbaseBuilder { let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); let covenant = self.covenant; + let encrypted_value = EncryptedValue::todo_encrypt_from(total_reward); let metadata_sig = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), total_reward, @@ -213,6 +215,7 @@ impl CoinbaseBuilder { &output_features, &sender_offset_private_key, &covenant, + &encrypted_value, ) .map_err(|e| CoinbaseBuildError::BuildError(e.to_string()))?; @@ -227,6 +230,7 @@ impl CoinbaseBuilder { metadata_sig, 0, covenant, + encrypted_value, ); let output = if let Some(rewind_data) = self.rewind_data.as_ref() { unblinded_output diff --git a/base_layer/core/src/transactions/test_helpers.rs b/base_layer/core/src/transactions/test_helpers.rs index 4529b2ef76..bdaea03e58 100644 --- a/base_layer/core/src/transactions/test_helpers.rs +++ b/base_layer/core/src/transactions/test_helpers.rs @@ -33,7 +33,7 @@ use tari_crypto::{ }; use tari_script::{inputs, script, ExecutionStack, TariScript}; -use super::transaction_components::{TransactionInputVersion, TransactionOutputVersion}; +use super::transaction_components::{EncryptedValue, TransactionInputVersion, TransactionOutputVersion}; use crate::{ consensus::{ConsensusEncodingSized, ConsensusManager}, covenants::Covenant, @@ -178,6 +178,7 @@ impl TestParams { let updated_features = OutputFeatures::features_with_updated_recovery_byte(&commitment, rewind_data, ¶ms.features); + let encrypted_value = EncryptedValue::todo_encrypt_from(params.value); let metadata_signature = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), params.value, @@ -186,6 +187,7 @@ impl TestParams { &updated_features, &self.sender_offset_private_key, ¶ms.covenant, + &encrypted_value, ) .unwrap(); @@ -205,6 +207,7 @@ impl TestParams { metadata_signature, 0, params.covenant, + encrypted_value, ) } @@ -221,6 +224,7 @@ impl TestParams { &updated_features, &self.sender_offset_private_key, &uo.covenant, + &uo.encrypted_value, ) .unwrap(); @@ -235,6 +239,7 @@ impl TestParams { metadata_signature, uo.script_lock_height, uo.covenant, + uo.encrypted_value, ) } @@ -685,6 +690,7 @@ 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( @@ -695,6 +701,7 @@ pub fn create_stx_protocol(schema: TransactionSchema) -> (SenderTransactionProto &utxo.features, &test_params.sender_offset_private_key, &utxo.covenant, + &encrypted_value, ) .unwrap(); utxo.sender_offset_public_key = test_params.sender_offset_public_key; @@ -719,6 +726,9 @@ pub fn create_stx_protocol(schema: TransactionSchema) -> (SenderTransactionProto recovery_byte, ..Default::default() }; + + // TODO: Get it using `something.encrypt_value(change)` + let encrypted_value = EncryptedValue::todo_encrypt_from(change); let change_metadata_sig = TransactionOutput::create_final_metadata_signature( output_version, change, @@ -727,6 +737,7 @@ pub fn create_stx_protocol(schema: TransactionSchema) -> (SenderTransactionProto &change_features, &test_params_change_and_txn.sender_offset_private_key, &covenant, + &encrypted_value, ) .unwrap(); @@ -743,6 +754,7 @@ pub fn create_stx_protocol(schema: TransactionSchema) -> (SenderTransactionProto change_metadata_sig, 0, covenant, + encrypted_value, ); outputs.push(change_output); (stx_protocol, outputs) @@ -775,6 +787,7 @@ pub fn create_utxo( let updated_features = OutputFeatures::features_with_updated_recovery_byte(&commitment, None, features); + let encrypted_value = EncryptedValue::todo_encrypt_from(value); let metadata_sig = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), value, @@ -783,6 +796,7 @@ pub fn create_utxo( &updated_features, &offset_keys.k, covenant, + &encrypted_value, ) .unwrap(); diff --git a/base_layer/core/src/transactions/transaction_components/encrypted_value.rs b/base_layer/core/src/transactions/transaction_components/encrypted_value.rs new file mode 100644 index 0000000000..b56eee1df2 --- /dev/null +++ b/base_layer/core/src/transactions/transaction_components/encrypted_value.rs @@ -0,0 +1,86 @@ +// Copyright 2022 The Tari Project +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote +// products derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// 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 +// +// Portions of this file were originally copyrighted (c) 2018 The Grin Developers, issued under the Apache License, +// Version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0. + +use std::io::{self, Read, Write}; + +use serde::{Deserialize, Serialize}; +use tari_utilities::{ByteArray, ByteArrayError}; + +use crate::consensus::{ConsensusDecoding, ConsensusEncoding}; + +const SIZE: usize = 24; + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct EncryptedValue { + /// value: u64 + tag: [u8; 16] + pub data: [u8; SIZE], +} + +impl Default for EncryptedValue { + fn default() -> Self { + Self { data: [0; SIZE] } + } +} + +impl ByteArray for EncryptedValue { + fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() == SIZE { + let mut this = Self::default(); + this.data.copy_from_slice(bytes); + Ok(this) + } else { + Err(ByteArrayError::IncorrectLength) + } + } + + fn as_bytes(&self) -> &[u8] { + &self.data + } +} + +impl EncryptedValue { + /// TODO: Replace this method with a real call of encryption service + /// that will produce an encrypted value from the given `amount`. + pub fn todo_encrypt_from(amount: impl Into) -> Self { + let mut data: [u8; SIZE] = [0; SIZE]; + let value = amount.into().to_le_bytes(); + data[0..8].copy_from_slice(&value); + Self { data } + } +} + +impl ConsensusEncoding for EncryptedValue { + fn consensus_encode(&self, writer: &mut W) -> Result<(), io::Error> { + self.data.consensus_encode(writer)?; + Ok(()) + } +} + +impl ConsensusDecoding for EncryptedValue { + fn consensus_decode(reader: &mut R) -> Result { + let data = <[u8; 24]>::consensus_decode(reader)?; + Ok(Self { data }) + } +} diff --git a/base_layer/core/src/transactions/transaction_components/mod.rs b/base_layer/core/src/transactions/transaction_components/mod.rs index c36bcd8f28..e5c925cb99 100644 --- a/base_layer/core/src/transactions/transaction_components/mod.rs +++ b/base_layer/core/src/transactions/transaction_components/mod.rs @@ -25,6 +25,7 @@ pub use asset_output_features::AssetOutputFeatures; pub use committee_definition_features::CommitteeDefinitionFeatures; +pub use encrypted_value::EncryptedValue; pub use error::TransactionError; pub use full_rewind_result::FullRewindResult; pub use kernel_builder::KernelBuilder; @@ -53,6 +54,7 @@ pub use unblinded_output_builder::UnblindedOutputBuilder; mod asset_output_features; mod committee_definition_features; +mod encrypted_value; mod error; mod full_rewind_result; mod kernel_builder; diff --git a/base_layer/core/src/transactions/transaction_components/test.rs b/base_layer/core/src/transactions/transaction_components/test.rs index e483d74bf0..ef05ed3332 100644 --- a/base_layer/core/src/transactions/transaction_components/test.rs +++ b/base_layer/core/src/transactions/transaction_components/test.rs @@ -41,7 +41,7 @@ use crate::{ tari_amount::{uT, MicroTari, T}, test_helpers, test_helpers::{create_sender_transaction_protocol_with, create_unblinded_txos, TestParams, UtxoTestParams}, - transaction_components::OutputFeatures, + transaction_components::{EncryptedValue, OutputFeatures}, transaction_protocol::TransactionProtocolError, CryptoFactories, }, @@ -148,6 +148,7 @@ fn range_proof_verification() { .construct_proof(&test_params_2.spend_key, 2u64.pow(32) + 1) .unwrap(); + let encrypted_value = EncryptedValue::todo_encrypt_from(value); let tx_output3 = TransactionOutput::new_current_version( output_features.clone(), c, @@ -162,6 +163,7 @@ fn range_proof_verification() { &output_features, &test_params_2.sender_offset_private_key, &Covenant::default(), + &encrypted_value, ) .unwrap(), Covenant::default(), diff --git a/base_layer/core/src/transactions/transaction_components/transaction_output.rs b/base_layer/core/src/transactions/transaction_components/transaction_output.rs index 13ead45ca8..ef482e7c47 100644 --- a/base_layer/core/src/transactions/transaction_components/transaction_output.rs +++ b/base_layer/core/src/transactions/transaction_components/transaction_output.rs @@ -63,6 +63,7 @@ use crate::{ transaction_components::{ full_rewind_result::FullRewindResult, rewind_result::RewindResult, + EncryptedValue, OutputFeatures, OutputFlags, TransactionError, @@ -172,6 +173,8 @@ impl TransactionOutput { self.metadata_signature.public_nonce(), &self.commitment, &self.covenant, + // TODO: Use `self.encrypted_value` instead + &EncryptedValue::default(), ); if !self.metadata_signature.verify_challenge( &(&self.commitment + &self.sender_offset_public_key), @@ -240,6 +243,7 @@ impl TransactionOutput { &nonce_commitment, &self.commitment, &self.covenant, + &EncryptedValue::default(), ) } @@ -252,6 +256,7 @@ impl TransactionOutput { public_commitment_nonce: &Commitment, commitment: &Commitment, covenant: &Covenant, + _encrypted_value: &EncryptedValue, ) -> Challenge { match version { TransactionOutputVersion::V0 | TransactionOutputVersion::V1 => ConsensusHashWriter::default() @@ -261,6 +266,7 @@ impl TransactionOutput { .chain(sender_offset_public_key) .chain(commitment) .chain(covenant) + //.chain(encrypted_value) .into_digest(), } } @@ -277,6 +283,7 @@ impl TransactionOutput { partial_commitment_nonce: Option<&PublicKey>, sender_offset_private_key: Option<&PrivateKey>, covenant: &Covenant, + _encrypted_value: &EncryptedValue, ) -> Result { let nonce_a = PrivateKey::random(&mut OsRng); let nonce_b = PrivateKey::random(&mut OsRng); @@ -285,8 +292,9 @@ impl TransactionOutput { None => nonce_commitment, Some(partial_nonce) => &nonce_commitment + partial_nonce, }; - let value = PrivateKey::from(value.as_u64()); - let commitment = PedersenCommitmentFactory::default().commit(spending_key, &value); + let pk_value = PrivateKey::from(value.as_u64()); + let commitment = PedersenCommitmentFactory::default().commit(spending_key, &pk_value); + let encrypted_value = EncryptedValue::todo_encrypt_from(value.as_u64()); let e = TransactionOutput::build_metadata_signature_challenge( version, script, @@ -295,13 +303,15 @@ impl TransactionOutput { &nonce_commitment, &commitment, covenant, + &encrypted_value, ); let secret_x = match sender_offset_private_key { None => spending_key.clone(), Some(key) => spending_key + key, }; + // TODO: Take `encrypted_value` into account Ok(ComSignature::sign( - &value, + &pk_value, &secret_x, &nonce_a, &nonce_b, @@ -320,6 +330,7 @@ impl TransactionOutput { sender_offset_public_key: &PublicKey, partial_commitment_nonce: &PublicKey, covenant: &Covenant, + encrypted_value: &EncryptedValue, ) -> Result { TransactionOutput::create_metadata_signature( version, @@ -331,6 +342,7 @@ impl TransactionOutput { Some(partial_commitment_nonce), None, covenant, + encrypted_value, ) } @@ -343,6 +355,7 @@ impl TransactionOutput { output_features: &OutputFeatures, sender_offset_private_key: &PrivateKey, covenant: &Covenant, + encrypted_value: &EncryptedValue, ) -> Result { let sender_offset_public_key = PublicKey::from_secret_key(sender_offset_private_key); TransactionOutput::create_metadata_signature( @@ -355,6 +368,7 @@ impl TransactionOutput { None, Some(sender_offset_private_key), covenant, + encrypted_value, ) } diff --git a/base_layer/core/src/transactions/transaction_components/unblinded_output.rs b/base_layer/core/src/transactions/transaction_components/unblinded_output.rs index 121761754f..9bbf7c32c9 100644 --- a/base_layer/core/src/transactions/transaction_components/unblinded_output.rs +++ b/base_layer/core/src/transactions/transaction_components/unblinded_output.rs @@ -59,6 +59,7 @@ use crate::{ transaction_components::{ transaction_input::{SpentOutput, TransactionInput}, transaction_output::TransactionOutput, + EncryptedValue, OutputFeatures, TransactionError, TransactionInputVersion, @@ -87,6 +88,7 @@ pub struct UnblindedOutput { pub sender_offset_public_key: PublicKey, pub metadata_signature: ComSignature, pub script_lock_height: u64, + pub encrypted_value: EncryptedValue, } impl UnblindedOutput { @@ -104,6 +106,7 @@ impl UnblindedOutput { metadata_signature: ComSignature, script_lock_height: u64, covenant: Covenant, + encrypted_value: EncryptedValue, ) -> Self { Self { version, @@ -117,6 +120,7 @@ impl UnblindedOutput { metadata_signature, script_lock_height, covenant, + encrypted_value, } } @@ -131,6 +135,7 @@ impl UnblindedOutput { metadata_signature: ComSignature, script_lock_height: u64, covenant: Covenant, + encrypted_value: EncryptedValue, ) -> Self { Self::new( TransactionOutputVersion::get_current_version(), @@ -144,6 +149,7 @@ impl UnblindedOutput { metadata_signature, script_lock_height, covenant, + encrypted_value, ) } diff --git a/base_layer/core/src/transactions/transaction_components/unblinded_output_builder.rs b/base_layer/core/src/transactions/transaction_components/unblinded_output_builder.rs index 17c52f4071..6120c8a975 100644 --- a/base_layer/core/src/transactions/transaction_components/unblinded_output_builder.rs +++ b/base_layer/core/src/transactions/transaction_components/unblinded_output_builder.rs @@ -30,6 +30,7 @@ use crate::{ transactions::{ tari_amount::MicroTari, transaction_components::{ + EncryptedValue, OutputFeatures, TransactionError, TransactionOutput, @@ -57,6 +58,7 @@ pub struct UnblindedOutputBuilder { metadata_signature: Option, metadata_signed_by_receiver: bool, metadata_signed_by_sender: bool, + // TODO: Probably add `encrypted_value` here } impl UnblindedOutputBuilder { @@ -93,6 +95,7 @@ impl UnblindedOutputBuilder { ) -> Result<(), TransactionError> { self.sender_offset_public_key = Some(sender_offset_public_key.clone()); + let encrypted_value = EncryptedValue::todo_encrypt_from(self.value); let metadata_partial = TransactionOutput::create_partial_metadata_signature( TransactionOutputVersion::get_current_version(), self.value, @@ -104,6 +107,7 @@ impl UnblindedOutputBuilder { &sender_offset_public_key, &public_nonce_commitment, &self.covenant, + &encrypted_value, )?; self.metadata_signature = Some(metadata_partial); self.metadata_signed_by_receiver = true; @@ -111,6 +115,7 @@ impl UnblindedOutputBuilder { } pub fn sign_as_sender(&mut self, sender_offset_private_key: &PrivateKey) -> Result<(), TransactionError> { + let encrypted_value = EncryptedValue::todo_encrypt_from(self.value); let metadata_sig = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), self.value, @@ -121,6 +126,7 @@ impl UnblindedOutputBuilder { &self.features, sender_offset_private_key, &self.covenant, + &encrypted_value, )?; self.metadata_signature = Some(metadata_sig); self.metadata_signed_by_sender = true; @@ -138,6 +144,7 @@ impl UnblindedOutputBuilder { "Cannot build output because it has not been signed by the sender".to_string(), )); } + let encrypted_value = EncryptedValue::todo_encrypt_from(self.value); let ub = UnblindedOutput::new_current_version( self.value, self.spending_key, @@ -154,6 +161,7 @@ impl UnblindedOutputBuilder { .ok_or_else(|| TransactionError::ValidationError("metadata_signature must be set".to_string()))?, 0, self.covenant, + encrypted_value, ); Ok(ub) } diff --git a/base_layer/core/src/transactions/transaction_protocol/sender.rs b/base_layer/core/src/transactions/transaction_protocol/sender.rs index df3fc94a8d..20418ba372 100644 --- a/base_layer/core/src/transactions/transaction_protocol/sender.rs +++ b/base_layer/core/src/transactions/transaction_protocol/sender.rs @@ -784,6 +784,7 @@ mod test { tari_amount::*, test_helpers::{create_test_input, create_unblinded_output, TestParams}, transaction_components::{ + EncryptedValue, KernelFeatures, OutputFeatures, TransactionError, @@ -887,6 +888,7 @@ mod test { .unwrap(); let covenant = Covenant::default(); + let encrypted_value = EncryptedValue::todo_encrypt_from(value); let partial_metadata_signature = TransactionOutput::create_partial_metadata_signature( TransactionOutputVersion::get_current_version(), value.into(), @@ -896,6 +898,7 @@ mod test { &sender_offset_public_key, &sender_public_commitment_nonce, &covenant, + &encrypted_value, ) .unwrap(); diff --git a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs index 5c0b09de7f..f3fd903738 100644 --- a/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs +++ b/base_layer/core/src/transactions/transaction_protocol/single_receiver.rs @@ -30,7 +30,7 @@ use tari_crypto::{ use crate::transactions::{ crypto_factories::CryptoFactories, - transaction_components::{OutputFeatures, TransactionOutput, TransactionOutputVersion}, + transaction_components::{EncryptedValue, OutputFeatures, TransactionOutput, TransactionOutputVersion}, transaction_protocol::{ build_challenge, recipient::RecipientSignedMessage as RD, @@ -110,6 +110,7 @@ impl SingleReceiverTransactionProtocol { &sender_info.features.clone(), ); + let encrypted_value = EncryptedValue::todo_encrypt_from(sender_info.amount); let partial_metadata_signature = TransactionOutput::create_partial_metadata_signature( TransactionOutputVersion::get_current_version(), sender_info.amount, @@ -119,6 +120,7 @@ impl SingleReceiverTransactionProtocol { &sender_info.sender_offset_public_key, &sender_info.public_commitment_nonce, &sender_info.covenant, + &encrypted_value, )?; let output = TransactionOutput::new_current_version( diff --git a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs index c908ef1144..6a91d4946f 100644 --- a/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs +++ b/base_layer/core/src/transactions/transaction_protocol/transaction_initializer.rs @@ -49,6 +49,7 @@ use crate::{ fee::Fee, tari_amount::*, transaction_components::{ + EncryptedValue, OutputFeatures, TransactionInput, TransactionOutput, @@ -235,6 +236,7 @@ impl SenderTransactionInitializer { output.metadata_signature.public_nonce(), &commitment, &output.covenant, + &output.encrypted_value, ); if !output.metadata_signature.verify_challenge( &(&commitment + &output.sender_offset_public_key), @@ -392,6 +394,7 @@ impl SenderTransactionInitializer { .ok_or("Change spending key was not provided")?; let commitment = factories.commitment.commit_value(&change_key.clone(), v.as_u64()); output_features.update_recovery_byte(&commitment, self.rewind_data.as_ref()); + let encrypted_value = EncryptedValue::todo_encrypt_from(v); let metadata_signature = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), v, @@ -400,6 +403,7 @@ impl SenderTransactionInitializer { &output_features, &change_sender_offset_private_key, &self.change_covenant, + &encrypted_value, ) .map_err(|e| e.to_string())?; let change_unblinded_output = UnblindedOutput::new_current_version( @@ -419,6 +423,7 @@ impl SenderTransactionInitializer { metadata_signature, 0, self.change_covenant.clone(), + encrypted_value, ); Ok((fee_without_change + change_fee, v, Some(change_unblinded_output))) }, diff --git a/base_layer/core/tests/node_comms_interface.rs b/base_layer/core/tests/node_comms_interface.rs index 1038028576..faef5750a5 100644 --- a/base_layer/core/tests/node_comms_interface.rs +++ b/base_layer/core/tests/node_comms_interface.rs @@ -42,7 +42,13 @@ use tari_core::{ transactions::{ tari_amount::MicroTari, test_helpers::{create_utxo, spend_utxos}, - transaction_components::{OutputFeatures, TransactionOutput, TransactionOutputVersion, UnblindedOutput}, + transaction_components::{ + EncryptedValue, + OutputFeatures, + TransactionOutput, + TransactionOutputVersion, + UnblindedOutput, + }, CryptoFactories, }, txn_schema, @@ -351,25 +357,22 @@ async fn inbound_fetch_blocks_before_horizon_height() { connectivity, ); let script = script!(Nop); - let (utxo, key, offset) = create_utxo( - MicroTari(10_000), - &factories, - &Default::default(), - &script, - &Covenant::default(), - ); + let amount = MicroTari(10_000); + let (utxo, key, offset) = create_utxo(amount, &factories, &Default::default(), &script, &Covenant::default()); + let encrypted_value = EncryptedValue::todo_encrypt_from(amount); let metadata_signature = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), - MicroTari(10_000), + amount, &key, &script, &OutputFeatures::default(), &offset, &Covenant::default(), + &encrypted_value, ) .unwrap(); let unblinded_output = UnblindedOutput::new_current_version( - MicroTari(10_000), + amount, key.clone(), Default::default(), script, @@ -379,6 +382,7 @@ async fn inbound_fetch_blocks_before_horizon_height() { metadata_signature, 0, Covenant::default(), + encrypted_value, ); let mut txn = DbTransaction::new(); txn.insert_utxo(utxo.clone(), block0.hash().clone(), 0, 4002); diff --git a/base_layer/wallet/migrations/2022-05-25-154612_add_encrypted_value/down.sql b/base_layer/wallet/migrations/2022-05-25-154612_add_encrypted_value/down.sql new file mode 100644 index 0000000000..d9a93fe9a1 --- /dev/null +++ b/base_layer/wallet/migrations/2022-05-25-154612_add_encrypted_value/down.sql @@ -0,0 +1 @@ +-- This file should undo anything in `up.sql` diff --git a/base_layer/wallet/migrations/2022-05-25-154612_add_encrypted_value/up.sql b/base_layer/wallet/migrations/2022-05-25-154612_add_encrypted_value/up.sql new file mode 100644 index 0000000000..8983f479a4 --- /dev/null +++ b/base_layer/wallet/migrations/2022-05-25-154612_add_encrypted_value/up.sql @@ -0,0 +1,47 @@ +PRAGMA foreign_keys=OFF; + +ALTER TABLE outputs + RENAME TO outputs_old; + +CREATE TABLE outputs +( + id INTEGER NOT NULL PRIMARY KEY, --auto inc, + commitment BLOB NULL, + spending_key BLOB NOT NULL, + value BIGINT NOT NULL, + flags INTEGER NOT NULL, + maturity BIGINT NOT NULL, + recovery_byte INTEGER NOT NULL DEFAULT 0, + status INTEGER NOT NULL, + hash BLOB NULL, + script BLOB NOT NULL, + input_data BLOB NOT NULL, + script_private_key BLOB NOT NULL, + script_lock_height UNSIGNED BIGINT NOT NULL DEFAULT 0, + sender_offset_public_key BLOB NOT NULL, + metadata_signature_nonce BLOB NOT NULL, + metadata_signature_u_key BLOB NOT NULL, + metadata_signature_v_key BLOB NOT NULL, + mined_height UNSIGNED BIGINT NULL, + mined_in_block BLOB NULL, + mined_mmr_position BIGINT NULL, + marked_deleted_at_height BIGINT, + marked_deleted_in_block BLOB, + received_in_tx_id BIGINT, + spent_in_tx_id BIGINT, + coinbase_block_height UNSIGNED BIGINT NULL, + metadata BLOB, + features_parent_public_key BLOB, + features_unique_id BLOB, + features_json TEXT NOT NULL DEFAULT '{}', + spending_priority UNSIGNED Integer NOT NULL DEFAULT 500, + covenant BLOB NOT NULL DEFAULT '', + encrypted_value BLOB NOT NULL, + CONSTRAINT unique_commitment UNIQUE (commitment) +); + +INSERT INTO outputs +SELECT *, NULL +FROM outputs_old; +DROP TABLE outputs_old; +PRAGMA foreign_keys=ON; diff --git a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs index ac16915d12..0adb9f1663 100644 --- a/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs +++ b/base_layer/wallet/src/output_manager_service/recovery/standard_outputs_recoverer.rs @@ -29,7 +29,7 @@ use tari_common_types::{ types::{BulletRangeProof, PrivateKey, PublicKey}, }; use tari_core::transactions::{ - transaction_components::{TransactionOutput, UnblindedOutput}, + transaction_components::{EncryptedValue, TransactionOutput, UnblindedOutput}, transaction_protocol::RewindData, CryptoFactories, }; @@ -105,6 +105,7 @@ where return None; } let script_key = PrivateKey::random(&mut OsRng); + let encrypted_value = EncryptedValue::todo_encrypt_from(rewind_result.committed_value); Some(( UnblindedOutput::new( output.version, @@ -118,6 +119,7 @@ where output.metadata_signature, 0, output.covenant, + encrypted_value, ), output.proof, )) diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index bdc37b2124..e4888f8baf 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -40,6 +40,7 @@ use tari_core::{ fee::Fee, tari_amount::MicroTari, transaction_components::{ + EncryptedValue, KernelFeatures, OutputFeatures, Transaction, @@ -754,6 +755,7 @@ where Some(&self.resources.rewind_data), &single_round_sender_data.features.clone(), ); + let encrypted_value = EncryptedValue::todo_encrypt_from(single_round_sender_data.amount); let output = DbUnblindedOutput::rewindable_from_unblinded_output( UnblindedOutput::new_current_version( single_round_sender_data.amount, @@ -774,9 +776,11 @@ where &single_round_sender_data.sender_offset_public_key, &single_round_sender_data.public_commitment_nonce, &single_round_sender_data.covenant, + &encrypted_value, )?, 0, single_round_sender_data.covenant.clone(), + encrypted_value, ), &self.resources.factories, &self.resources.rewind_data, @@ -1304,6 +1308,7 @@ where unique_id: unique_id.clone(), ..Default::default() }; + let encrypted_value = EncryptedValue::todo_encrypt_from(amount); let metadata_signature = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), amount, @@ -1312,6 +1317,7 @@ where &output_features, &sender_offset_private_key, &covenant, + &encrypted_value, )?; let utxo = DbUnblindedOutput::rewindable_from_unblinded_output( UnblindedOutput::new_current_version( @@ -1325,6 +1331,7 @@ where metadata_signature, 0, covenant, + encrypted_value, ), &self.resources.factories, &self.resources.rewind_data, @@ -1653,6 +1660,7 @@ where let sender_offset_private_key = PrivateKey::random(&mut OsRng); let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); + let encrypted_value = EncryptedValue::todo_encrypt_from(output_amount); let metadata_signature = TransactionOutput::create_final_metadata_signature( TransactionOutputVersion::get_current_version(), output_amount, @@ -1661,6 +1669,7 @@ where &output_features, &sender_offset_private_key, &covenant, + &encrypted_value, )?; let utxo = DbUnblindedOutput::rewindable_from_unblinded_output( UnblindedOutput::new_current_version( @@ -1674,6 +1683,7 @@ where metadata_signature, 0, covenant.clone(), + encrypted_value, ), &self.resources.factories, &self.resources.rewind_data.clone(), @@ -1788,6 +1798,7 @@ where let rewound = output.full_rewind_range_proof(&self.resources.factories.range_proof, &rewind_key, &blinding_key)?; + let encrypted_value = EncryptedValue::todo_encrypt_from(rewound.committed_value); let rewound_output = UnblindedOutput::new( output.version, rewound.committed_value, @@ -1802,6 +1813,7 @@ where // as we are claiming the Hashed part which has a 0 time lock 0, output.covenant, + encrypted_value, ); let amount = rewound.committed_value; @@ -2007,6 +2019,7 @@ where ); if let Ok(rewound_result) = rewound { + let encrypted_value = EncryptedValue::todo_encrypt_from(rewound_result.committed_value); let rewound_output = UnblindedOutput::new( output.version, rewound_result.committed_value, @@ -2019,6 +2032,7 @@ where output.metadata_signature, known_one_sided_payment_scripts[i].script_lock_height, output.covenant, + encrypted_value, ); let db_output = DbUnblindedOutput::rewindable_from_unblinded_output( diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs index 8abcd0220e..a98e9a08c4 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/new_output_sql.rs @@ -64,6 +64,7 @@ pub struct NewOutputSql { pub coinbase_block_height: Option, pub features_json: String, pub covenant: Vec, + pub encrypted_value: Vec, } impl NewOutputSql { @@ -106,6 +107,7 @@ impl NewOutputSql { } })?, covenant: output.unblinded_output.covenant.to_bytes(), + encrypted_value: output.unblinded_output.encrypted_value.to_vec(), }) } @@ -155,6 +157,7 @@ impl From for NewOutputSql { coinbase_block_height: o.coinbase_block_height, features_json: o.features_json, covenant: o.covenant, + encrypted_value: o.encrypted_value, } } } diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index 5e6fe09d95..5d81f7358f 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -34,7 +34,7 @@ use tari_core::{ covenants::Covenant, transactions::{ tari_amount::MicroTari, - transaction_components::{OutputFeatures, OutputFlags, SideChainFeatures, UnblindedOutput}, + transaction_components::{EncryptedValue, OutputFeatures, OutputFlags, SideChainFeatures, UnblindedOutput}, CryptoFactories, }, }; @@ -98,6 +98,7 @@ pub struct OutputSql { pub features_json: String, pub spending_priority: i32, pub covenant: Vec, + pub encrypted_value: Vec, } impl OutputSql { @@ -527,6 +528,7 @@ impl TryFrom for DbUnblindedOutput { .map(|p| PublicKey::from_bytes(&p)) .transpose()?; features.recovery_byte = u8::try_from(o.recovery_byte).unwrap(); + let encrypted_value = EncryptedValue::from_bytes(&o.encrypted_value)?; let unblinded_output = UnblindedOutput::new_current_version( MicroTari::from(o.value as u64), PrivateKey::from_vec(&o.spending_key).map_err(|_| { @@ -598,6 +600,7 @@ impl TryFrom for DbUnblindedOutput { reason: "Covenant could not be converted from bytes".to_string(), } })?, + encrypted_value, ); let hash = match o.hash { diff --git a/base_layer/wallet/src/schema.rs b/base_layer/wallet/src/schema.rs index a870315deb..b746e3d5b6 100644 --- a/base_layer/wallet/src/schema.rs +++ b/base_layer/wallet/src/schema.rs @@ -135,6 +135,7 @@ table! { features_json -> Text, spending_priority -> Integer, covenant -> Binary, + encrypted_value -> Binary, } } diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 03da3cf4cd..5739f2b498 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -45,7 +45,7 @@ use tari_core::{ proto::base_node as base_node_proto, transactions::{ tari_amount::MicroTari, - transaction_components::{KernelFeatures, Transaction, TransactionOutput, UnblindedOutput}, + transaction_components::{EncryptedValue, KernelFeatures, Transaction, TransactionOutput, UnblindedOutput}, transaction_protocol::{ proto::protocol as proto, recipient::RecipientSignedMessage, @@ -1042,6 +1042,7 @@ where let recipient_reply = rtp.get_signed_data()?.clone(); let output = recipient_reply.output.clone(); + let encrypted_value = EncryptedValue::todo_encrypt_from(amount); let unblinded_output = UnblindedOutput::new_current_version( amount, spend_key, @@ -1053,6 +1054,7 @@ where output.metadata_signature.clone(), height, covenant, + encrypted_value, ); // Start finalizing diff --git a/base_layer/wallet/src/wallet.rs b/base_layer/wallet/src/wallet.rs index 86f9da1252..2cc10c30f2 100644 --- a/base_layer/wallet/src/wallet.rs +++ b/base_layer/wallet/src/wallet.rs @@ -43,7 +43,7 @@ use tari_core::{ covenants::Covenant, transactions::{ tari_amount::MicroTari, - transaction_components::{OutputFeatures, UnblindedOutput}, + transaction_components::{EncryptedValue, OutputFeatures, UnblindedOutput}, CryptoFactories, }, }; @@ -422,6 +422,7 @@ where script_lock_height: u64, covenant: Covenant, ) -> Result { + let encrypted_value = EncryptedValue::todo_encrypt_from(amount); let unblinded_output = UnblindedOutput::new_current_version( amount, spending_key.clone(), @@ -433,6 +434,7 @@ where metadata_signature, script_lock_height, covenant, + encrypted_value, ); let tx_id = self diff --git a/base_layer/wallet/tests/output_manager_service_tests/service.rs b/base_layer/wallet/tests/output_manager_service_tests/service.rs index fe4e8d085d..43c74a34ca 100644 --- a/base_layer/wallet/tests/output_manager_service_tests/service.rs +++ b/base_layer/wallet/tests/output_manager_service_tests/service.rs @@ -41,7 +41,7 @@ use tari_core::{ fee::Fee, tari_amount::{uT, MicroTari}, test_helpers::{create_unblinded_output, TestParams as TestParamsHelpers}, - transaction_components::{OutputFeatures, OutputFlags, TransactionOutput, UnblindedOutput}, + transaction_components::{EncryptedValue, OutputFeatures, OutputFlags, TransactionOutput, UnblindedOutput}, transaction_protocol::{sender::TransactionSenderMessage, RewindData}, weight::TransactionWeight, CryptoFactories, @@ -2048,6 +2048,7 @@ async fn test_get_status_by_tx_id() { } #[tokio::test] +#[allow(clippy::too_many_lines)] async fn scan_for_recovery_test() { let factories = CryptoFactories::default(); let (connection, _tempdir) = get_temp_sqlite_database_connection(); @@ -2074,13 +2075,13 @@ async fn scan_for_recovery_test() { ) .await .unwrap(); - let commitment = factories - .commitment - .commit_value(&spending_key_result.key, 1000 * i as u64); + let amount = 1_000 * i as u64; + let commitment = factories.commitment.commit_value(&spending_key_result.key, amount); let mut features = OutputFeatures::default(); features.update_recovery_byte(&commitment, Some(&oms.rewind_data)); + let encrypted_value = EncryptedValue::todo_encrypt_from(amount); let uo = UnblindedOutput::new_current_version( - MicroTari::from(1000 * i as u64), + MicroTari::from(amount), spending_key_result.key, features, script!(Nop), @@ -2090,6 +2091,7 @@ async fn scan_for_recovery_test() { ComSignature::default(), 0, Covenant::new(), + encrypted_value, ); rewindable_unblinded_outputs.push(uo); } diff --git a/common/src/configuration/network.rs b/common/src/configuration/network.rs index 51a0b79c30..83c955d8d6 100644 --- a/common/src/configuration/network.rs +++ b/common/src/configuration/network.rs @@ -130,13 +130,13 @@ mod test { let dibbler = Network::Dibbler; // test .as_byte() - assert_eq!(mainnet.as_byte(), 0x00 as u8); - assert_eq!(localnet.as_byte(), 0x10 as u8); - assert_eq!(ridcully.as_byte(), 0x21 as u8); - assert_eq!(stibbons.as_byte(), 0x22 as u8); - assert_eq!(weatherwas.as_byte(), 0xa3 as u8); - assert_eq!(igor.as_byte(), 0x24 as u8); - assert_eq!(dibbler.as_byte(), 0x25 as u8); + assert_eq!(mainnet.as_byte(), 0x00_u8); + assert_eq!(localnet.as_byte(), 0x10_u8); + assert_eq!(ridcully.as_byte(), 0x21_u8); + assert_eq!(stibbons.as_byte(), 0x22_u8); + assert_eq!(weatherwas.as_byte(), 0xa3_u8); + assert_eq!(igor.as_byte(), 0x24_u8); + assert_eq!(dibbler.as_byte(), 0x25_u8); // test .as_key_str() assert_eq!(mainnet.as_key_str(), "mainnet");