Skip to content

Commit

Permalink
fix(wallet): invalid metadata sig when creating code template utxo (#…
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
sdbondi authored Nov 30, 2022
1 parent 8455ce8 commit a8e2e00
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 112 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions applications/tari_console_wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ use tari_core::{
},
},
};
use tari_script::script;
use tari_utilities::{hex::Hex, ByteArray};
use tari_wallet::{
connectivity_service::{OnlineStatus, WalletConnectivityInterface},
Expand Down Expand Up @@ -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)),
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -45,7 +38,6 @@ use crate::{
UnblindedOutput,
},
transaction_protocol::RewindData,
CryptoFactories,
},
};

Expand Down Expand Up @@ -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(())
}
Expand Down Expand Up @@ -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)]
Expand All @@ -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());
Expand Down
8 changes: 1 addition & 7 deletions base_layer/wallet/src/output_manager_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit a8e2e00

Please sign in to comment.