From a8e2e00c09673b0a692f831e20fefd8652ce3572 Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Wed, 30 Nov 2022 08:57:21 +0200 Subject: [PATCH] fix(wallet): invalid metadata sig when creating code template utxo (#4975) Description --- - fixes invalid metadata signature error when submitting transaction with custom outputs e.g. CodeTemplateRegistration Motivation and Context --- `create_send_to_self_with_output` generates a transaction with an invalid metadata signature. This function is used by `create_template_registration` in wallet GRPC. In order to send a send-to-self transaction, the UnblindedOutputBuilder needs to create a complete signature for the new output. The UnblindedOutputBuilder is only currently used in create_pay_to_self_containing_outputs calls. How Has This Been Tested? --- Manually, able to submit a valid template reg utxo --- Cargo.lock | 1 + applications/tari_console_wallet/Cargo.toml | 1 + .../src/grpc/wallet_grpc_server.rs | 5 +- .../unblinded_output_builder.rs | 159 ++++++------------ .../src/output_manager_service/service.rs | 8 +- 5 files changed, 62 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c9e93dba1..329ff1f6c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4898,6 +4898,7 @@ dependencies = [ "tari_key_manager", "tari_libtor", "tari_p2p", + "tari_script", "tari_shutdown", "tari_utilities", "tari_wallet", diff --git a/applications/tari_console_wallet/Cargo.toml b/applications/tari_console_wallet/Cargo.toml index 99301fe157..e0ea471ea4 100644 --- a/applications/tari_console_wallet/Cargo.toml +++ b/applications/tari_console_wallet/Cargo.toml @@ -19,6 +19,7 @@ tari_app_grpc = { path = "../tari_app_grpc" } tari_shutdown = { path = "../../infrastructure/shutdown" } tari_key_manager = { path = "../../base_layer/key_manager" } tari_utilities = { git = "https://github.com/tari-project/tari_utilities.git", tag = "v0.4.10" } +tari_script = { path = "../../infrastructure/tari_script" } # Uncomment for tokio tracing via tokio-console (needs "tracing" featurs) #console-subscriber = "0.1.3" diff --git a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs index 94fe7da849..e4608e61b9 100644 --- a/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/tari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -99,6 +99,7 @@ use tari_core::{ }, }, }; +use tari_script::script; use tari_utilities::{hex::Hex, ByteArray}; use tari_wallet::{ connectivity_service::{OnlineStatus, WalletConnectivityInterface}, @@ -929,7 +930,7 @@ impl wallet_server::Wallet for WalletGrpcServer { let fee_per_gram = message.fee_per_gram; let message = format!("Template registration {}", template_registration.template_name); - let output = output_manager + let mut output = output_manager .create_output_with_features(1 * T, OutputFeatures { output_type: OutputType::CodeTemplateRegistration, sidechain_feature: Some(SideChainFeature::TemplateRegistration(template_registration)), @@ -938,6 +939,8 @@ impl wallet_server::Wallet for WalletGrpcServer { .await .map_err(|e| Status::internal(e.to_string()))?; + output = output.with_script(script![Nop]); + let (tx_id, transaction) = output_manager .create_send_to_self_with_output(vec![output], fee_per_gram.into(), UtxoSelectionCriteria::default()) .await 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 6c4d17aea4..e37b8cbfc5 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 @@ -21,15 +21,8 @@ // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use derivative::Derivative; -use tari_common_types::types::{ - BlindingFactor, - ComAndPubSignature, - Commitment, - CommitmentFactory, - PrivateKey, - PublicKey, -}; -use tari_crypto::commitment::HomomorphicCommitmentFactory; +use tari_common_types::types::{BlindingFactor, ComAndPubSignature, PrivateKey, PublicKey}; +use tari_crypto::keys::PublicKey as PublicKeyTrait; use tari_script::{ExecutionStack, TariScript}; use crate::{ @@ -45,7 +38,6 @@ use crate::{ UnblindedOutput, }, transaction_protocol::RewindData, - CryptoFactories, }, }; @@ -94,56 +86,69 @@ impl UnblindedOutputBuilder { self.sender_offset_public_key = Some(sender_offset_public_key); } - pub fn sign_as_receiver(&mut self, ephemeral_pubkey: PublicKey) -> Result<(), TransactionError> { - let sender_offset_public_key = self - .sender_offset_public_key - .as_ref() - .ok_or(TransactionError::MissingTransactionInputData)?; - let metadata_partial = TransactionOutput::create_receiver_partial_metadata_signature( - TransactionOutputVersion::get_current_version(), - self.value, - &self.spending_key, - self.script - .as_ref() - .ok_or_else(|| TransactionError::ValidationError("script must be set".to_string()))?, - &self.features, - sender_offset_public_key, - &ephemeral_pubkey, - &self.covenant, - &self.encrypted_value, - self.minimum_value_promise, - )?; - self.metadata_signature = Some(metadata_partial); - self.metadata_signed_by_receiver = true; - Ok(()) + pub fn with_features(mut self, features: OutputFeatures) -> Self { + self.features = features; + self + } + + pub fn with_script(mut self, script: TariScript) -> Self { + self.script = Some(script); + self + } + + pub fn with_input_data(mut self, input_data: ExecutionStack) -> Self { + self.input_data = Some(input_data); + self + } + + pub fn with_rewind_data(mut self, rewind_data: RewindData) -> Self { + self.rewind_data = Some(rewind_data); + self + } + + pub fn with_script_private_key(mut self, script_private_key: PrivateKey) -> Self { + self.script_private_key = Some(script_private_key); + self + } + + pub fn value(&self) -> MicroTari { + self.value + } + + pub fn features(&self) -> &OutputFeatures { + &self.features + } + + pub fn script(&self) -> Option<&TariScript> { + self.script.as_ref() + } + + pub fn covenant(&self) -> &Covenant { + &self.covenant } - pub fn sign_as_sender( + pub fn sign_as_sender_and_receiver( &mut self, sender_offset_private_key: &PrivateKey, - ephemeral_private_key: &PrivateKey, ) -> Result<(), TransactionError> { - let commitment = CommitmentFactory::default().commit_value(&self.spending_key, self.value.as_u64()); - let def_commitment = Commitment::default(); - let ephemeral_commitment = match self.metadata_signature.as_ref().map(|v| v.ephemeral_commitment()) { - Some(v) => v, - None => &def_commitment, - }; - let metadata_sig = TransactionOutput::create_sender_partial_metadata_signature( + let script = self + .script + .as_ref() + .ok_or_else(|| TransactionError::ValidationError("Cannot sign metadata without a script".to_string()))?; + let metadata_signature = TransactionOutput::create_metadata_signature( TransactionOutputVersion::get_current_version(), - &commitment, - ephemeral_commitment, - self.script - .as_ref() - .ok_or_else(|| TransactionError::ValidationError("script must be set".to_string()))?, + self.value, + &self.spending_key, + script, &self.features, sender_offset_private_key, - Some(ephemeral_private_key), &self.covenant, &self.encrypted_value, self.minimum_value_promise, )?; - self.metadata_signature = Some(metadata_sig); + self.sender_offset_public_key = Some(PublicKey::from_secret_key(sender_offset_private_key)); + self.metadata_signature = Some(metadata_signature); + self.metadata_signed_by_receiver = true; self.metadata_signed_by_sender = true; Ok(()) } @@ -180,53 +185,6 @@ impl UnblindedOutputBuilder { ); Ok(ub) } - - pub fn with_features(mut self, features: OutputFeatures) -> Self { - self.features = features; - self - } - - pub fn with_script(mut self, script: TariScript) -> Self { - self.script = Some(script); - self - } - - pub fn with_input_data(mut self, input_data: ExecutionStack) -> Self { - self.input_data = Some(input_data); - self - } - - pub fn with_rewind_data(mut self, rewind_data: RewindData) -> Self { - self.rewind_data = Some(rewind_data); - self - } - - pub fn with_script_private_key(mut self, script_private_key: PrivateKey) -> Self { - self.script_private_key = Some(script_private_key); - self - } - - pub fn value(&self) -> MicroTari { - self.value - } - - pub fn features(&self) -> &OutputFeatures { - &self.features - } - - pub fn script(&self) -> Option<&TariScript> { - self.script.as_ref() - } - - pub fn covenant(&self) -> &Covenant { - &self.covenant - } - - pub fn generate_commitment(&self, factories: &CryptoFactories) -> Commitment { - factories - .commitment - .commit_value(&self.spending_key, self.value.as_u64()) - } } #[cfg(test)] @@ -237,19 +195,12 @@ mod test { #[test] fn test_try_build() { - let mut uob = UnblindedOutputBuilder::new(100.into(), RistrettoSecretKey::default()); - assert!(uob.sign_as_receiver(PublicKey::default(),).is_err()); - assert!(uob - .sign_as_sender(&PrivateKey::default(), &PrivateKey::default()) - .is_err()); + let uob = UnblindedOutputBuilder::new(100.into(), RistrettoSecretKey::default()); let mut uob = uob.with_script(TariScript::new(vec![])); assert!(uob.clone().try_build().is_err()); uob.with_sender_offset_public_key(PublicKey::default()); - assert!(uob.sign_as_receiver(PublicKey::default()).is_ok()); + assert!(uob.sign_as_sender_and_receiver(&PrivateKey::default()).is_ok()); assert!(uob.clone().try_build().is_err()); - assert!(uob - .sign_as_sender(&PrivateKey::default(), &PrivateKey::default()) - .is_ok()); let uob = uob.with_input_data(ExecutionStack::new(vec![])); let uob = uob.with_script_private_key(RistrettoSecretKey::default()); let uob = uob.with_features(OutputFeatures::default()); diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 33014b2e05..5b644f2fe9 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -1187,13 +1187,7 @@ where let mut db_outputs = vec![]; for mut unblinded_output in outputs { let sender_offset_private_key = PrivateKey::random(&mut OsRng); - let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); - unblinded_output.with_sender_offset_public_key(sender_offset_public_key); - let ephemeral_private_key = PrivateKey::random(&mut OsRng); - let ephemeral_pub_key = PublicKey::from_secret_key(&ephemeral_private_key); - - unblinded_output.sign_as_receiver(ephemeral_pub_key)?; - unblinded_output.sign_as_sender(&sender_offset_private_key, &ephemeral_private_key)?; + unblinded_output.sign_as_sender_and_receiver(&sender_offset_private_key)?; let ub = unblinded_output.try_build()?; builder