From 4bdd3643855e7d47b035a27d61a989291862fb03 Mon Sep 17 00:00:00 2001 From: Stan Bondi Date: Fri, 8 Sep 2023 13:21:43 +0400 Subject: [PATCH] feat!: add claim_public_key to sidechain features (#5619) Description --- Add claim public key to validator node registration Motivation and Context --- This claim key is informational and will be used in L2 to restrict fee claims to a particular signing key How Has This Been Tested? --- Existing tests What process can a PR reviewer use to test or verify this change? --- Run register validator node wallet command Breaking Changes --- - [ ] None - [x] Requires data directory on base node to be deleted - [x] Requires hard fork - [ ] Other - Please specify --- .../proto/sidechain_types.proto | 1 + .../minotari_app_grpc/proto/wallet.proto | 5 +-- .../src/conversions/sidechain_feature.rs | 22 ++++++++----- .../src/automation/commands.rs | 3 ++ .../minotari_console_wallet/src/cli.rs | 1 + .../src/grpc/wallet_grpc_server.rs | 5 ++- .../tests/blockchain_database.rs | 9 ++++-- .../core/src/proto/sidechain_feature.proto | 1 + .../core/src/proto/sidechain_feature.rs | 22 ++++++++----- .../transaction_components/output_features.rs | 10 +++--- .../side_chain/validator_node_registration.rs | 31 +++++++++++++------ .../side_chain/validator_node_signature.rs | 21 ++++++++++--- .../wallet/src/transaction_service/handle.rs | 3 ++ .../wallet/src/transaction_service/service.rs | 10 ++++-- 14 files changed, 102 insertions(+), 42 deletions(-) diff --git a/applications/minotari_app_grpc/proto/sidechain_types.proto b/applications/minotari_app_grpc/proto/sidechain_types.proto index 184a321d11..0d51db0d2b 100644 --- a/applications/minotari_app_grpc/proto/sidechain_types.proto +++ b/applications/minotari_app_grpc/proto/sidechain_types.proto @@ -36,6 +36,7 @@ message SideChainFeature { message ValidatorNodeRegistration { bytes public_key = 1; Signature signature = 2; + bytes claim_public_key = 3; } message TemplateRegistration { diff --git a/applications/minotari_app_grpc/proto/wallet.proto b/applications/minotari_app_grpc/proto/wallet.proto index 2df811fee6..382d6af54a 100644 --- a/applications/minotari_app_grpc/proto/wallet.proto +++ b/applications/minotari_app_grpc/proto/wallet.proto @@ -339,8 +339,9 @@ message TransactionEventResponse { message RegisterValidatorNodeRequest { bytes validator_node_public_key = 1; Signature validator_node_signature = 2; - uint64 fee_per_gram = 3; - string message = 4; + bytes validator_node_claim_public_key = 3; + uint64 fee_per_gram = 4; + string message = 5; } message RegisterValidatorNodeResponse { diff --git a/applications/minotari_app_grpc/src/conversions/sidechain_feature.rs b/applications/minotari_app_grpc/src/conversions/sidechain_feature.rs index 90f5da2ad4..d73174a113 100644 --- a/applications/minotari_app_grpc/src/conversions/sidechain_feature.rs +++ b/applications/minotari_app_grpc/src/conversions/sidechain_feature.rs @@ -87,13 +87,20 @@ impl TryFrom for ValidatorNodeRegistration { type Error = String; fn try_from(value: grpc::ValidatorNodeRegistration) -> Result { - Ok(ValidatorNodeRegistration::new(ValidatorNodeSignature::new( - PublicKey::from_bytes(&value.public_key).map_err(|e| e.to_string())?, - value - .signature - .map(Signature::try_from) - .ok_or("signature not provided")??, - ))) + let public_key = PublicKey::from_bytes(&value.public_key).map_err(|e| format!("Invalid public key: {}", e))?; + let claim_public_key = + PublicKey::from_bytes(&value.claim_public_key).map_err(|e| format!("Invalid claim public key: {}", e))?; + + Ok(ValidatorNodeRegistration::new( + ValidatorNodeSignature::new( + public_key, + value + .signature + .map(Signature::try_from) + .ok_or("signature not provided")??, + ), + claim_public_key, + )) } } @@ -102,6 +109,7 @@ impl From for grpc::ValidatorNodeRegistration { Self { public_key: value.public_key().to_vec(), signature: Some(value.signature().into()), + claim_public_key: value.claim_public_key().to_vec(), } } } diff --git a/applications/minotari_console_wallet/src/automation/commands.rs b/applications/minotari_console_wallet/src/automation/commands.rs index ed20171dde..e22251acec 100644 --- a/applications/minotari_console_wallet/src/automation/commands.rs +++ b/applications/minotari_console_wallet/src/automation/commands.rs @@ -202,6 +202,7 @@ pub async fn register_validator_node( mut wallet_transaction_service: TransactionServiceHandle, validator_node_public_key: PublicKey, validator_node_signature: Signature, + validator_node_claim_public_key: PublicKey, selection_criteria: UtxoSelectionCriteria, fee_per_gram: MicroMinotari, message: String, @@ -211,6 +212,7 @@ pub async fn register_validator_node( amount, validator_node_public_key, validator_node_signature, + validator_node_claim_public_key, selection_criteria, fee_per_gram, message, @@ -1001,6 +1003,7 @@ pub async fn command_runner( args.validator_node_public_nonce.into(), RistrettoSecretKey::from_vec(&args.validator_node_signature)?, ), + args.validator_node_claim_public_key.into(), UtxoSelectionCriteria::default(), config.fee_per_gram * uT, args.message, diff --git a/applications/minotari_console_wallet/src/cli.rs b/applications/minotari_console_wallet/src/cli.rs index a92346081f..6efbdb3d46 100644 --- a/applications/minotari_console_wallet/src/cli.rs +++ b/applications/minotari_console_wallet/src/cli.rs @@ -293,6 +293,7 @@ pub struct RegisterValidatorNodeArgs { pub validator_node_public_key: UniPublicKey, pub validator_node_public_nonce: UniPublicKey, pub validator_node_signature: Vec, + pub validator_node_claim_public_key: UniPublicKey, #[clap(short, long, default_value = "Registering VN")] pub message: String, } diff --git a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs index e161327db4..8f7832feb4 100644 --- a/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs +++ b/applications/minotari_console_wallet/src/grpc/wallet_grpc_server.rs @@ -1006,12 +1006,14 @@ impl wallet_server::Wallet for WalletGrpcServer { let request = request.into_inner(); let mut transaction_service = self.get_transaction_service(); let validator_node_public_key = CommsPublicKey::from_bytes(&request.validator_node_public_key) - .map_err(|_| Status::internal("Destination address is malformed".to_string()))?; + .map_err(|_| Status::invalid_argument("Validator node address is malformed"))?; let validator_node_signature = request .validator_node_signature .ok_or_else(|| Status::invalid_argument("Validator node signature is missing!"))? .try_into() .map_err(|_| Status::invalid_argument("Validator node signature is malformed!"))?; + let validator_node_claim_public_key = PublicKey::from_bytes(&request.validator_node_claim_public_key) + .map_err(|_| Status::invalid_argument("Claim public key is malformed"))?; let constants = self.get_consensus_constants().map_err(|e| { error!(target: LOG_TARGET, "Failed to get consensus constants: {}", e); @@ -1023,6 +1025,7 @@ impl wallet_server::Wallet for WalletGrpcServer { constants.validator_node_registration_min_deposit_amount(), validator_node_public_key, validator_node_signature, + validator_node_claim_public_key, UtxoSelectionCriteria::default(), request.fee_per_gram.into(), request.message, diff --git a/base_layer/core/src/chain_storage/tests/blockchain_database.rs b/base_layer/core/src/chain_storage/tests/blockchain_database.rs index d0357fcacf..ffba06bdbf 100644 --- a/base_layer/core/src/chain_storage/tests/blockchain_database.rs +++ b/base_layer/core/src/chain_storage/tests/blockchain_database.rs @@ -640,9 +640,12 @@ mod validator_node_merkle_root { let (blocks, outputs) = add_many_chained_blocks(1, &db, &key_manager).await; let (sk, public_key) = PublicKey::random_keypair(&mut OsRng); - let signature = ValidatorNodeSignature::sign(&sk, &[]); - let features = - OutputFeatures::for_validator_node_registration(public_key.clone(), signature.signature().clone()); + let signature = ValidatorNodeSignature::sign(&sk, &public_key, &[]); + let features = OutputFeatures::for_validator_node_registration( + public_key.clone(), + signature.signature().clone(), + public_key.clone(), + ); let (tx, _outputs) = schema_to_transaction( &[txn_schema!( from: vec![outputs[0].clone()], diff --git a/base_layer/core/src/proto/sidechain_feature.proto b/base_layer/core/src/proto/sidechain_feature.proto index 707dda9db2..62e26f8d46 100644 --- a/base_layer/core/src/proto/sidechain_feature.proto +++ b/base_layer/core/src/proto/sidechain_feature.proto @@ -18,6 +18,7 @@ message SideChainFeature { message ValidatorNodeRegistration { bytes public_key = 1; Signature signature = 2; + bytes claim_public_key = 3; } message TemplateRegistration { diff --git a/base_layer/core/src/proto/sidechain_feature.rs b/base_layer/core/src/proto/sidechain_feature.rs index 4430148947..337dc8b6b4 100644 --- a/base_layer/core/src/proto/sidechain_feature.rs +++ b/base_layer/core/src/proto/sidechain_feature.rs @@ -89,13 +89,20 @@ impl TryFrom for ValidatorNodeRegistrat type Error = String; fn try_from(value: proto::types::ValidatorNodeRegistration) -> Result { - Ok(Self::new(ValidatorNodeSignature::new( - PublicKey::from_bytes(&value.public_key).map_err(|e| e.to_string())?, - value - .signature - .map(Signature::try_from) - .ok_or("signature not provided")??, - ))) + let public_key = PublicKey::from_bytes(&value.public_key).map_err(|e| format!("public_key: {}", e))?; + let claim_public_key = + PublicKey::from_bytes(&value.claim_public_key).map_err(|e| format!("claim_public_key: {}", e))?; + + Ok(Self::new( + ValidatorNodeSignature::new( + public_key, + value + .signature + .map(Signature::try_from) + .ok_or("signature not provided")??, + ), + claim_public_key, + )) } } @@ -104,6 +111,7 @@ impl From for proto::types::ValidatorNodeRegistration Self { public_key: value.public_key().to_vec(), signature: Some(value.signature().into()), + claim_public_key: value.claim_public_key().to_vec(), } } } diff --git a/base_layer/core/src/transactions/transaction_components/output_features.rs b/base_layer/core/src/transactions/transaction_components/output_features.rs index 5400210d33..eab4589a3c 100644 --- a/base_layer/core/src/transactions/transaction_components/output_features.rs +++ b/base_layer/core/src/transactions/transaction_components/output_features.rs @@ -142,16 +142,14 @@ impl OutputFeatures { } pub fn for_validator_node_registration( - validator_node_public_key: PublicKey, - validator_node_signature: Signature, + public_key: PublicKey, + signature: Signature, + claim_public_key: PublicKey, ) -> OutputFeatures { OutputFeatures { output_type: OutputType::ValidatorNodeRegistration, sidechain_feature: Some(SideChainFeature::ValidatorNodeRegistration( - ValidatorNodeRegistration::new(ValidatorNodeSignature::new( - validator_node_public_key, - validator_node_signature, - )), + ValidatorNodeRegistration::new(ValidatorNodeSignature::new(public_key, signature), claim_public_key), )), ..Default::default() } diff --git a/base_layer/core/src/transactions/transaction_components/side_chain/validator_node_registration.rs b/base_layer/core/src/transactions/transaction_components/side_chain/validator_node_registration.rs index a5e9fec208..606fd7bf55 100644 --- a/base_layer/core/src/transactions/transaction_components/side_chain/validator_node_registration.rs +++ b/base_layer/core/src/transactions/transaction_components/side_chain/validator_node_registration.rs @@ -37,15 +37,19 @@ use crate::{ #[derive(Debug, Clone, Hash, PartialEq, Eq, Deserialize, Serialize, BorshSerialize, BorshDeserialize)] pub struct ValidatorNodeRegistration { signature: ValidatorNodeSignature, + claim_public_key: PublicKey, } impl ValidatorNodeRegistration { - pub fn new(signature: ValidatorNodeSignature) -> Self { - Self { signature } + pub fn new(signature: ValidatorNodeSignature, claim_public_key: PublicKey) -> Self { + Self { + signature, + claim_public_key, + } } pub fn is_valid_signature_for(&self, msg: &[u8]) -> bool { - self.signature.is_valid_signature_for(msg) + self.signature.is_valid_signature_for(&self.claim_public_key, msg) } pub fn derive_shard_key( @@ -71,6 +75,10 @@ impl ValidatorNodeRegistration { self.signature.public_key() } + pub fn claim_public_key(&self) -> &PublicKey { + &self.claim_public_key + } + pub fn signature(&self) -> &Signature { self.signature.signature() } @@ -94,14 +102,19 @@ fn generate_shard_key(public_key: &PublicKey, entropy: &[u8; 32]) -> [u8; 32] { mod test { use rand::rngs::OsRng; use tari_common_types::types::PrivateKey; - use tari_crypto::keys::SecretKey; + use tari_crypto::keys::{PublicKey, SecretKey}; use super::*; use crate::test_helpers::new_public_key; fn create_instance() -> ValidatorNodeRegistration { let sk = PrivateKey::random(&mut OsRng); - ValidatorNodeRegistration::new(ValidatorNodeSignature::sign(&sk, b"valid")) + let claim_public_key = PublicKey::from_secret_key(&sk); + + ValidatorNodeRegistration::new( + ValidatorNodeSignature::sign(&sk, &claim_public_key, b"valid"), + claim_public_key, + ) } mod is_valid_signature_for { @@ -122,10 +135,10 @@ mod test { #[test] fn it_returns_false_for_invalid_signature() { let mut reg = create_instance(); - reg = ValidatorNodeRegistration::new(ValidatorNodeSignature::new( - reg.public_key().clone(), - Signature::default(), - )); + reg = ValidatorNodeRegistration::new( + ValidatorNodeSignature::new(reg.public_key().clone(), Signature::default()), + Default::default(), + ); assert!(!reg.is_valid_signature_for(b"valid")); } } diff --git a/base_layer/core/src/transactions/transaction_components/side_chain/validator_node_signature.rs b/base_layer/core/src/transactions/transaction_components/side_chain/validator_node_signature.rs index cc74ff4f8e..196c1045b4 100644 --- a/base_layer/core/src/transactions/transaction_components/side_chain/validator_node_signature.rs +++ b/base_layer/core/src/transactions/transaction_components/side_chain/validator_node_signature.rs @@ -46,25 +46,36 @@ impl ValidatorNodeSignature { Self { public_key, signature } } - pub fn sign(private_key: &PrivateKey, msg: &[u8]) -> Self { + pub fn sign(private_key: &PrivateKey, claim_public_key: &PublicKey, msg: &[u8]) -> Self { let (secret_nonce, public_nonce) = PublicKey::random_keypair(&mut OsRng); let public_key = PublicKey::from_secret_key(private_key); - let challenge = Self::construct_challenge(&public_key, &public_nonce, msg); + let challenge = Self::construct_challenge(&public_key, &public_nonce, claim_public_key, msg); let signature = Signature::sign_raw(private_key, secret_nonce, &*challenge) .expect("Sign cannot fail with 32-byte challenge and a RistrettoPublicKey"); Self { public_key, signature } } - fn construct_challenge(public_key: &PublicKey, public_nonce: &PublicKey, msg: &[u8]) -> FixedHash { + fn construct_challenge( + public_key: &PublicKey, + public_nonce: &PublicKey, + claim_public_key: &PublicKey, + msg: &[u8], + ) -> FixedHash { let hasher = DomainSeparatedHasher::, ValidatorNodeHashDomain>::new_with_label("registration") .chain(public_key.as_bytes()) .chain(public_nonce.as_bytes()) + .chain(claim_public_key.as_bytes()) .chain(msg); digest::Digest::finalize(hasher).into() } - pub fn is_valid_signature_for(&self, msg: &[u8]) -> bool { - let challenge = Self::construct_challenge(&self.public_key, self.signature.get_public_nonce(), msg); + pub fn is_valid_signature_for(&self, claim_public_key: &PublicKey, msg: &[u8]) -> bool { + let challenge = Self::construct_challenge( + &self.public_key, + self.signature.get_public_nonce(), + claim_public_key, + msg, + ); self.signature.verify_challenge(&self.public_key, &*challenge) } diff --git a/base_layer/wallet/src/transaction_service/handle.rs b/base_layer/wallet/src/transaction_service/handle.rs index e65f595a51..bb0808697b 100644 --- a/base_layer/wallet/src/transaction_service/handle.rs +++ b/base_layer/wallet/src/transaction_service/handle.rs @@ -101,6 +101,7 @@ pub enum TransactionServiceRequest { amount: MicroMinotari, validator_node_public_key: CommsPublicKey, validator_node_signature: Signature, + validator_node_claim_public_key: CommsPublicKey, selection_criteria: UtxoSelectionCriteria, fee_per_gram: MicroMinotari, message: String, @@ -497,6 +498,7 @@ impl TransactionServiceHandle { amount: MicroMinotari, validator_node_public_key: PublicKey, validator_node_signature: Signature, + validator_node_claim_public_key: PublicKey, selection_criteria: UtxoSelectionCriteria, fee_per_gram: MicroMinotari, message: String, @@ -507,6 +509,7 @@ impl TransactionServiceHandle { amount, validator_node_public_key, validator_node_signature, + validator_node_claim_public_key, selection_criteria, fee_per_gram, message, diff --git a/base_layer/wallet/src/transaction_service/service.rs b/base_layer/wallet/src/transaction_service/service.rs index 54d3525825..e06800461e 100644 --- a/base_layer/wallet/src/transaction_service/service.rs +++ b/base_layer/wallet/src/transaction_service/service.rs @@ -672,6 +672,7 @@ where amount, validator_node_public_key, validator_node_signature, + validator_node_claim_public_key, selection_criteria, fee_per_gram, message, @@ -681,6 +682,7 @@ where amount, validator_node_public_key, validator_node_signature, + validator_node_claim_public_key, selection_criteria, fee_per_gram, message, @@ -1733,6 +1735,7 @@ where amount: MicroMinotari, validator_node_public_key: CommsPublicKey, validator_node_signature: Signature, + validator_node_claim_public_key: PublicKey, selection_criteria: UtxoSelectionCriteria, fee_per_gram: MicroMinotari, message: String, @@ -1744,8 +1747,11 @@ where >, reply_channel: oneshot::Sender>, ) -> Result<(), TransactionServiceError> { - let output_features = - OutputFeatures::for_validator_node_registration(validator_node_public_key, validator_node_signature); + let output_features = OutputFeatures::for_validator_node_registration( + validator_node_public_key, + validator_node_signature, + validator_node_claim_public_key, + ); self.send_transaction( self.resources.wallet_identity.address.clone(), amount,