diff --git a/cli/src/lib.rs b/cli/src/lib.rs index b63e0880efd..297885a836f 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -265,7 +265,7 @@ impl Iroha { &config.block_sync, sumeragi.clone(), Arc::clone(&kura), - PeerId::new(&config.torii.p2p_addr, &config.public_key), + PeerId::new(config.torii.p2p_addr.clone(), config.public_key.clone()), network.clone(), ) .start(); @@ -447,6 +447,9 @@ impl Iroha { // FIXME: don't like neither the message nor inability to throw Result to the outside .expect("Cannot proceed without working subscriptions"); + // See https://github.com/tokio-rs/tokio/issues/5616 and + // https://github.com/rust-lang/rust-clippy/issues/10636 + #[allow(clippy::redundant_pub_crate)] loop { tokio::select! { Ok(()) = log_level_update.changed() => { diff --git a/cli/src/samples.rs b/cli/src/samples.rs index 1a59f2b0a25..d88f974485f 100644 --- a/cli/src/samples.rs +++ b/cli/src/samples.rs @@ -30,16 +30,10 @@ pub fn get_trusted_peers(public_key: Option<&PublicKey>) -> HashSet { ), ] .iter() - .map(|(a, k)| PeerId { - address: a.parse().expect("Valid"), - public_key: PublicKey::from_str(k).unwrap(), - }) + .map(|(a, k)| PeerId::new(a.parse().expect("Valid"), PublicKey::from_str(k).unwrap())) .collect(); if let Some(pubkey) = public_key { - trusted_peers.insert(PeerId { - address: DEFAULT_TORII_P2P_ADDR.clone(), - public_key: pubkey.clone(), - }); + trusted_peers.insert(PeerId::new(DEFAULT_TORII_P2P_ADDR.clone(), pubkey.clone())); } trusted_peers } diff --git a/client/benches/tps/utils.rs b/client/benches/tps/utils.rs index c1a3494260f..7a032543af1 100644 --- a/client/benches/tps/utils.rs +++ b/client/benches/tps/utils.rs @@ -222,9 +222,7 @@ impl MeasurerUnit { .with_instructions([instruction]); transaction.set_nonce(nonce); // Use nonce to avoid transaction duplication within the same thread - let transaction = submitter - .sign_transaction(transaction) - .expect("Failed to sign transaction"); + let transaction = submitter.sign_transaction(transaction); if let Err(error) = submitter.submit_transaction(&transaction) { iroha_logger::error!(?error, "Failed to submit transaction"); } diff --git a/client/examples/tutorial.rs b/client/examples/tutorial.rs index cead2516b4a..40c56a1a3aa 100644 --- a/client/examples/tutorial.rs +++ b/client/examples/tutorial.rs @@ -74,9 +74,7 @@ fn domain_registration_test(config: &Configuration) -> Result<(), Error> { // Prepare a transaction let metadata = UnlimitedMetadata::default(); let instructions: Vec = vec![create_looking_glass.into()]; - let tx = iroha_client - .build_transaction(instructions, metadata) - .wrap_err("Error building a domain registration transaction")?; + let tx = iroha_client.build_transaction(instructions, metadata); // #endregion domain_register_example_prepare_tx // #region domain_register_example_submit_tx @@ -148,7 +146,7 @@ fn account_registration_test(config: &Configuration) -> Result<(), Error> { // Account's RegisterBox let metadata = UnlimitedMetadata::new(); let instructions: Vec = vec![create_account.into()]; - let tx = iroha_client.build_transaction(instructions, metadata)?; + let tx = iroha_client.build_transaction(instructions, metadata); // #endregion register_account_prepare_tx // #region register_account_submit_tx diff --git a/client/src/client.rs b/client/src/client.rs index 3a4c7615397..0bf364280c7 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -63,30 +63,17 @@ pub type QueryResult = core::result::Result; /// Trait for signing transactions pub trait Sign { /// Sign transaction with provided key pair. - /// - /// # Errors - /// - /// Fails if signature creation fails - fn sign( - self, - key_pair: crate::crypto::KeyPair, - ) -> Result; + fn sign(self, key_pair: crate::crypto::KeyPair) -> SignedTransaction; } impl Sign for TransactionBuilder { - fn sign( - self, - key_pair: crate::crypto::KeyPair, - ) -> Result { + fn sign(self, key_pair: crate::crypto::KeyPair) -> SignedTransaction { self.sign(key_pair) } } impl Sign for SignedTransaction { - fn sign( - self, - key_pair: crate::crypto::KeyPair, - ) -> Result { + fn sign(self, key_pair: crate::crypto::KeyPair) -> SignedTransaction { self.sign(key_pair) } } @@ -465,7 +452,7 @@ impl Client { &self, instructions: impl Into, metadata: UnlimitedMetadata, - ) -> Result { + ) -> SignedTransaction { let tx_builder = TransactionBuilder::new(self.account_id.clone()); let mut tx_builder = match instructions.into() { @@ -484,27 +471,22 @@ impl Client { tx_builder .with_metadata(metadata) .sign(self.key_pair.clone()) - .wrap_err("Failed to sign transaction") } /// Signs transaction /// /// # Errors /// Fails if signature generation fails - pub fn sign_transaction(&self, transaction: Tx) -> Result { - transaction - .sign(self.key_pair.clone()) - .wrap_err("Failed to sign transaction") + pub fn sign_transaction(&self, transaction: Tx) -> SignedTransaction { + transaction.sign(self.key_pair.clone()) } /// Signs query /// /// # Errors /// Fails if signature generation fails - pub fn sign_query(&self, query: QueryBuilder) -> Result { - query - .sign(self.key_pair.clone()) - .wrap_err("Failed to sign query") + pub fn sign_query(&self, query: QueryBuilder) -> SignedQuery { + query.sign(self.key_pair.clone()) } /// Instructions API entry point. Submits one Iroha Special Instruction to `Iroha` peers. @@ -554,7 +536,7 @@ impl Client { instructions: impl IntoIterator, metadata: UnlimitedMetadata, ) -> Result> { - self.submit_transaction(&self.build_transaction(instructions, metadata)?) + self.submit_transaction(&self.build_transaction(instructions, metadata)) } /// Submit a prebuilt transaction. @@ -743,15 +725,12 @@ impl Client { instructions: impl IntoIterator, metadata: UnlimitedMetadata, ) -> Result> { - let transaction = self.build_transaction(instructions, metadata)?; + let transaction = self.build_transaction(instructions, metadata); self.submit_transaction_blocking(&transaction) } /// Lower-level Query API entry point. Prepares an http-request and returns it with an http-response handler. /// - /// # Errors - /// Fails if query signing fails. - /// /// # Examples /// /// ```ignore @@ -814,12 +793,12 @@ impl Client { pagination: Pagination, sorting: Sorting, fetch_size: FetchSize, - ) -> Result<(DefaultRequestBuilder, QueryResponseHandler)> + ) -> (DefaultRequestBuilder, QueryResponseHandler) where >::Error: Into, { let query_builder = QueryBuilder::new(request, self.account_id.clone()).with_filter(filter); - let request = self.sign_query(query_builder)?.encode_versioned(); + let request = self.sign_query(query_builder).encode_versioned(); let query_request = QueryRequest { torii_url: self.torii_url.clone(), @@ -831,10 +810,10 @@ impl Client { ), }; - Ok(( + ( query_request.clone().assemble(), QueryResponseHandler::new(query_request), - )) + ) } /// Create a request with pagination, sorting and add the filter. @@ -855,7 +834,7 @@ impl Client { { iroha_logger::trace!(?request, %pagination, ?sorting, ?filter); let (req, mut resp_handler) = - self.prepare_query_request::(request, filter, pagination, sorting, fetch_size)?; + self.prepare_query_request::(request, filter, pagination, sorting, fetch_size); let response = req.build()?.send()?; let value = resp_handler.handle(&response)?; @@ -1679,11 +1658,8 @@ mod tests { .expect("Client config should build as all required fields were provided"); let client = Client::new(&cfg).expect("Invalid client configuration"); - let build_transaction = || { - client - .build_transaction(Vec::::new(), UnlimitedMetadata::new()) - .unwrap() - }; + let build_transaction = + || client.build_transaction(Vec::::new(), UnlimitedMetadata::new()); let tx1 = build_transaction(); let mut tx2 = build_transaction(); assert_ne!(tx1.payload().hash(), tx2.payload().hash()); diff --git a/client/tests/integration/asset.rs b/client/tests/integration/asset.rs index 3b151b99ec8..3f0c28ee139 100644 --- a/client/tests/integration/asset.rs +++ b/client/tests/integration/asset.rs @@ -106,7 +106,7 @@ fn client_add_asset_quantity_to_existing_asset_should_increase_asset_amount() -> AssetId::new(asset_definition_id.clone(), account_id.clone()), ); let instructions: [InstructionBox; 2] = [create_asset.into(), mint.into()]; - let tx = test_client.build_transaction(instructions, metadata)?; + let tx = test_client.build_transaction(instructions, metadata); test_client.submit_transaction(&tx)?; test_client.poll_request(client::asset::by_account_id(account_id), |result| { let assets = result.collect::>>().expect("Valid"); @@ -137,7 +137,7 @@ fn client_add_big_asset_quantity_to_existing_asset_should_increase_asset_amount( AssetId::new(asset_definition_id.clone(), account_id.clone()), ); let instructions: [InstructionBox; 2] = [create_asset.into(), mint.into()]; - let tx = test_client.build_transaction(instructions, metadata)?; + let tx = test_client.build_transaction(instructions, metadata); test_client.submit_transaction(&tx)?; test_client.poll_request(client::asset::by_account_id(account_id), |result| { let assets = result.collect::>>().expect("Valid"); @@ -169,7 +169,7 @@ fn client_add_asset_with_decimal_should_increase_asset_amount() -> Result<()> { AssetId::new(asset_definition_id.clone(), account_id.clone()), ); let instructions: [InstructionBox; 2] = [create_asset.into(), mint.into()]; - let tx = test_client.build_transaction(instructions, metadata)?; + let tx = test_client.build_transaction(instructions, metadata); test_client.submit_transaction(&tx)?; test_client.poll_request(client::asset::by_account_id(account_id.clone()), |result| { let assets = result.collect::>>().expect("Valid"); @@ -279,8 +279,7 @@ fn find_rate_and_make_exchange_isi_should_succeed() { let grant_asset_transfer_tx = TransactionBuilder::new(asset_id.account_id().clone()) .with_instructions([allow_alice_to_transfer_asset]) - .sign(owner_keypair) - .expect("Failed to sign seller transaction"); + .sign(owner_keypair); test_client .submit_transaction_blocking(&grant_asset_transfer_tx) diff --git a/client/tests/integration/burn_public_keys.rs b/client/tests/integration/burn_public_keys.rs index f207894995d..16ffaccf3dc 100644 --- a/client/tests/integration/burn_public_keys.rs +++ b/client/tests/integration/burn_public_keys.rs @@ -17,12 +17,9 @@ fn submit( TransactionBuilder::new(account_id) .with_instructions(instructions) .sign(keypair) - .unwrap() } else { - let tx = client - .build_transaction(instructions, UnlimitedMetadata::default()) - .unwrap(); - client.sign_transaction(tx).unwrap() + let tx = client.build_transaction(instructions, UnlimitedMetadata::default()); + client.sign_transaction(tx) }; (tx.hash(), client.submit_transaction_blocking(&tx)) diff --git a/client/tests/integration/domain_owner.rs b/client/tests/integration/domain_owner.rs index eeeb881b324..7a8b4b7d4b5 100644 --- a/client/tests/integration/domain_owner.rs +++ b/client/tests/integration/domain_owner.rs @@ -124,7 +124,7 @@ fn domain_owner_asset_definition_permissions() -> Result<()> { let coin = AssetDefinition::quantity(coin_id.clone()); let transaction = TransactionBuilder::new(bob_id.clone()) .with_instructions([Register::asset_definition(coin)]) - .sign(bob_keypair)?; + .sign(bob_keypair); test_client.submit_transaction_blocking(&transaction)?; // check that "alice@wonderland" as owner of domain can transfer asset definitions in her domain @@ -186,7 +186,7 @@ fn domain_owner_asset_permissions() -> Result<()> { Register::asset_definition(coin), Register::asset_definition(store), ]) - .sign(bob_keypair)?; + .sign(bob_keypair); test_client.submit_transaction_blocking(&transaction)?; // check that "alice@wonderland" as owner of domain can register and unregister assets in her domain diff --git a/client/tests/integration/events/data.rs b/client/tests/integration/events/data.rs index 9f914bb5bb3..be62fe1b193 100644 --- a/client/tests/integration/events/data.rs +++ b/client/tests/integration/events/data.rs @@ -93,9 +93,7 @@ fn transaction_execution_should_produce_events( // submit transaction to produce events init_receiver.recv()?; - let transaction = client - .build_transaction(executable, UnlimitedMetadata::new()) - .unwrap(); + let transaction = client.build_transaction(executable, UnlimitedMetadata::new()); client.submit_transaction_blocking(&transaction)?; // assertion diff --git a/client/tests/integration/events/pipeline.rs b/client/tests/integration/events/pipeline.rs index 77d99dd1b57..5bfae9eb47d 100644 --- a/client/tests/integration/events/pipeline.rs +++ b/client/tests/integration/events/pipeline.rs @@ -53,7 +53,7 @@ fn test_with_instruction_and_status_and_port( // Given let submitter = client; - let transaction = submitter.build_transaction(instruction, UnlimitedMetadata::new())?; + let transaction = submitter.build_transaction(instruction, UnlimitedMetadata::new()); let hash = transaction.payload().hash(); let mut handles = Vec::new(); for listener in clients { diff --git a/client/tests/integration/multisignature_transaction.rs b/client/tests/integration/multisignature_transaction.rs index 4cf5739788b..7fb8f646772 100644 --- a/client/tests/integration/multisignature_transaction.rs +++ b/client/tests/integration/multisignature_transaction.rs @@ -55,8 +55,8 @@ fn multisignature_transactions_should_wait_for_all_signatures() -> Result<()> { client_configuration.private_key = private_key1; let client = Client::new(&client_configuration)?; let instructions = [mint_asset.clone()]; - let transaction = client.build_transaction(instructions, UnlimitedMetadata::new())?; - client.submit_transaction(&client.sign_transaction(transaction)?)?; + let transaction = client.build_transaction(instructions, UnlimitedMetadata::new()); + client.submit_transaction(&client.sign_transaction(transaction))?; thread::sleep(pipeline_time); //Then @@ -81,11 +81,11 @@ fn multisignature_transactions_should_wait_for_all_signatures() -> Result<()> { client_configuration.private_key = private_key2; let client_2 = Client::new(&client_configuration)?; let instructions = [mint_asset]; - let transaction = client_2.build_transaction(instructions, UnlimitedMetadata::new())?; + let transaction = client_2.build_transaction(instructions, UnlimitedMetadata::new()); let transaction = client_2 .get_original_transaction(&transaction, 3, Duration::from_millis(100))? .expect("Found no pending transaction for this account."); - client_2.submit_transaction(&client_2.sign_transaction(transaction)?)?; + client_2.submit_transaction(&client_2.sign_transaction(transaction))?; thread::sleep(pipeline_time); let assets = client_1 .request(request)? diff --git a/client/tests/integration/non_mintable.rs b/client/tests/integration/non_mintable.rs index c80be2ca4d9..b07df3158ff 100644 --- a/client/tests/integration/non_mintable.rs +++ b/client/tests/integration/non_mintable.rs @@ -28,7 +28,7 @@ fn non_mintable_asset_can_be_minted_once_but_not_twice() -> Result<()> { ); let instructions: [InstructionBox; 2] = [create_asset.into(), mint.clone().into()]; - let tx = test_client.build_transaction(instructions, metadata)?; + let tx = test_client.build_transaction(instructions, metadata); // We can register and mint the non-mintable token test_client.submit_transaction(&tx)?; diff --git a/client/tests/integration/offline_peers.rs b/client/tests/integration/offline_peers.rs index fc14502caa3..bc14bce0376 100644 --- a/client/tests/integration/offline_peers.rs +++ b/client/tests/integration/offline_peers.rs @@ -8,6 +8,7 @@ use iroha_client::{ }; use iroha_config::iroha::Configuration; use iroha_crypto::KeyPair; +use iroha_primitives::addr::socket_addr; use test_network::*; use tokio::runtime::Runtime; @@ -51,10 +52,10 @@ fn register_offline_peer() -> Result<()> { check_status(&peer_clients, 1); - let address = "128.0.0.2:8085".parse()?; + let address = socket_addr!(128.0.0.2:8085); let key_pair = KeyPair::generate().unwrap(); let public_key = key_pair.public_key().clone(); - let peer_id = PeerId::new(&address, &public_key); + let peer_id = PeerId::new(address, public_key); let register_peer = Register::peer(DataModelPeer::new(peer_id)); // Wait for some time to allow peers to connect diff --git a/client/tests/integration/permissions.rs b/client/tests/integration/permissions.rs index f9ff3c05cdf..fbc964ecb8c 100644 --- a/client/tests/integration/permissions.rs +++ b/client/tests/integration/permissions.rs @@ -96,8 +96,7 @@ fn permissions_disallow_asset_transfer() { ); let transfer_tx = TransactionBuilder::new(mouse_id) .with_instructions([transfer_asset]) - .sign(mouse_keypair) - .expect("Failed to sign mouse transaction"); + .sign(mouse_keypair); let err = iroha_client .submit_transaction_blocking(&transfer_tx) .expect_err("Transaction was not rejected."); @@ -146,8 +145,7 @@ fn permissions_disallow_asset_burn() { ); let burn_tx = TransactionBuilder::new(mouse_id) .with_instructions([burn_asset]) - .sign(mouse_keypair) - .expect("Failed to sign mouse transaction"); + .sign(mouse_keypair); let err = iroha_client .submit_transaction_blocking(&burn_tx) @@ -228,8 +226,7 @@ fn permissions_differ_not_only_by_names() { let grant_hats_access_tx = TransactionBuilder::new(mouse_id.clone()) .with_instructions([allow_alice_to_set_key_value_in_hats]) - .sign(mouse_keypair.clone()) - .expect("Failed to sign mouse transaction"); + .sign(mouse_keypair.clone()); client .submit_transaction_blocking(&grant_hats_access_tx) .expect("Failed grant permission to modify Mouse's hats"); @@ -265,8 +262,7 @@ fn permissions_differ_not_only_by_names() { let grant_shoes_access_tx = TransactionBuilder::new(mouse_id) .with_instructions([allow_alice_to_set_key_value_in_shoes]) - .sign(mouse_keypair) - .expect("Failed to sign mouse transaction"); + .sign(mouse_keypair); client .submit_transaction_blocking(&grant_shoes_access_tx) @@ -315,8 +311,7 @@ fn stored_vs_granted_token_payload() -> Result<()> { let transaction = TransactionBuilder::new(mouse_id) .with_instructions([allow_alice_to_set_key_value_in_mouse_asset]) - .sign(mouse_keypair) - .expect("Failed to sign mouse transaction"); + .sign(mouse_keypair); iroha_client .submit_transaction_blocking(&transaction) .expect("Failed to grant permission to alice."); diff --git a/client/tests/integration/queries/mod.rs b/client/tests/integration/queries/mod.rs index df0104e07bc..d654c8fc83b 100644 --- a/client/tests/integration/queries/mod.rs +++ b/client/tests/integration/queries/mod.rs @@ -49,7 +49,7 @@ fn live_query_is_dropped_after_smart_contract_end() -> Result<()> { let transaction = client.build_transaction( WasmSmartContract::from_compiled(wasm), UnlimitedMetadata::default(), - )?; + ); client.submit_transaction_blocking(&transaction)?; let metadata_value = client.request(FindAccountKeyValueByIdAndKey::new( diff --git a/client/tests/integration/roles.rs b/client/tests/integration/roles.rs index 95245852db4..3c8a1c55b20 100644 --- a/client/tests/integration/roles.rs +++ b/client/tests/integration/roles.rs @@ -78,7 +78,7 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> { let grant_role = Grant::role(role_id.clone(), alice_id.clone()); let grant_role_tx = TransactionBuilder::new(mouse_id.clone()) .with_instructions([grant_role]) - .sign(mouse_key_pair)?; + .sign(mouse_key_pair); test_client.submit_transaction_blocking(&grant_role_tx)?; // Alice modifies Mouse's metadata diff --git a/client/tests/integration/tx_history.rs b/client/tests/integration/tx_history.rs index 4d26d32fe19..85d81fbd3f8 100644 --- a/client/tests/integration/tx_history.rs +++ b/client/tests/integration/tx_history.rs @@ -48,7 +48,7 @@ fn client_has_rejected_and_acepted_txs_should_return_tx_history() -> Result<()> &mint_not_existed_asset }; let instructions: Vec = vec![mint_asset.clone().into()]; - let transaction = client.build_transaction(instructions, UnlimitedMetadata::new())?; + let transaction = client.build_transaction(instructions, UnlimitedMetadata::new()); client.submit_transaction(&transaction)?; } thread::sleep(pipeline_time * 5); diff --git a/client/tests/integration/upgrade.rs b/client/tests/integration/upgrade.rs index 3ec49a84600..3784fb3c28e 100644 --- a/client/tests/integration/upgrade.rs +++ b/client/tests/integration/upgrade.rs @@ -32,7 +32,7 @@ fn executor_upgrade_should_work() -> Result<()> { let transfer_alice_rose = Transfer::asset_quantity(alice_rose, 1_u32, admin_rose); let transfer_rose_tx = TransactionBuilder::new(admin_id.clone()) .with_instructions([transfer_alice_rose.clone()]) - .sign(admin_keypair.clone())?; + .sign(admin_keypair.clone()); let _ = client .submit_transaction_blocking(&transfer_rose_tx) .expect_err("Should fail"); @@ -46,7 +46,7 @@ fn executor_upgrade_should_work() -> Result<()> { // Creating new transaction instead of cloning, because we need to update it's creation time let transfer_rose_tx = TransactionBuilder::new(admin_id) .with_instructions([transfer_alice_rose]) - .sign(admin_keypair)?; + .sign(admin_keypair); client .submit_transaction_blocking(&transfer_rose_tx) .expect("Should succeed"); diff --git a/client_cli/src/main.rs b/client_cli/src/main.rs index 7f22f2dbb89..f3e7d768926 100644 --- a/client_cli/src/main.rs +++ b/client_cli/src/main.rs @@ -235,13 +235,7 @@ fn submit( ) -> Result<()> { let iroha_client = Client::new(context.configuration())?; let instructions = instructions.into(); - #[cfg(debug_assertions)] - let err_msg = format!("Failed to build transaction from instruction {instructions:?}"); - #[cfg(not(debug_assertions))] - let err_msg = "Failed to build transaction."; - let tx = iroha_client - .build_transaction(instructions, metadata) - .wrap_err(err_msg)?; + let tx = iroha_client.build_transaction(instructions, metadata); let tx = if context.skip_mst_check() { tx } else { @@ -257,7 +251,7 @@ fn submit( Do you want to sign this transaction (yes) \ instead of submitting a new transaction (no)?") .interact() - .wrap_err("Failed to show interactive prompt.")? => iroha_client.sign_transaction(original_transaction).wrap_err("Failed to sign transaction.")?, + .wrap_err("Failed to show interactive prompt.")? => iroha_client.sign_transaction(original_transaction), _ => tx, } }; @@ -897,17 +891,17 @@ mod peer { #[derive(clap::Subcommand, Debug)] pub enum Args { /// Register subcommand of peer - Register(Register), + Register(Box), /// Unregister subcommand of peer - Unregister(Unregister), + Unregister(Box), } impl RunArgs for Args { fn run(self, context: &mut dyn RunContext) -> Result<()> { - match_all!( - (self, context), - { Args::Register, Args::Unregister } - ) + match self { + Args::Register(register) => RunArgs::run(*register, context), + Args::Unregister(unregister) => RunArgs::run(*unregister, context), + } } } @@ -931,9 +925,8 @@ mod peer { key, metadata, } = self; - let register_peer = iroha_client::data_model::isi::Register::peer(Peer::new( - PeerId::new(&address, &key), - )); + let register_peer = + iroha_client::data_model::isi::Register::peer(Peer::new(PeerId::new(address, key))); submit([register_peer], metadata.load()?, context).wrap_err("Failed to register peer") } } @@ -959,7 +952,7 @@ mod peer { metadata, } = self; let unregister_peer = - iroha_client::data_model::isi::Unregister::peer(PeerId::new(&address, &key)); + iroha_client::data_model::isi::Unregister::peer(PeerId::new(address, key)); submit([unregister_peer], metadata.load()?, context) .wrap_err("Failed to unregister peer") } diff --git a/config/src/iroha.rs b/config/src/iroha.rs index 1946b2571b1..4864e3bfa9a 100644 --- a/config/src/iroha.rs +++ b/config/src/iroha.rs @@ -115,7 +115,7 @@ impl ConfigurationProxy { if let Some(torii_proxy) = &mut self.torii { if sumeragi_proxy.peer_id.is_none() { sumeragi_proxy.peer_id = Some(iroha_data_model::prelude::PeerId::new( - &torii_proxy + torii_proxy .p2p_addr .clone() .ok_or(ConfigError::MissingField { @@ -123,7 +123,7 @@ impl ConfigurationProxy { message: "`p2p_addr` should not be set to `null` or `None` explicitly.", })?, - &self.public_key.clone().expect( + self.public_key.clone().expect( "Iroha `public_key` should have been initialized above at the latest", ), )); diff --git a/config/src/path.rs b/config/src/path.rs index 488af5ded46..23f1bd80b57 100644 --- a/config/src/path.rs +++ b/config/src/path.rs @@ -69,9 +69,10 @@ impl Path { /// If the path has an extension. pub fn default(path: impl AsRef) -> Self { let path = path.as_ref().to_path_buf(); - if path.extension().is_some() { - panic!("Default config path is not supposed to have an extension. It is a bug.") - } + assert!( + path.extension().is_none(), + "Default config path is not supposed to have an extension. It is a bug." + ); Self(Default(path)) } diff --git a/core/benches/blocks/common.rs b/core/benches/blocks/common.rs index 7aef12edd2d..346291f585c 100644 --- a/core/benches/blocks/common.rs +++ b/core/benches/blocks/common.rs @@ -28,8 +28,7 @@ pub fn create_block( ) -> CommittedBlock { let transaction = TransactionBuilder::new(account_id) .with_instructions(instructions) - .sign(key_pair.clone()) - .unwrap(); + .sign(key_pair.clone()); let limits = wsv.transaction_executor().transaction_limits; let topology = Topology::new(UniqueVec::new()); @@ -40,7 +39,6 @@ pub fn create_block( ) .chain(0, wsv) .sign(key_pair) - .unwrap() .commit(&topology) .unwrap(); diff --git a/core/benches/kura.rs b/core/benches/kura.rs index a47f731e31d..e619093b6ad 100644 --- a/core/benches/kura.rs +++ b/core/benches/kura.rs @@ -27,8 +27,7 @@ async fn measure_block_size_for_n_executors(n_executors: u32) { let keypair = KeyPair::generate().expect("Failed to generate KeyPair."); let tx = TransactionBuilder::new(AccountId::from_str("alice@wonderland").expect("checked")) .with_instructions([transfer]) - .sign(keypair.clone()) - .expect("Failed to sign."); + .sign(keypair.clone()); let transaction_limits = TransactionLimits { max_instruction_number: 4096, max_wasm_size_bytes: 0, @@ -49,13 +48,10 @@ async fn measure_block_size_for_n_executors(n_executors: u32) { let topology = Topology::new(UniqueVec::new()); let mut block = BlockBuilder::new(vec![tx], topology, Vec::new()) .chain(0, &mut wsv) - .sign(KeyPair::generate().unwrap()) - .unwrap(); + .sign(KeyPair::generate().unwrap()); for _ in 1..n_executors { - block = block - .sign(KeyPair::generate().expect("Failed to generate KeyPair.")) - .unwrap(); + block = block.sign(KeyPair::generate().unwrap()); } let mut block_store = BlockStore::new(dir.path(), LockStatus::Unlocked); block_store.create_files_if_they_do_not_exist().unwrap(); diff --git a/core/benches/validation.rs b/core/benches/validation.rs index 3a5bcaefe23..c962f7a5efe 100644 --- a/core/benches/validation.rs +++ b/core/benches/validation.rs @@ -2,7 +2,7 @@ use std::str::FromStr as _; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, BatchSize, Criterion}; use iroha_core::{ block::*, prelude::*, @@ -53,7 +53,6 @@ fn build_test_transaction(keys: KeyPair) -> SignedTransaction { )) .with_instructions(instructions) .sign(keys) - .expect("Failed to sign.") } fn build_test_and_transient_wsv(keys: KeyPair) -> WorldStateView { @@ -112,15 +111,18 @@ fn sign_transaction(criterion: &mut Criterion) { let keys = KeyPair::generate().expect("Failed to generate keys"); let transaction = build_test_transaction(keys); let key_pair = KeyPair::generate().expect("Failed to generate KeyPair."); - let mut success_count = 0; - let mut failures_count = 0; + let mut count = 0; let _ = criterion.bench_function("sign", |b| { - b.iter(|| match transaction.clone().sign(key_pair.clone()) { - Ok(_) => success_count += 1, - Err(_) => failures_count += 1, - }); + b.iter_batched( + || transaction.clone(), + |transaction| { + let _: SignedTransaction = transaction.sign(key_pair.clone()); + count += 1; + }, + BatchSize::SmallInput, + ); }); - println!("Success count: {success_count}, Failures count: {failures_count}"); + println!("Count: {count}"); } fn validate_transaction(criterion: &mut Criterion) { @@ -155,18 +157,21 @@ fn sign_blocks(criterion: &mut Criterion) { let mut wsv = WorldStateView::new(World::new(), kura, query_handle); let topology = Topology::new(UniqueVec::new()); - let mut success_count = 0; - let mut failures_count = 0; + let mut count = 0; let block = BlockBuilder::new(vec![transaction], topology, Vec::new()).chain(0, &mut wsv); let _ = criterion.bench_function("sign_block", |b| { - b.iter(|| match block.clone().sign(key_pair.clone()) { - Ok(_) => success_count += 1, - Err(_) => failures_count += 1, - }); + b.iter_batched( + || block.clone(), + |block| { + let _: ValidBlock = block.sign(key_pair.clone()); + count += 1; + }, + BatchSize::SmallInput, + ); }); - println!("Success count: {success_count}, Failures count: {failures_count}"); + println!("Count: {count}"); } criterion_group!( diff --git a/core/src/block.rs b/core/src/block.rs index 164dc6b5456..8b101e6f0bb 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -216,20 +216,16 @@ mod chained { impl BlockBuilder { /// Sign this block and get [`SignedBlock`]. - /// - /// # Errors - /// - /// Fails if signature generation fails - pub fn sign(self, key_pair: KeyPair) -> Result { - let signature = SignatureOf::new(key_pair, &self.0 .0)?; + pub fn sign(self, key_pair: KeyPair) -> ValidBlock { + let signature = SignatureOf::new(key_pair, &self.0 .0); - Ok(ValidBlock( + ValidBlock( SignedBlockV1 { payload: self.0 .0, signatures: SignaturesOf::from(signature), } .into(), - )) + ) } } } @@ -426,12 +422,9 @@ mod valid { } /// Add additional signatures for [`Self`]. - /// - /// # Errors - /// - /// If signature generation fails - pub fn sign(self, key_pair: KeyPair) -> Result { - self.0.sign(key_pair).map(ValidBlock) + #[must_use] + pub fn sign(self, key_pair: KeyPair) -> Self { + ValidBlock(self.0.sign(key_pair)) } /// Add additional signature for [`Self`] @@ -462,7 +455,6 @@ mod valid { event_recommendations: Vec::new(), })) .sign(KeyPair::generate().unwrap()) - .unwrap() } /// Check if block's signatures meet requirements for given topology. @@ -536,9 +528,7 @@ mod valid { let payload = block.payload().clone(); key_pairs .iter() - .map(|key_pair| { - SignatureOf::new(key_pair.clone(), &payload).expect("Failed to sign") - }) + .map(|key_pair| SignatureOf::new(key_pair.clone(), &payload)) .try_for_each(|signature| block.add_signature(signature)) .expect("Failed to add signatures"); @@ -561,9 +551,7 @@ mod valid { key_pairs .iter() .enumerate() - .map(|(_, key_pair)| { - SignatureOf::new(key_pair.clone(), &payload).expect("Failed to sign") - }) + .map(|(_, key_pair)| SignatureOf::new(key_pair.clone(), &payload)) .try_for_each(|signature| block.add_signature(signature)) .expect("Failed to add signatures"); @@ -584,8 +572,7 @@ mod valid { let mut block = ValidBlock::new_dummy(); let payload = block.payload().clone(); - let proxy_tail_signature = - SignatureOf::new(key_pairs[4].clone(), &payload).expect("Failed to sign"); + let proxy_tail_signature = SignatureOf::new(key_pairs[4].clone(), &payload); block .add_signature(proxy_tail_signature) .expect("Failed to add signature"); @@ -617,7 +604,7 @@ mod valid { .iter() .enumerate() .filter(|(i, _)| *i != 4) // Skip proxy tail - .map(|(_, key_pair)| SignatureOf::new(key_pair.clone(), &payload).expect("Failed to sign")) + .map(|(_, key_pair)| SignatureOf::new(key_pair.clone(), &payload)) .try_for_each(|signature| block.add_signature(signature)) .expect("Failed to add signatures"); @@ -742,8 +729,7 @@ mod tests { let transaction_limits = &wsv.transaction_executor().transaction_limits; let tx = TransactionBuilder::new(alice_id) .with_instructions([create_asset_definition]) - .sign(alice_keys.clone()) - .expect("Valid"); + .sign(alice_keys.clone()); let tx = AcceptedTransaction::accept(tx, transaction_limits).expect("Valid"); // Creating a block of two identical transactions and validating it @@ -751,8 +737,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) .chain(0, &mut wsv) - .sign(alice_keys) - .expect("Valid"); + .sign(alice_keys); // The first transaction should be confirmed assert!(valid_block.payload().transactions[0].error.is_none()); @@ -785,8 +770,7 @@ mod tests { let transaction_limits = &wsv.transaction_executor().transaction_limits; let tx = TransactionBuilder::new(alice_id.clone()) .with_instructions([create_asset_definition]) - .sign(alice_keys.clone()) - .expect("Valid"); + .sign(alice_keys.clone()); let tx = AcceptedTransaction::accept(tx, transaction_limits).expect("Valid"); let quantity: u32 = 200; @@ -804,14 +788,12 @@ mod tests { let tx0 = TransactionBuilder::new(alice_id.clone()) .with_instructions([fail_mint]) - .sign(alice_keys.clone()) - .expect("Valid"); + .sign(alice_keys.clone()); let tx0 = AcceptedTransaction::accept(tx0, transaction_limits).expect("Valid"); let tx2 = TransactionBuilder::new(alice_id) .with_instructions([succeed_mint]) - .sign(alice_keys.clone()) - .expect("Valid"); + .sign(alice_keys.clone()); let tx2 = AcceptedTransaction::accept(tx2, transaction_limits).expect("Valid"); // Creating a block of two identical transactions and validating it @@ -819,8 +801,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) .chain(0, &mut wsv) - .sign(alice_keys) - .expect("Valid"); + .sign(alice_keys); // The first transaction should fail assert!(valid_block.payload().transactions[0].error.is_some()); @@ -860,13 +841,11 @@ mod tests { let instructions_accept: [InstructionBox; 2] = [create_domain.into(), create_asset.into()]; let tx_fail = TransactionBuilder::new(alice_id.clone()) .with_instructions(instructions_fail) - .sign(alice_keys.clone()) - .expect("Valid"); + .sign(alice_keys.clone()); let tx_fail = AcceptedTransaction::accept(tx_fail, transaction_limits).expect("Valid"); let tx_accept = TransactionBuilder::new(alice_id) .with_instructions(instructions_accept) - .sign(alice_keys.clone()) - .expect("Valid"); + .sign(alice_keys.clone()); let tx_accept = AcceptedTransaction::accept(tx_accept, transaction_limits).expect("Valid"); // Creating a block of where first transaction must fail and second one fully executed @@ -874,8 +853,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) .chain(0, &mut wsv) - .sign(alice_keys) - .expect("Valid"); + .sign(alice_keys); // The first transaction should be rejected assert!( diff --git a/core/src/queue.rs b/core/src/queue.rs index 2872ebc9365..1f68679f84c 100644 --- a/core/src/queue.rs +++ b/core/src/queue.rs @@ -400,8 +400,7 @@ mod tests { let instructions = [Fail { message }]; let tx = TransactionBuilder::new(AccountId::from_str(account_id).expect("Valid")) .with_instructions(instructions) - .sign(key) - .expect("Failed to sign."); + .sign(key); let limits = TransactionLimits { max_instruction_number: 4096, max_wasm_size_bytes: 0, @@ -520,12 +519,9 @@ mod tests { max_wasm_size_bytes: 0, }; let fully_signed_tx: AcceptedTransaction = { - let mut signed_tx = tx - .clone() - .sign(key_pairs[0].clone()) - .expect("Failed to sign."); + let mut signed_tx = tx.clone().sign(key_pairs[0].clone()); for key_pair in &key_pairs[1..] { - signed_tx = signed_tx.sign(key_pair.clone()).expect("Failed to sign"); + signed_tx = signed_tx.sign(key_pair.clone()); } AcceptedTransaction::accept(signed_tx, &tx_limits) .expect("Failed to accept Transaction.") @@ -537,11 +533,8 @@ mod tests { )); let get_tx = |key_pair| { - AcceptedTransaction::accept( - tx.clone().sign(key_pair).expect("Failed to sign."), - &tx_limits, - ) - .expect("Failed to accept Transaction.") + AcceptedTransaction::accept(tx.clone().sign(key_pair), &tx_limits) + .expect("Failed to accept Transaction.") }; for key_pair in key_pairs { let partially_signed_tx: AcceptedTransaction = get_tx(key_pair); @@ -767,7 +760,7 @@ mod tests { TransactionBuilder::new(AccountId::from_str("alice@wonderland").expect("Valid")) .with_instructions(instructions); tx.set_ttl(Duration::from_millis(10)); - let tx = tx.sign(alice_key).expect("Failed to sign."); + let tx = tx.sign(alice_key); let limits = TransactionLimits { max_instruction_number: 4096, max_wasm_size_bytes: 0, diff --git a/core/src/smartcontracts/isi/account.rs b/core/src/smartcontracts/isi/account.rs index eff4dbaebbc..2cba9045607 100644 --- a/core/src/smartcontracts/isi/account.rs +++ b/core/src/smartcontracts/isi/account.rs @@ -171,7 +171,7 @@ pub mod isi { ))); } if !account.remove_signatory(&public_key) { - return Err(FindError::PublicKey(public_key).into()); + return Err(FindError::PublicKey(Box::new(public_key)).into()); } Ok(()) })?; diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index d14ed740d0b..98779e207b2 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -268,14 +268,14 @@ mod tests { let instructions: [InstructionBox; 0] = []; let tx = TransactionBuilder::new(ALICE_ID.clone()) .with_instructions(instructions) - .sign(ALICE_KEYS.clone())?; + .sign(ALICE_KEYS.clone()); AcceptedTransaction::accept(tx, &limits)? }; let invalid_tx = { let isi = Fail::new("fail".to_owned()); let tx = TransactionBuilder::new(ALICE_ID.clone()) .with_instructions([isi.clone(), isi]) - .sign(ALICE_KEYS.clone())?; + .sign(ALICE_KEYS.clone()); AcceptedTransaction::accept(tx, &huge_limits)? }; @@ -285,7 +285,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let first_block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) .chain(0, &mut wsv) - .sign(ALICE_KEYS.clone())? + .sign(ALICE_KEYS.clone()) .commit(&topology) .expect("Block is valid"); @@ -295,7 +295,7 @@ mod tests { for _ in 1u64..blocks { let block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) .chain(0, &mut wsv) - .sign(ALICE_KEYS.clone())? + .sign(ALICE_KEYS.clone()) .commit(&topology) .expect("Block is valid"); @@ -416,7 +416,7 @@ mod tests { let instructions: [InstructionBox; 0] = []; let tx = TransactionBuilder::new(ALICE_ID.clone()) .with_instructions(instructions) - .sign(ALICE_KEYS.clone())?; + .sign(ALICE_KEYS.clone()); let tx_limits = &wsv.transaction_executor().transaction_limits; let va_tx = AcceptedTransaction::accept(tx, tx_limits)?; @@ -424,7 +424,7 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let vcb = BlockBuilder::new(vec![va_tx.clone()], topology.clone(), Vec::new()) .chain(0, &mut wsv) - .sign(ALICE_KEYS.clone())? + .sign(ALICE_KEYS.clone()) .commit(&topology) .expect("Block is valid"); @@ -433,7 +433,7 @@ mod tests { let unapplied_tx = TransactionBuilder::new(ALICE_ID.clone()) .with_instructions([Unregister::account("account@domain".parse().unwrap())]) - .sign(ALICE_KEYS.clone())?; + .sign(ALICE_KEYS.clone()); let wrong_hash = unapplied_tx.hash(); let not_found = FindTransactionByHash::new(wrong_hash).execute(&wsv); assert!(matches!( diff --git a/core/src/sumeragi/main_loop.rs b/core/src/sumeragi/main_loop.rs index fcca60b867b..90a03d8123f 100644 --- a/core/src/sumeragi/main_loop.rs +++ b/core/src/sumeragi/main_loop.rs @@ -250,8 +250,7 @@ impl Sumeragi { let mut new_wsv = self.wsv.clone(); let genesis = BlockBuilder::new(transactions, self.current_topology.clone(), vec![]) .chain(0, &mut new_wsv) - .sign(self.key_pair.clone()) - .expect("Genesis signing failed"); + .sign(self.key_pair.clone()); let genesis_msg = MessagePacket::new( ProofChain::default(), @@ -373,8 +372,7 @@ fn suggest_view_change( ) { let suspect_proof = ProofBuilder::new(sumeragi.wsv.latest_block_hash(), current_view_change_index) - .sign(sumeragi.key_pair.clone()) - .expect("Proof signing failed"); + .sign(sumeragi.key_pair.clone()); view_change_proof_chain .insert_proof( @@ -610,20 +608,13 @@ fn process_message_independent( // TODO: properly process triggers! let mut new_wsv = sumeragi.wsv.clone(); let event_recommendations = Vec::new(); - let new_block = match BlockBuilder::new( + let new_block = BlockBuilder::new( transactions, sumeragi.current_topology.clone(), event_recommendations, ) .chain(current_view_change_index, &mut new_wsv) - .sign(sumeragi.key_pair.clone()) - { - Ok(block) => block, - Err(error) => { - error!(?error, "Failed to sign block"); - return; - } - }; + .sign(sumeragi.key_pair.clone()); if let Some(current_topology) = current_topology.is_consensus_required() { info!(%addr, block_payload_hash=%new_block.payload().hash(), "Block created"); @@ -1008,9 +999,7 @@ fn vote_for_block( } }; - let signed_block = block - .sign(sumeragi.key_pair.clone()) - .expect("Block signing failed"); + let signed_block = block.sign(sumeragi.key_pair.clone()); Some(VotingBlock::new(signed_block, new_wsv)) } @@ -1214,16 +1203,14 @@ mod tests { // Making two transactions that have the same instruction let tx = TransactionBuilder::new(alice_id.clone()) .with_instructions([fail_box]) - .sign(alice_keys.clone()) - .expect("Valid"); + .sign(alice_keys.clone()); let tx = AcceptedTransaction::accept(tx, &wsv.transaction_executor().transaction_limits) .expect("Valid"); // Creating a block of two identical transactions and validating it let block = BlockBuilder::new(vec![tx.clone(), tx], topology.clone(), Vec::new()) .chain(0, &mut wsv) - .sign(leader_key_pair.clone()) - .expect("Block is valid"); + .sign(leader_key_pair.clone()); let genesis = block.commit(topology).expect("Block is valid"); wsv.apply(&genesis).expect("Failed to apply block"); @@ -1239,15 +1226,13 @@ mod tests { let tx1 = TransactionBuilder::new(alice_id.clone()) .with_instructions([create_asset_definition1]) - .sign(alice_keys.clone()) - .expect("Valid"); + .sign(alice_keys.clone()); let tx1 = AcceptedTransaction::accept(tx1, &wsv.transaction_executor().transaction_limits) .map(Into::into) .expect("Valid"); let tx2 = TransactionBuilder::new(alice_id) .with_instructions([create_asset_definition2]) - .sign(alice_keys) - .expect("Valid"); + .sign(alice_keys); let tx2 = AcceptedTransaction::accept(tx2, &wsv.transaction_executor().transaction_limits) .map(Into::into) .expect("Valid"); @@ -1255,8 +1240,7 @@ mod tests { // Creating a block of two identical transactions and validating it let block = BlockBuilder::new(vec![tx1, tx2], topology.clone(), Vec::new()) .chain(0, &mut wsv.clone()) - .sign(leader_key_pair) - .expect("Block is valid"); + .sign(leader_key_pair); (wsv, kura, block.into()) } @@ -1266,8 +1250,8 @@ mod tests { async fn block_sync_invalid_block() { let leader_key_pair = KeyPair::generate().unwrap(); let topology = Topology::new(unique_vec![PeerId::new( - &"127.0.0.1:8080".parse().unwrap(), - leader_key_pair.public_key(), + "127.0.0.1:8080".parse().unwrap(), + leader_key_pair.public_key().clone(), )]); let (finalized_wsv, _, mut block) = create_data_for_test(&topology, leader_key_pair); let wsv = finalized_wsv.clone(); @@ -1283,8 +1267,8 @@ mod tests { async fn block_sync_invalid_soft_fork_block() { let leader_key_pair = KeyPair::generate().unwrap(); let topology = Topology::new(unique_vec![PeerId::new( - &"127.0.0.1:8080".parse().unwrap(), - leader_key_pair.public_key(), + "127.0.0.1:8080".parse().unwrap(), + leader_key_pair.public_key().clone(), )]); let (finalized_wsv, kura, mut block) = create_data_for_test(&topology, leader_key_pair); let mut wsv = finalized_wsv.clone(); @@ -1334,8 +1318,8 @@ mod tests { async fn block_sync_commit_block() { let leader_key_pair = KeyPair::generate().unwrap(); let topology = Topology::new(unique_vec![PeerId::new( - &"127.0.0.1:8080".parse().unwrap(), - leader_key_pair.public_key(), + "127.0.0.1:8080".parse().unwrap(), + leader_key_pair.public_key().clone(), )]); let (finalized_wsv, _, block) = create_data_for_test(&topology, leader_key_pair); let wsv = finalized_wsv.clone(); @@ -1347,8 +1331,8 @@ mod tests { async fn block_sync_replace_top_block() { let leader_key_pair = KeyPair::generate().unwrap(); let topology = Topology::new(unique_vec![PeerId::new( - &"127.0.0.1:8080".parse().unwrap(), - leader_key_pair.public_key(), + "127.0.0.1:8080".parse().unwrap(), + leader_key_pair.public_key().clone(), )]); let (finalized_wsv, kura, mut block) = create_data_for_test(&topology, leader_key_pair); let mut wsv = finalized_wsv.clone(); @@ -1371,8 +1355,8 @@ mod tests { async fn block_sync_small_view_change_index() { let leader_key_pair = KeyPair::generate().unwrap(); let topology = Topology::new(unique_vec![PeerId::new( - &"127.0.0.1:8080".parse().unwrap(), - leader_key_pair.public_key(), + "127.0.0.1:8080".parse().unwrap(), + leader_key_pair.public_key().clone(), )]); let (finalized_wsv, kura, mut block) = create_data_for_test(&topology, leader_key_pair); let mut wsv = finalized_wsv.clone(); diff --git a/core/src/sumeragi/network_topology.rs b/core/src/sumeragi/network_topology.rs index 4ba77806e45..1d5a0b57665 100644 --- a/core/src/sumeragi/network_topology.rs +++ b/core/src/sumeragi/network_topology.rs @@ -92,19 +92,19 @@ impl Topology { for role in roles { match (role, self.is_non_empty(), self.is_consensus_required()) { (Role::Leader, Some(topology), _) => { - public_keys.insert(&topology.leader().public_key); + public_keys.insert(topology.leader().public_key()); } (Role::ProxyTail, _, Some(topology)) => { public_keys.insert(&topology.proxy_tail().public_key); } (Role::ValidatingPeer, _, Some(topology)) => { for peer in topology.validating_peers() { - public_keys.insert(&peer.public_key); + public_keys.insert(peer.public_key()); } } (Role::ObservingPeer, _, Some(topology)) => { for peer in topology.observing_peers() { - public_keys.insert(&peer.public_key); + public_keys.insert(peer.public_key()); } } _ => {} @@ -265,7 +265,7 @@ macro_rules! test_peers { }}; ($($id:literal),+$(,)?: $key_pair_iter:expr) => { ::iroha_primitives::unique_vec![ - $(PeerId::new(&(([0, 0, 0, 0], $id).into()), $key_pair_iter.next().expect("Not enough key pairs").public_key())),+ + $(PeerId::new(([0, 0, 0, 0], $id).into(), $key_pair_iter.next().expect("Not enough key pairs").public_key().clone())),+ ] }; } @@ -344,7 +344,7 @@ mod tests { let dummy = "value to sign"; let signatures = key_pairs .iter() - .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy).expect("Failed to sign")) + .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy)) .collect::>>(); let leader_signatures = @@ -386,7 +386,7 @@ mod tests { let dummy = "value to sign"; let signatures = key_pairs .iter() - .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy).expect("Failed to sign")) + .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy)) .collect::>>(); let leader_signatures = @@ -419,7 +419,7 @@ mod tests { let dummy = "value to sign"; let signatures = key_pairs .iter() - .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy).expect("Failed to sign")) + .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy)) .collect::>>(); let leader_signatures = @@ -453,7 +453,7 @@ mod tests { let dummy = "value to sign"; let signatures = key_pairs .iter() - .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy).expect("Failed to sign")) + .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy)) .collect::>>(); let leader_signatures = @@ -488,7 +488,7 @@ mod tests { let dummy = "value to sign"; let signatures = key_pairs .iter() - .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy).expect("Failed to sign")) + .map(|key_pair| SignatureOf::new(key_pair.clone(), &dummy)) .collect::>>(); let leader_signatures = diff --git a/core/src/sumeragi/view_change.rs b/core/src/sumeragi/view_change.rs index 0b0ed73032c..ef9dbd891e0 100644 --- a/core/src/sumeragi/view_change.rs +++ b/core/src/sumeragi/view_change.rs @@ -4,7 +4,7 @@ use derive_more::{Deref, DerefMut}; use eyre::Result; use indexmap::IndexSet; -use iroha_crypto::{HashOf, KeyPair, PublicKey, SignatureOf, SignaturesOf}; +use iroha_crypto::{HashOf, KeyPair, SignatureOf, SignaturesOf}; use iroha_data_model::{block::SignedBlock, prelude::PeerId}; use parity_scale_codec::{Decode, Encode}; use thiserror::Error; @@ -54,13 +54,10 @@ impl ProofBuilder { } /// Sign this message with the peer's public and private key. - /// - /// # Errors - /// Can fail during creation of signature - pub fn sign(mut self, key_pair: KeyPair) -> Result { - let signature = SignatureOf::new(key_pair, &self.0.payload)?; + pub fn sign(mut self, key_pair: KeyPair) -> SignedProof { + let signature = SignatureOf::new(key_pair, &self.0.payload); self.0.signatures.insert(signature); - Ok(self.0) + self.0 } } @@ -76,8 +73,7 @@ impl SignedProof { /// Verify if the proof is valid, given the peers in `topology`. fn verify(&self, peers: &[PeerId], max_faults: usize) -> bool { - let peer_public_keys: IndexSet<&PublicKey> = - peers.iter().map(|peer_id| &peer_id.public_key).collect(); + let peer_public_keys: IndexSet<_> = peers.iter().map(PeerId::public_key).collect(); let valid_count = self .signatures diff --git a/core/test_network/src/lib.rs b/core/test_network/src/lib.rs index 9b217a69d59..1bbe254c82f 100644 --- a/core/test_network/src/lib.rs +++ b/core/test_network/src/lib.rs @@ -483,10 +483,7 @@ impl Peer { let key_pair = KeyPair::generate()?; let p2p_address = local_unique_port()?; let api_address = local_unique_port()?; - let id = PeerId { - address: p2p_address.clone(), - public_key: key_pair.public_key().clone(), - }; + let id = PeerId::new(p2p_address.clone(), key_pair.public_key().clone()); let shutdown = None; Ok(Self { id, diff --git a/crypto/src/encryption/mod.rs b/crypto/src/encryption/mod.rs index d72deacc7fb..39868208156 100644 --- a/crypto/src/encryption/mod.rs +++ b/crypto/src/encryption/mod.rs @@ -68,8 +68,7 @@ fn random_bytes>() -> Result, Error> { /// let encryptor = SymmetricEncryptor::::new_with_key(&key); /// let aad = b"Using ChaCha20Poly1305 to encrypt data"; /// let message = b"Hidden message"; -/// let res = encryptor.encrypt_easy(aad.as_ref(), message.as_ref()); -/// assert!(res.is_ok()); +/// encryptor.encrypt_easy(aad.as_ref(), message.as_ref()).unwrap(); /// /// let ciphertext = res.unwrap(); /// let res = encryptor.decrypt_easy(aad.as_ref(), ciphertext.as_slice()); diff --git a/crypto/src/kex/mod.rs b/crypto/src/kex/mod.rs index f76bc4ac090..de206e96663 100644 --- a/crypto/src/kex/mod.rs +++ b/crypto/src/kex/mod.rs @@ -14,25 +14,27 @@ use crate::{Error, KeyGenOption, PrivateKey, PublicKey, SessionKey}; pub trait KeyExchangeScheme { /// Generate a new instance of the scheme fn new() -> Self; + /// Create new keypairs. If - /// `options` is None, the keys are generated ephemerally from the `OsRng` - /// `options` is `UseSeed`, the keys are generated ephemerally from the sha256 hash of the seed which is - /// then used to seed the `ChaChaRng` - /// `options` is `FromPrivateKey`, the corresponding public key is returned. This should be used for + /// - `options` is [`None`], the keys are generated ephemerally from the [`OsRng`](rand::rngs::OsRng) + /// - `options` is [`UseSeed`](KeyGenOption::UseSeed), the keys are generated ephemerally from the sha256 hash of the seed which is + /// then used to seed the [`ChaChaRng`](rand_chacha::ChaChaRng) + /// - `options` is [`FromPrivateKey`](KeyGenOption::FromPrivateKey), the corresponding public key is returned. This should be used for /// static Diffie-Hellman and loading a long-term key. - /// - /// # Errors - /// - /// Returns an error if the key generation fails. - fn keypair(&self, options: Option) -> Result<(PublicKey, PrivateKey), Error>; + fn keypair(&self, options: Option) -> (PublicKey, PrivateKey); + /// Compute the diffie-hellman shared secret. /// `local_private_key` is the key generated from calling `keypair` while /// `remote_public_key` is the key received from a different call to `keypair` from another party. + /// + /// # Errors + /// + /// Returns an error if the computation fails, i.e. remote key is invalid. fn compute_shared_secret( &self, local_private_key: &PrivateKey, remote_public_key: &PublicKey, - ) -> SessionKey; + ) -> Result; /// Size of the shared secret in bytes. const SHARED_SECRET_SIZE: usize; diff --git a/crypto/src/kex/x25519.rs b/crypto/src/kex/x25519.rs index 63e5c7f6325..9fcbe18ff25 100644 --- a/crypto/src/kex/x25519.rs +++ b/crypto/src/kex/x25519.rs @@ -6,10 +6,8 @@ use sha2::Digest; use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret}; use zeroize::Zeroize; -const ALGORITHM: Algorithm = Algorithm::Ed25519; - use super::KeyExchangeScheme; -use crate::{Algorithm, Error, KeyGenOption, PrivateKey, PublicKey, SessionKey}; +use crate::{Error, KeyGenOption, PrivateKey, PublicKey, SessionKey}; /// Implements the [`KeyExchangeScheme`] using X25519 key exchange and SHA256 hash function. #[derive(Copy, Clone)] @@ -20,7 +18,13 @@ impl KeyExchangeScheme for X25519Sha256 { Self } - fn keypair(&self, mut option: Option) -> Result<(PublicKey, PrivateKey), Error> { + /// # Note about implementation + /// + /// We encode the `X25519` public key as an [`Ed25519`](PublicKey::Ed25519) public key which is + /// a not so good idea, because we have to do extra computations and extra error handling. + /// + /// See #4174 for more details. + fn keypair(&self, mut option: Option) -> (PublicKey, PrivateKey) { let (pk, sk) = match option { Some(KeyGenOption::UseSeed(ref mut s)) => { let hash = sha2::Sha256::digest(s.as_slice()); @@ -31,8 +35,10 @@ impl KeyExchangeScheme for X25519Sha256 { (pk, sk) } Some(KeyGenOption::FromPrivateKey(ref s)) => { - assert_eq!(s.digest_function, ALGORITHM); - let sk = StaticSecret::from(*array_ref!(&s.payload, 0, 32)); + let crate::PrivateKey::Ed25519(s) = s else { + panic!("Wrong private key type, expected `Ed25519`, got {s:?}") + }; + let sk = StaticSecret::from(*array_ref!(s.as_bytes(), 0, 32)); let pk = X25519PublicKey::from(&sk); (pk, sk) } @@ -43,30 +49,54 @@ impl KeyExchangeScheme for X25519Sha256 { (pk, sk) } }; - Ok(( - PublicKey { - digest_function: ALGORITHM, - payload: ConstVec::new(pk.as_bytes().to_vec()), - }, - PrivateKey { - digest_function: ALGORITHM, - payload: ConstVec::new(sk.to_bytes().to_vec()), - }, - )) + + let montgomery = curve25519_dalek::MontgomeryPoint(pk.to_bytes()); + // 0 here means the positive sign, but it doesn't matter, because in + // `compute_shared_secret()` we convert it back to Montgomery form losing the sign. + let edwards = montgomery + .to_edwards(0) + .expect("Montgomery to Edwards conversion failed"); + let edwards_compressed = edwards.compress(); + + ( + PublicKey::Ed25519( + crate::ed25519::PublicKey::from_bytes(edwards_compressed.as_bytes()).expect( + "Ed25519 public key should be possible to create from X25519 public key", + ), + ), + PrivateKey::Ed25519(Box::new(crate::ed25519::PrivateKey::from_bytes( + sk.as_bytes(), + ))), + ) } fn compute_shared_secret( &self, local_private_key: &PrivateKey, remote_public_key: &PublicKey, - ) -> SessionKey { - assert_eq!(local_private_key.digest_function, ALGORITHM); - assert_eq!(remote_public_key.digest_function, ALGORITHM); - let sk = StaticSecret::from(*array_ref!(&local_private_key.payload, 0, 32)); - let pk = X25519PublicKey::from(*array_ref!(&remote_public_key.payload, 0, 32)); + ) -> Result { + let crate::PrivateKey::Ed25519(local_private_key) = local_private_key else { + panic!("Wrong private key type, expected `Ed25519`, got {local_private_key:?}") + }; + let crate::PublicKey::Ed25519(remote_public_key) = remote_public_key else { + panic!("Wrong public key type, expected `Ed25519`, got {remote_public_key:?}") + }; + + let sk = StaticSecret::from(*local_private_key.as_bytes()); + + let pk_slice: &[u8; 32] = remote_public_key.as_bytes(); + let edwards_compressed = + curve25519_dalek::edwards::CompressedEdwardsY::from_slice(pk_slice) + .expect("Ed25519 public key has 32 bytes"); + let edwards = edwards_compressed.decompress().ok_or_else(|| { + Error::Parse("Invalid public key: failed to decompress edwards point".to_owned()) + })?; + let montgomery = edwards.to_montgomery(); + let pk = X25519PublicKey::from(montgomery.to_bytes()); + let shared_secret = sk.diffie_hellman(&pk); let hash = sha2::Sha256::digest(shared_secret.as_bytes()); - SessionKey(ConstVec::new(hash.as_slice().to_vec())) + Ok(SessionKey(ConstVec::new(hash.as_slice().to_vec()))) } const SHARED_SECRET_SIZE: usize = 32; @@ -81,17 +111,19 @@ mod tests { #[test] fn key_exchange() { let scheme = X25519Sha256::new(); - let (public_key1, secret_key1) = scheme.keypair(None).unwrap(); - let _res = scheme.compute_shared_secret(&secret_key1, &public_key1); - let res = scheme.keypair(None); - let (public_key2, secret_key2) = res.unwrap(); - let _res = scheme.compute_shared_secret(&secret_key2, &public_key1); - let _res = scheme.compute_shared_secret(&secret_key1, &public_key2); + let (public_key1, secret_key1) = scheme.keypair(None); - let (public_key2, secret_key1) = scheme - .keypair(Some(KeyGenOption::FromPrivateKey(secret_key1))) + let (public_key2, secret_key2) = scheme.keypair(None); + let shared_secret1 = scheme + .compute_shared_secret(&secret_key2, &public_key1) .unwrap(); + let shared_secret2 = scheme + .compute_shared_secret(&secret_key1, &public_key2) + .unwrap(); + assert_eq!(shared_secret1.payload(), shared_secret2.payload()); + + let (public_key2, _secret_key1) = + scheme.keypair(Some(KeyGenOption::FromPrivateKey(secret_key1))); assert_eq!(public_key2, public_key1); - assert_eq!(secret_key1, secret_key1); } } diff --git a/crypto/src/lib.rs b/crypto/src/lib.rs index 5ae39a89ea2..380778a0a17 100755 --- a/crypto/src/lib.rs +++ b/crypto/src/lib.rs @@ -31,19 +31,19 @@ pub use base64; #[cfg(feature = "std")] #[cfg(not(feature = "ffi_import"))] pub use blake2; -use derive_more::{DebugCustom, Display}; +use derive_more::Display; use error::{Error, NoSuchAlgorithm}; -use getset::{CopyGetters, Getters}; +use getset::Getters; pub use hash::*; use iroha_macro::ffi_impl_opaque; use iroha_primitives::const_vec::ConstVec; -use iroha_schema::IntoSchema; +use iroha_schema::{Declaration, IntoSchema, MetaMap, Metadata, NamedFieldsMeta, TypeId}; pub use merkle::MerkleTree; #[cfg(not(feature = "ffi_import"))] use parity_scale_codec::{Decode, Encode}; #[cfg(feature = "std")] use serde::Deserialize; -use serde::Serialize; +use serde::{ser::SerializeStruct, Serialize}; use serde_with::{DeserializeFromStr, SerializeDisplay}; pub use self::signature::*; @@ -192,8 +192,7 @@ impl KeyPair { self.private_key.digest_function() } - /// Construct `KeyPair` - /// + /// Construct a [`KeyPair`] /// # Errors /// If public and private key don't match, i.e. if they don't make a pair #[cfg(any(feature = "std", feature = "ffi_import"))] @@ -231,22 +230,53 @@ impl KeyPair { (_, key_gen_option) => key_gen_option, }; - let (public_key, private_key) = match configuration.algorithm { - Algorithm::Ed25519 => signature::ed25519::Ed25519Sha512::keypair(key_gen_option), + Ok(match configuration.algorithm { + Algorithm::Ed25519 => signature::ed25519::Ed25519Sha512::keypair(key_gen_option).into(), Algorithm::Secp256k1 => { - signature::secp256k1::EcdsaSecp256k1Sha256::keypair(key_gen_option) + signature::secp256k1::EcdsaSecp256k1Sha256::keypair(key_gen_option).into() } - Algorithm::BlsNormal => signature::bls::BlsNormal::keypair(key_gen_option), - Algorithm::BlsSmall => signature::bls::BlsSmall::keypair(key_gen_option), - }?; - - Ok(Self { - public_key, - private_key, + Algorithm::BlsNormal => signature::bls::BlsNormal::keypair(key_gen_option).into(), + Algorithm::BlsSmall => signature::bls::BlsSmall::keypair(key_gen_option).into(), }) } } +impl From<(ed25519::PublicKey, ed25519::PrivateKey)> for KeyPair { + fn from((public_key, private_key): (ed25519::PublicKey, ed25519::PrivateKey)) -> Self { + Self { + public_key: PublicKey::Ed25519(public_key), + private_key: PrivateKey::Ed25519(Box::new(private_key)), + } + } +} + +impl From<(secp256k1::PublicKey, secp256k1::PrivateKey)> for KeyPair { + fn from((public_key, private_key): (secp256k1::PublicKey, secp256k1::PrivateKey)) -> Self { + Self { + public_key: PublicKey::Secp256k1(public_key), + private_key: PrivateKey::Secp256k1(private_key), + } + } +} + +impl From<(bls::BlsNormalPublicKey, bls::PrivateKey)> for KeyPair { + fn from((public_key, private_key): (bls::BlsNormalPublicKey, bls::PrivateKey)) -> Self { + Self { + public_key: PublicKey::BlsNormal(public_key), + private_key: PrivateKey::BlsNormal(private_key), + } + } +} + +impl From<(bls::BlsSmallPublicKey, bls::PrivateKey)> for KeyPair { + fn from((public_key, private_key): (bls::BlsSmallPublicKey, bls::PrivateKey)) -> Self { + Self { + public_key: PublicKey::BlsSmall(Box::new(public_key)), + private_key: PrivateKey::BlsSmall(private_key), + } + } +} + #[cfg(feature = "std")] #[cfg(not(feature = "ffi_import"))] impl<'de> Deserialize<'de> for KeyPair { @@ -278,59 +308,52 @@ impl From for (PublicKey, PrivateKey) { ffi::ffi_item! { /// Public Key used in signatures. - #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, CopyGetters)] - #[cfg_attr(not(feature="ffi_import"), derive(DebugCustom, Display, Hash, DeserializeFromStr, SerializeDisplay, Decode, Encode, IntoSchema))] - #[cfg_attr(not(feature="ffi_import"), debug(fmt = "{{digest: {digest_function}, payload: {}}}", "self.normalize()"))] - #[cfg_attr(not(feature="ffi_import"), display(fmt = "{}", "self.normalize()"))] - pub struct PublicKey { - /// Digest function - #[getset(get_copy = "pub")] - digest_function: Algorithm, - /// Key payload - payload: ConstVec, + #[derive(Clone, PartialEq, Eq)] + #[cfg_attr(not(feature="ffi_import"), derive(DeserializeFromStr, SerializeDisplay))] + #[cfg_attr(all(feature = "ffi_export", not(feature = "ffi_import")), ffi_type(opaque))] + #[allow(missing_docs)] + pub enum PublicKey { + Ed25519(ed25519::PublicKey), + Secp256k1(secp256k1::PublicKey), + BlsNormal(bls::BlsNormalPublicKey), + BlsSmall(Box), } } #[ffi_impl_opaque] impl PublicKey { /// Creates a new public key from raw bytes received from elsewhere - pub fn from_raw(algorithm: Algorithm, payload: ConstVec) -> Self { - Self { - digest_function: algorithm, - payload, + pub fn from_raw(algorithm: Algorithm, payload: &[u8]) -> Result { + match algorithm { + Algorithm::Ed25519 => { + ed25519::Ed25519Sha512::parse_public_key(payload).map(Self::Ed25519) + } + Algorithm::Secp256k1 => { + secp256k1::EcdsaSecp256k1Sha256::parse_public_key(payload).map(Self::Secp256k1) + } + Algorithm::BlsNormal => bls::BlsNormal::parse_public_key(payload).map(Self::BlsNormal), + Algorithm::BlsSmall => bls::BlsSmall::parse_public_key(payload) + .map(Box::new) + .map(Self::BlsSmall), } } /// Extracts the raw bytes from public key - pub fn into_raw(self) -> (Algorithm, ConstVec) { - (self.digest_function, self.payload) + pub fn into_raw(self) -> (Algorithm, Vec) { + (self.digest_function(), self.payload()) } /// Key payload - // TODO: Derive with getset once FFI impl is fixed - pub fn payload(&self) -> &[u8] { - self.payload.as_ref() - } - - #[cfg(feature = "std")] - fn try_from_private(private_key: PrivateKey) -> Result { - let digest_function = private_key.digest_function(); - let key_gen_option = Some(KeyGenOption::FromPrivateKey(private_key)); - - let (public_key, _) = match digest_function { - Algorithm::Ed25519 => signature::ed25519::Ed25519Sha512::keypair(key_gen_option), - Algorithm::Secp256k1 => { - signature::secp256k1::EcdsaSecp256k1Sha256::keypair(key_gen_option) - } - Algorithm::BlsNormal => signature::bls::BlsNormal::keypair(key_gen_option), - Algorithm::BlsSmall => signature::bls::BlsSmall::keypair(key_gen_option), - }?; - - Ok(public_key) + pub fn payload(&self) -> Vec { + match self { + PublicKey::Ed25519(key) => key.as_bytes().to_vec(), + PublicKey::Secp256k1(key) => key.to_sec1_bytes().to_vec(), + PublicKey::BlsNormal(key) => key.to_bytes(), + PublicKey::BlsSmall(key) => key.to_bytes(), + } } - /// Construct `PrivateKey` from hex encoded string - /// + /// Construct [`PublicKey`] from hex encoded string /// # Errors /// /// - If the given payload is not hex encoded @@ -338,19 +361,114 @@ impl PublicKey { #[cfg(feature = "std")] pub fn from_hex(digest_function: Algorithm, payload: &str) -> Result { let payload = hex_decode(payload)?; - let payload = ConstVec::new(payload); - // NOTE: PrivateKey does some validation by generating a public key from the provided bytes - // we can't really do this for PublicKey - // this can be solved if the keys used here would be actually aware of the underlying crypto primitive types - // instead of just being raw bytes - Ok(Self { - digest_function, - payload, + Self::from_raw(digest_function, &payload) + } + + /// Get the digest function of the public key + pub fn digest_function(&self) -> Algorithm { + match self { + Self::Ed25519(_) => Algorithm::Ed25519, + Self::Secp256k1(_) => Algorithm::Secp256k1, + Self::BlsNormal(_) => Algorithm::BlsNormal, + Self::BlsSmall(_) => Algorithm::BlsSmall, + } + } +} + +#[cfg(not(feature = "ffi_import"))] +impl fmt::Debug for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple(self.digest_function().as_static_str()) + .field(&self.normalize()) + .finish() + } +} + +#[cfg(not(feature = "ffi_import"))] +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.normalize()) + } +} + +#[cfg(not(feature = "ffi_import"))] +impl core::hash::Hash for PublicKey { + fn hash(&self, state: &mut H) { + (self.digest_function(), self.payload()).hash(state) + } +} + +impl PartialOrd for PublicKey { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for PublicKey { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + (self.digest_function(), self.payload()).cmp(&(other.digest_function(), other.payload())) + } +} + +#[cfg(not(feature = "ffi_import"))] +impl Encode for PublicKey { + fn size_hint(&self) -> usize { + self.digest_function().size_hint() + self.payload().size_hint() + } + + fn encode_to(&self, dest: &mut W) { + self.digest_function().encode_to(dest); + self.payload().encode_to(dest); + } +} + +#[cfg(not(feature = "ffi_import"))] +impl Decode for PublicKey { + fn decode( + input: &mut I, + ) -> Result { + let digest_function = Algorithm::decode(input)?; + let payload = >::decode(input)?; + Self::from_raw(digest_function, &payload).map_err(|_| { + parity_scale_codec::Error::from( + "Failed to construct public key from digest function and payload", + ) }) } } +#[cfg(not(feature = "ffi_import"))] +impl IntoSchema for PublicKey { + fn type_name() -> String { + Self::id() + } + + fn update_schema_map(metamap: &mut MetaMap) { + if !metamap.contains_key::() { + metamap.insert::(Metadata::Struct(NamedFieldsMeta { + declarations: vec![ + Declaration { + name: String::from("algorithm"), + ty: core::any::TypeId::of::(), + }, + Declaration { + name: String::from("payload"), + ty: core::any::TypeId::of::>(), + }, + ], + })); + } + } +} + +#[cfg(not(feature = "ffi_import"))] +impl TypeId for PublicKey { + fn id() -> String { + "PublicKey".to_owned() + } +} + impl FromStr for PublicKey { type Err = Error; @@ -360,7 +478,7 @@ impl FromStr for PublicKey { multihash::Multihash::try_from(bytes) .map_err(|err| Error::Parse(err.to_string())) - .map(Into::into) + .and_then(TryInto::try_into) } } @@ -384,52 +502,69 @@ impl PublicKey { #[cfg(not(feature = "ffi_import"))] impl From for PublicKey { fn from(private_key: PrivateKey) -> Self { - Self::try_from_private(private_key).expect("can't fail for valid `PrivateKey`") + let digest_function = private_key.digest_function(); + let key_gen_option = Some(KeyGenOption::FromPrivateKey(private_key)); + + match digest_function { + Algorithm::Ed25519 => { + PublicKey::Ed25519(ed25519::Ed25519Sha512::keypair(key_gen_option).0) + } + Algorithm::Secp256k1 => { + PublicKey::Secp256k1(secp256k1::EcdsaSecp256k1Sha256::keypair(key_gen_option).0) + } + Algorithm::BlsNormal => PublicKey::BlsNormal(bls::BlsNormal::keypair(key_gen_option).0), + Algorithm::BlsSmall => { + PublicKey::BlsSmall(Box::new(bls::BlsSmall::keypair(key_gen_option).0)) + } + } } } ffi::ffi_item! { /// Private Key used in signatures. - #[derive(Clone, PartialEq, Eq, CopyGetters)] - #[cfg_attr(not(feature="ffi_import"), derive(DebugCustom, Display, Serialize))] - #[cfg_attr(not(feature="ffi_import"), debug(fmt = "{{digest: {digest_function}, payload: {}}}", "hex::encode_upper(payload)"))] - #[cfg_attr(not(feature="ffi_import"), display(fmt = "{}", "hex::encode_upper(payload)"))] - pub struct PrivateKey { - /// Digest function - #[getset(get_copy = "pub")] - digest_function: Algorithm, - /// Key payload - #[serde(with = "hex::serde")] - payload: ConstVec, + #[derive(Clone, PartialEq, Eq)] + #[cfg_attr(all(feature = "ffi_export", not(feature = "ffi_import")), ffi_type(opaque))] + #[allow(missing_docs)] + pub enum PrivateKey { + Ed25519(Box), + Secp256k1(secp256k1::PrivateKey), + BlsNormal(bls::PrivateKey), + BlsSmall(bls::PrivateKey), } } #[ffi_impl_opaque] impl PrivateKey { /// Key payload - // TODO: Derive with getset once FFI impl is fixed - pub fn payload(&self) -> &[u8] { - self.payload.as_ref() + pub fn payload(&self) -> Vec { + match self { + Self::Ed25519(key) => key.to_keypair_bytes().to_vec(), + Self::Secp256k1(key) => key.to_bytes().to_vec(), + Self::BlsNormal(key) | Self::BlsSmall(key) => key.to_bytes(), + } } } impl PrivateKey { - /// Construct `PrivateKey` from hex encoded string without validating the key + /// Creates a new public key from raw bytes received from elsewhere /// /// # Errors /// - /// If the given payload is not hex encoded - pub fn from_hex_unchecked( - digest_function: Algorithm, - payload: &(impl AsRef<[u8]> + ?Sized), - ) -> Result { - Ok(Self { - digest_function, - payload: crate::hex_decode(payload).map(ConstVec::new)?, - }) + /// - If the given payload is not a valid private key for the given digest function + pub fn from_raw(digest_function: Algorithm, payload: &[u8]) -> Result { + match digest_function { + Algorithm::Ed25519 => ed25519::Ed25519Sha512::parse_private_key(payload) + .map(Box::new) + .map(Self::Ed25519), + Algorithm::Secp256k1 => { + secp256k1::EcdsaSecp256k1Sha256::parse_private_key(payload).map(Self::Secp256k1) + } + Algorithm::BlsNormal => bls::BlsNormal::parse_private_key(payload).map(Self::BlsNormal), + Algorithm::BlsSmall => bls::BlsSmall::parse_private_key(payload).map(Self::BlsSmall), + } } - /// Construct `PrivateKey` from hex encoded string + /// Construct [`PrivateKey`] from hex encoded string /// /// # Errors /// @@ -440,15 +575,43 @@ impl PrivateKey { let payload = hex_decode(payload)?; let payload = ConstVec::new(payload); - let private_key_candidate = Self { - digest_function, - payload: payload.clone(), - }; + Self::from_raw(digest_function, &payload) + } - PublicKey::try_from_private(private_key_candidate).map(|_| Self { - digest_function, - payload, - }) + /// Get the digest function of the private key + pub fn digest_function(&self) -> Algorithm { + match self { + Self::Ed25519(_) => Algorithm::Ed25519, + Self::Secp256k1(_) => Algorithm::Secp256k1, + Self::BlsNormal(_) => Algorithm::BlsNormal, + Self::BlsSmall(_) => Algorithm::BlsSmall, + } + } +} + +#[cfg(not(feature = "ffi_import"))] +impl std::fmt::Debug for PrivateKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple(self.digest_function().as_static_str()) + .field(&hex::encode_upper(self.payload())) + .finish() + } +} + +#[cfg(not(feature = "ffi_import"))] +impl std::fmt::Display for PrivateKey { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&hex::encode_upper(self.payload())) + } +} + +#[cfg(not(feature = "ffi_import"))] +impl Serialize for PrivateKey { + fn serialize(&self, serializer: S) -> Result { + let mut state = serializer.serialize_struct("PublicKey", 2)?; + state.serialize_field("digest_function", &self.digest_function())?; + state.serialize_field("payload", &hex::encode(self.payload()))?; + state.end() } } @@ -513,6 +676,9 @@ pub mod error { /// Returned when an error occurs during the signing process #[display(fmt = "Signing failed. {_0}")] Signing(String), + /// Returned when an error occurs during the signature verification process + #[display(fmt = "Signature verification failed")] + BadSignature, /// Returned when an error occurs during key generation #[display(fmt = "Key generation failed. {_0}")] KeyGen(String), @@ -612,10 +778,6 @@ mod tests { use super::*; - fn parse_const_bytes(hex: &str) -> ConstVec { - ConstVec::new(hex_decode(hex).expect("Failed to decode hex bytes")) - } - #[test] fn algorithm_serialize_deserialize_consistent() { for algorithm in [ @@ -763,48 +925,44 @@ mod tests { assert_eq!( format!( "{}", - PublicKey { - digest_function: Algorithm::Ed25519, - payload: parse_const_bytes( - "1509A611AD6D97B01D871E58ED00C8FD7C3917B6CA61A8C2833A19E000AAC2E4" - ) - } + PublicKey::from_hex( + Algorithm::Ed25519, + "1509A611AD6D97B01D871E58ED00C8FD7C3917B6CA61A8C2833A19E000AAC2E4" + ) + .unwrap() ), "ed01201509A611AD6D97B01D871E58ED00C8FD7C3917B6CA61A8C2833A19E000AAC2E4" ); assert_eq!( format!( "{}", - PublicKey { - digest_function: Algorithm::Secp256k1, - payload: parse_const_bytes( - "0312273E8810581E58948D3FB8F9E8AD53AAA21492EBB8703915BBB565A21B7FCC" - ) - } + PublicKey::from_hex( + Algorithm::Secp256k1, + "0312273E8810581E58948D3FB8F9E8AD53AAA21492EBB8703915BBB565A21B7FCC" + ) + .unwrap() ), "e701210312273E8810581E58948D3FB8F9E8AD53AAA21492EBB8703915BBB565A21B7FCC" ); assert_eq!( format!( "{}", - PublicKey { - digest_function: Algorithm::BlsNormal, - payload: parse_const_bytes( + PublicKey::from_hex( + Algorithm::BlsNormal, + "04175B1E79B15E8A2D5893BF7F8933CA7D0863105D8BAC3D6F976CB043378A0E4B885C57ED14EB85FC2FABC639ADC7DE7F0020C70C57ACC38DEE374AF2C04A6F61C11DE8DF9034B12D849C7EB90099B0881267D0E1507D4365D838D7DCC31511E7" - ) - } + ).unwrap() ), "ea016104175B1E79B15E8A2D5893BF7F8933CA7D0863105D8BAC3D6F976CB043378A0E4B885C57ED14EB85FC2FABC639ADC7DE7F0020C70C57ACC38DEE374AF2C04A6F61C11DE8DF9034B12D849C7EB90099B0881267D0E1507D4365D838D7DCC31511E7" ); assert_eq!( format!( "{}", - PublicKey { - digest_function: Algorithm::BlsSmall, - payload: parse_const_bytes( + PublicKey::from_hex( + Algorithm::BlsSmall, + "040CB3231F601E7245A6EC9A647B450936F707CA7DC347ED258586C1924941D8BC38576473A8BA3BB2C37E3E121130AB67103498A96D0D27003E3AD960493DA79209CF024E2AA2AE961300976AEEE599A31A5E1B683EAA1BCFFC47B09757D20F21123C594CF0EE0BAF5E1BDD272346B7DC98A8F12C481A6B28174076A352DA8EAE881B90911013369D7FA960716A5ABC5314307463FA2285A5BF2A5B5C6220D68C2D34101A91DBFC531C5B9BBFB2245CCC0C50051F79FC6714D16907B1FC40E0C0" - ) - } + ).unwrap() ), "eb01c1040CB3231F601E7245A6EC9A647B450936F707CA7DC347ED258586C1924941D8BC38576473A8BA3BB2C37E3E121130AB67103498A96D0D27003E3AD960493DA79209CF024E2AA2AE961300976AEEE599A31A5E1B683EAA1BCFFC47B09757D20F21123C594CF0EE0BAF5E1BDD272346B7DC98A8F12C481A6B28174076A352DA8EAE881B90911013369D7FA960716A5ABC5314307463FA2285A5BF2A5B5C6220D68C2D34101A91DBFC531C5B9BBFB2245CCC0C50051F79FC6714D16907B1FC40E0C0" ); @@ -828,16 +986,15 @@ mod tests { } }").expect("Failed to deserialize."), TestJson { - public_key: PublicKey { - digest_function: Algorithm::Ed25519, - payload: parse_const_bytes( + public_key: PublicKey::from_hex( + Algorithm::Ed25519, + "1509A611AD6D97B01D871E58ED00C8FD7C3917B6CA61A8C2833A19E000AAC2E4" - ) - }, - private_key: PrivateKey { - digest_function: Algorithm::Ed25519, - payload: parse_const_bytes("3A7991AF1ABB77F3FD27CC148404A6AE4439D095A63591B77C788D53F708A02A1509A611AD6D97B01D871E58ED00C8FD7C3917B6CA61A8C2833A19E000AAC2E4"), - } + ).unwrap(), + private_key: PrivateKey::from_hex( + Algorithm::Ed25519, + "3A7991AF1ABB77F3FD27CC148404A6AE4439D095A63591B77C788D53F708A02A1509A611AD6D97B01D871E58ED00C8FD7C3917B6CA61A8C2833A19E000AAC2E4", + ).unwrap() } ); } @@ -854,16 +1011,15 @@ mod tests { } }").expect("Failed to deserialize."), TestJson { - public_key: PublicKey { - digest_function: Algorithm::Secp256k1, - payload: parse_const_bytes( + public_key: PublicKey::from_hex( + Algorithm::Secp256k1, + "0312273E8810581E58948D3FB8F9E8AD53AAA21492EBB8703915BBB565A21B7FCC" - ) - }, - private_key: PrivateKey { - digest_function: Algorithm::Secp256k1, - payload: parse_const_bytes("4DF4FCA10762D4B529FE40A2188A60CA4469D2C50A825B5F33ADC2CB78C69445"), - } + ).unwrap(), + private_key: PrivateKey::from_hex( + Algorithm::Secp256k1, + "4DF4FCA10762D4B529FE40A2188A60CA4469D2C50A825B5F33ADC2CB78C69445", + ).unwrap() } ); } @@ -880,16 +1036,15 @@ mod tests { } }").expect("Failed to deserialize."), TestJson { - public_key: PublicKey { - digest_function: Algorithm::BlsNormal, - payload: parse_const_bytes( + public_key: PublicKey::from_hex( + Algorithm::BlsNormal, + "04175B1E79B15E8A2D5893BF7F8933CA7D0863105D8BAC3D6F976CB043378A0E4B885C57ED14EB85FC2FABC639ADC7DE7F0020C70C57ACC38DEE374AF2C04A6F61C11DE8DF9034B12D849C7EB90099B0881267D0E1507D4365D838D7DCC31511E7" - ) - }, - private_key: PrivateKey { - digest_function: Algorithm::BlsNormal, - payload: parse_const_bytes("000000000000000000000000000000002F57460183837EFBAC6AA6AB3B8DBB7CFFCFC59E9448B7860A206D37D470CBA3"), - } + ).unwrap(), + private_key: PrivateKey::from_hex( + Algorithm::BlsNormal, + "000000000000000000000000000000002F57460183837EFBAC6AA6AB3B8DBB7CFFCFC59E9448B7860A206D37D470CBA3", + ).unwrap() } ); assert_eq!( @@ -901,17 +1056,16 @@ mod tests { } }").expect("Failed to deserialize."), TestJson { - public_key: PublicKey { - digest_function: Algorithm::BlsSmall, - payload: parse_const_bytes( + public_key: PublicKey::from_hex( + Algorithm::BlsSmall, + "040CB3231F601E7245A6EC9A647B450936F707CA7DC347ED258586C1924941D8BC38576473A8BA3BB2C37E3E121130AB67103498A96D0D27003E3AD960493DA79209CF024E2AA2AE961300976AEEE599A31A5E1B683EAA1BCFFC47B09757D20F21123C594CF0EE0BAF5E1BDD272346B7DC98A8F12C481A6B28174076A352DA8EAE881B90911013369D7FA960716A5ABC5314307463FA2285A5BF2A5B5C6220D68C2D34101A91DBFC531C5B9BBFB2245CCC0C50051F79FC6714D16907B1FC40E0C0" - ) - }, - private_key: PrivateKey { - digest_function: Algorithm::BlsSmall, - payload: parse_const_bytes( - "0000000000000000000000000000000060F3C1AC9ADDBBED8DB83BC1B2EF22139FB049EECB723A557A41CA1A4B1FED63"), - } + ).unwrap(), + private_key: PrivateKey::from_hex( + Algorithm::BlsSmall, + + "0000000000000000000000000000000060F3C1AC9ADDBBED8DB83BC1B2EF22139FB049EECB723A557A41CA1A4B1FED63", + ).unwrap() } ); } diff --git a/crypto/src/multihash.rs b/crypto/src/multihash.rs index 579f9708d02..6b07d819a4b 100644 --- a/crypto/src/multihash.rs +++ b/crypto/src/multihash.rs @@ -1,4 +1,5 @@ //! Module with multihash implementation + #[cfg(not(feature = "std"))] use alloc::{ string::{String, ToString as _}, @@ -154,9 +155,11 @@ impl TryFrom<&Multihash> for Vec { } } -impl From for PublicKey { +impl TryFrom for PublicKey { + type Error = crate::Error; + #[inline] - fn from(multihash: Multihash) -> Self { + fn try_from(multihash: Multihash) -> Result { let digest_function = match multihash.digest_function { DigestFunction::Ed25519Pub => Algorithm::Ed25519, DigestFunction::Secp256k1Pub => Algorithm::Secp256k1, @@ -164,10 +167,7 @@ impl From for PublicKey { DigestFunction::Bls12381G2Pub => Algorithm::BlsSmall, }; - Self { - digest_function, - payload: multihash.payload, - } + Self::from_raw(digest_function, &multihash.payload) } } @@ -183,7 +183,7 @@ impl From for Multihash { Self { digest_function, - payload: public_key.payload, + payload: public_key.payload().into(), } } } diff --git a/crypto/src/signature/bls/implementation.rs b/crypto/src/signature/bls/implementation.rs index d7084aa82c7..1794b04342b 100644 --- a/crypto/src/signature/bls/implementation.rs +++ b/crypto/src/signature/bls/implementation.rs @@ -15,10 +15,7 @@ pub(super) const MESSAGE_CONTEXT: &[u8; 20] = b"for signing messages"; const PUBLICKEY_CONTEXT: &[u8; 47] = b"for signing public keys for proof of possession"; use super::PRIVATE_KEY_SIZE; -use crate::{ - Algorithm, ConstVec, Error, KeyGenOption, PrivateKey as IrohaPrivateKey, - PublicKey as IrohaPublicKey, -}; +use crate::{Algorithm, Error, KeyGenOption}; /// This is a simple alias so the consumer can just use `PrivateKey::random`() to generate a new one /// instead of wrapping it as a private field @@ -30,12 +27,14 @@ pub trait BlsConfiguration { const SIG_SIZE: usize; type Generator: GroupElement + Eq + PartialEq + Hash; type SignatureGroup: GroupElement + Eq + PartialEq + Hash; + fn ate_2_pairing_is_one( g: &Self::Generator, sig: &Self::SignatureGroup, pk: &Self::Generator, hash: &Self::SignatureGroup, ) -> bool; + fn set_pairs(p: &(Self::Generator, Self::SignatureGroup)) -> (&G1, &G2); /// Creates a new BLS key pair @@ -64,6 +63,8 @@ pub trait BlsConfiguration { let ctx: &[u8] = context.unwrap_or(PUBLICKEY_CONTEXT); Self::hash_to_point(pk.to_bytes(), ctx) } + + fn extract_private_key(private_key: &crate::PrivateKey) -> Option<&PrivateKey>; } pub struct PublicKey(C::Generator); @@ -86,6 +87,43 @@ impl PublicKey { } } +impl core::fmt::Debug for PublicKey +where + C: BlsConfiguration + ?Sized, + C::Generator: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("PublicKey").field(&self.0).finish() + } +} + +impl PartialEq for PublicKey +where + C: BlsConfiguration + ?Sized, + C::Generator: PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for PublicKey +where + C: BlsConfiguration + ?Sized, + C::Generator: Eq, +{ +} + +impl Clone for PublicKey +where + C: BlsConfiguration + ?Sized, + C::Generator: Clone, +{ + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + /// Signature over a message. One gotcha for BLS signatures /// is the need to mitigate rogue key attacks. There are two methods to achieve /// this: compute additional work to make each message distinct @@ -137,25 +175,12 @@ impl Signature { pub struct BlsImpl(PhantomData); impl BlsImpl { - fn parse_public_key(pk: &IrohaPublicKey) -> Result, Error> { - assert_eq!(pk.digest_function, C::ALGORITHM); - PublicKey::from_bytes(&pk.payload) - .map_err(|e| Error::Parse(format!("Failed to parse public key: {e}"))) - } - - fn parse_private_key(sk: &IrohaPrivateKey) -> Result { - assert_eq!(sk.digest_function, C::ALGORITHM); - PrivateKey::from_bytes(&sk.payload) - .map_err(|e| Error::Parse(format!("Failed to parse private key: {e}"))) - } - // the names are from an RFC, not a good idea to change them #[allow(clippy::similar_names)] - pub fn keypair( - options: Option, - ) -> Result<(IrohaPublicKey, IrohaPrivateKey), Error> { - let (public_key, private_key) = match options { - Some(option) => match option { + pub fn keypair(option: Option) -> (PublicKey, PrivateKey) { + option.map_or_else( + || C::generate(&C::Generator::generator()), + |o| match o { // Follows https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/?include_text=1 KeyGenOption::UseSeed(ref seed) => { let salt = b"BLS-SIG-KEYGEN-SALT-"; @@ -164,9 +189,8 @@ impl BlsImpl { ikm[..seed.len()].copy_from_slice(seed); // IKM || I2OSP(0, 1) let mut okm = [0u8; PRIVATE_KEY_SIZE]; let h = hkdf::Hkdf::::new(Some(&salt[..]), &ikm); - h.expand(&info[..], &mut okm).map_err(|err| { - Error::KeyGen(format!("Failed to generate keypair: {err}")) - })?; + h.expand(&info[..], &mut okm) + .expect("`okm` has the correct length"); let private_key: PrivateKey = PrivateKey::from(&okm); ( PublicKey::new(&private_key, &C::Generator::generator()), @@ -174,38 +198,41 @@ impl BlsImpl { ) } KeyGenOption::FromPrivateKey(ref key) => { - let private_key = Self::parse_private_key(key)?; + let private_key = C::extract_private_key(key).unwrap_or_else(|| { + panic!( + "Wrong private key type for {} algorithm, got {key:?}", + C::ALGORITHM, + ) + }); ( - PublicKey::new(&private_key, &C::Generator::generator()), - private_key, + PublicKey::new(private_key, &C::Generator::generator()), + private_key.clone(), ) } }, - None => C::generate(&C::Generator::generator()), - }; - Ok(( - IrohaPublicKey { - digest_function: C::ALGORITHM, - payload: ConstVec::new(public_key.to_bytes()), - }, - IrohaPrivateKey { - digest_function: C::ALGORITHM, - payload: ConstVec::new(private_key.to_bytes()), - }, - )) + ) } - pub fn sign(message: &[u8], sk: &IrohaPrivateKey) -> Result, Error> { - let sk = Self::parse_private_key(sk)?; + pub fn sign(message: &[u8], sk: &PrivateKey) -> Vec { + Signature::::new(message, None, sk).to_bytes() + } - Ok(Signature::::new(message, None, &sk).to_bytes()) + pub fn verify(message: &[u8], signature: &[u8], pk: &PublicKey) -> Result<(), Error> { + let signature = Signature::::from_bytes(signature) + .map_err(|_| Error::Parse("Failed to parse signature.".to_string()))?; + + if !signature.verify(message, None, pk, &C::Generator::generator()) { + return Err(Error::BadSignature); + } + + Ok(()) } - pub fn verify(message: &[u8], signature: &[u8], pk: &IrohaPublicKey) -> Result { - let pk = Self::parse_public_key(pk)?; + pub fn parse_public_key(payload: &[u8]) -> Result, Error> { + PublicKey::from_bytes(payload).map_err(|err| Error::Parse(err.to_string())) + } - Ok(Signature::::from_bytes(signature) - .map_err(|_| Error::Parse("Failed to parse signature.".to_string()))? - .verify(message, None, &pk, &C::Generator::generator())) + pub fn parse_private_key(payload: &[u8]) -> Result { + PrivateKey::from_bytes(payload).map_err(|err| Error::Parse(err.to_string())) } } diff --git a/crypto/src/signature/bls/mod.rs b/crypto/src/signature/bls/mod.rs index c3dc918abfb..a2cd1ed8ea3 100644 --- a/crypto/src/signature/bls/mod.rs +++ b/crypto/src/signature/bls/mod.rs @@ -1,3 +1,7 @@ +pub use implementation::PrivateKey; +pub use normal::{NormalBls as BlsNormal, NormalPublicKey as BlsNormalPublicKey}; +pub use small::{SmallBls as BlsSmall, SmallPublicKey as BlsSmallPublicKey}; + // Do not expose the [implementation] module & the [implementation::BlsConfiguration] trait mod implementation; @@ -29,6 +33,7 @@ mod normal { #[derive(Debug, Clone, Copy)] pub struct NormalConfiguration; + impl BlsConfiguration for NormalConfiguration { const ALGORITHM: Algorithm = Algorithm::BlsNormal; const PK_SIZE: usize = GroupG1_SIZE; @@ -48,12 +53,19 @@ mod normal { fn set_pairs((g1, g2): &(Self::Generator, Self::SignatureGroup)) -> (&G1, &G2) { (g1, g2) } + + fn extract_private_key(private_key: &crate::PrivateKey) -> Option<&super::PrivateKey> { + if let crate::PrivateKey::BlsNormal(key) = private_key { + Some(key) + } else { + None + } + } } pub type NormalBls = implementation::BlsImpl; #[cfg(test)] pub type NormalSignature = implementation::Signature; - #[cfg(test)] pub type NormalPublicKey = implementation::PublicKey; } @@ -105,17 +117,21 @@ mod small { fn set_pairs((g2, g1): &(Self::Generator, Self::SignatureGroup)) -> (&G1, &G2) { (g1, g2) } + + fn extract_private_key(private_key: &crate::PrivateKey) -> Option<&super::PrivateKey> { + if let crate::PrivateKey::BlsSmall(key) = private_key { + Some(key) + } else { + None + } + } } pub type SmallBls = implementation::BlsImpl; #[cfg(test)] pub type SmallSignature = implementation::Signature; - #[cfg(test)] pub type SmallPublicKey = implementation::PublicKey; } -pub use normal::NormalBls as BlsNormal; -pub use small::SmallBls as BlsSmall; - #[cfg(test)] mod tests; diff --git a/crypto/src/signature/bls/tests.rs b/crypto/src/signature/bls/tests.rs index 243f4d27bca..3d187e0af9c 100644 --- a/crypto/src/signature/bls/tests.rs +++ b/crypto/src/signature/bls/tests.rs @@ -34,9 +34,13 @@ fn size_check() { assert_eq!(sig.to_bytes().len(), GroupG1_SIZE); } -fn signature_generation_from_seed() { - let keypair_1 = BlsImpl::::keypair(Some(KeyGenOption::UseSeed(SEED.to_vec()))).unwrap(); - let keypair_2 = BlsImpl::::keypair(Some(KeyGenOption::UseSeed(SEED.to_vec()))).unwrap(); +fn signature_generation_from_seed() +where + C: BlsConfiguration, + C::Generator: core::fmt::Debug, +{ + let keypair_1 = BlsImpl::::keypair(Some(KeyGenOption::UseSeed(SEED.to_vec()))); + let keypair_2 = BlsImpl::::keypair(Some(KeyGenOption::UseSeed(SEED.to_vec()))); assert_eq!(keypair_1, keypair_2); } diff --git a/crypto/src/signature/ed25519.rs b/crypto/src/signature/ed25519.rs index 0312bff8c12..d16c2a39a9c 100644 --- a/crypto/src/signature/ed25519.rs +++ b/crypto/src/signature/ed25519.rs @@ -1,70 +1,64 @@ use std::convert::TryFrom; use arrayref::array_ref; -use ed25519_dalek::{Signature, SigningKey, VerifyingKey as PK}; -use iroha_primitives::const_vec::ConstVec; +use ed25519_dalek::Signature; use rand::{rngs::OsRng, SeedableRng}; use rand_chacha::ChaChaRng; use sha2::Digest; use signature::{Signer as _, Verifier as _}; use zeroize::Zeroize; -const ALGORITHM: Algorithm = Algorithm::Ed25519; +use crate::{Error, KeyGenOption}; -use crate::{Algorithm, Error, KeyGenOption, PrivateKey, PublicKey}; - -fn parse_private_key(sk: &PrivateKey) -> Result { - assert_eq!(sk.digest_function, ALGORITHM); - SigningKey::from_keypair_bytes( - &<[u8; 64]>::try_from(&sk.payload[..]).map_err(|e| Error::Parse(e.to_string()))?, - ) - .map_err(|e| Error::Parse(e.to_string())) -} - -fn parse_public_key(pk: &PublicKey) -> Result { - assert_eq!(pk.digest_function, ALGORITHM); - PK::try_from(&pk.payload[..]).map_err(|e| Error::Parse(e.to_string())) -} +pub type PublicKey = ed25519_dalek::VerifyingKey; +pub type PrivateKey = ed25519_dalek::SigningKey; #[derive(Debug, Clone, Copy)] pub struct Ed25519Sha512; impl Ed25519Sha512 { - pub fn keypair(mut option: Option) -> Result<(PublicKey, PrivateKey), Error> { - let kp = match option { - Some(KeyGenOption::UseSeed(ref mut s)) => { - let hash = sha2::Sha256::digest(s.as_slice()); - s.zeroize(); - let mut rng = ChaChaRng::from_seed(*array_ref!(hash.as_slice(), 0, 32)); - SigningKey::generate(&mut rng) - } - Some(KeyGenOption::FromPrivateKey(ref s)) => parse_private_key(s)?, - None => { - let mut rng = OsRng; - SigningKey::generate(&mut rng) - } - }; - Ok(( - PublicKey { - digest_function: ALGORITHM, - payload: ConstVec::new(kp.verifying_key().to_bytes().to_vec()), - }, - PrivateKey { - digest_function: ALGORITHM, - payload: ConstVec::new(kp.to_keypair_bytes().to_vec()), + pub fn keypair(option: Option) -> (PublicKey, PrivateKey) { + let signing_key = option.map_or_else( + || PrivateKey::generate(&mut OsRng), + |mut o| match o { + KeyGenOption::UseSeed(ref mut s) => { + let hash = sha2::Sha256::digest(s.as_slice()); + s.zeroize(); + let mut rng = ChaChaRng::from_seed(*array_ref!(hash.as_slice(), 0, 32)); + PrivateKey::generate(&mut rng) + } + KeyGenOption::FromPrivateKey(ref s) => { + let crate::PrivateKey::Ed25519(s) = s else { + panic!("Wrong private key type, expected `Ed25519`, got {s:?}") + }; + PrivateKey::clone(s) + } }, - )) + ); + (signing_key.verifying_key(), signing_key) } - pub fn sign(message: &[u8], sk: &PrivateKey) -> Result, Error> { - let kp = parse_private_key(sk)?; - Ok(kp.sign(message).to_bytes().to_vec()) + + pub fn parse_public_key(payload: &[u8]) -> Result { + PublicKey::from_bytes(arrayref::array_ref!(payload, 0, 32)) + .map_err(|err| Error::Parse(err.to_string())) + } + + pub fn parse_private_key(payload: &[u8]) -> Result { + <[u8; 64]>::try_from(payload) + .map_err(|err| err.to_string()) + .and_then(|payload| { + PrivateKey::from_keypair_bytes(&payload).map_err(|err| err.to_string()) + }) + .map_err(Error::Parse) } - pub fn verify(message: &[u8], signature: &[u8], pk: &PublicKey) -> Result { - let p = parse_public_key(pk)?; + + pub fn sign(message: &[u8], sk: &PrivateKey) -> Vec { + sk.sign(message).to_bytes().to_vec() + } + + pub fn verify(message: &[u8], signature: &[u8], pk: &PublicKey) -> Result<(), Error> { let s = Signature::try_from(signature).map_err(|e| Error::Parse(e.to_string()))?; - p.verify(message, &s) - .map_err(|e| Error::Signing(e.to_string()))?; - Ok(true) + pk.verify(message, &s).map_err(|_| Error::BadSignature) } } @@ -76,7 +70,7 @@ mod test { use self::Ed25519Sha512; use super::*; - use crate::{KeyGenOption, PrivateKey, PublicKey}; + use crate::{Algorithm, KeyGenOption, PrivateKey, PublicKey}; const MESSAGE_1: &[u8] = b"This is a dummy message for use with tests"; const SIGNATURE_1: &str = "451b5b8e8725321541954997781de51f4142e4a56bab68d24f6a6b92615de5eefb74134138315859a32c7cf5fe5a488bc545e2e08e5eedfd1fb10188d532d808"; @@ -86,7 +80,7 @@ mod test { #[test] #[ignore] fn create_new_keys() { - let (p, s) = Ed25519Sha512::keypair(None).unwrap(); + let (p, s) = Ed25519Sha512::keypair(None); println!("{s:?}"); println!("{p:?}"); @@ -95,16 +89,14 @@ mod test { #[test] fn ed25519_load_keys() { let secret = PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap(); - let sres = Ed25519Sha512::keypair(Some(KeyGenOption::FromPrivateKey(secret))); - assert!(sres.is_ok()); - let (p1, s1) = sres.unwrap(); + let (p1, s1) = Ed25519Sha512::keypair(Some(KeyGenOption::FromPrivateKey(secret))); assert_eq!( - s1, + PrivateKey::Ed25519(Box::new(s1)), PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap() ); assert_eq!( - p1, + PublicKey::Ed25519(p1), PublicKey::from_hex(Algorithm::Ed25519, PUBLIC_KEY).unwrap() ); } @@ -112,21 +104,19 @@ mod test { #[test] fn ed25519_verify() { let secret = PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap(); - let (p, _) = Ed25519Sha512::keypair(Some(KeyGenOption::FromPrivateKey(secret))).unwrap(); + let (p, _) = Ed25519Sha512::keypair(Some(KeyGenOption::FromPrivateKey(secret))); - let result = - Ed25519Sha512::verify(MESSAGE_1, hex::decode(SIGNATURE_1).unwrap().as_slice(), &p); - assert!(result.is_ok()); - assert!(result.unwrap()); + Ed25519Sha512::verify(MESSAGE_1, hex::decode(SIGNATURE_1).unwrap().as_slice(), &p).unwrap(); - //Check if signatures produced here can be verified by libsodium + // Check if signatures produced here can be verified by libsodium let signature = hex::decode(SIGNATURE_1).unwrap(); + let p_bytes = p.to_bytes(); let res = unsafe { ffi::crypto_sign_ed25519_verify_detached( signature.as_slice().as_ptr(), MESSAGE_1.as_ptr(), MESSAGE_1.len() as u64, - p.payload().as_ptr(), + p_bytes.as_ptr(), ) }; assert_eq!(res, 0); @@ -135,12 +125,10 @@ mod test { #[test] fn ed25519_sign() { let secret = PrivateKey::from_hex(Algorithm::Ed25519, PRIVATE_KEY).unwrap(); - let (p, s) = Ed25519Sha512::keypair(Some(KeyGenOption::FromPrivateKey(secret))).unwrap(); + let (p, s) = Ed25519Sha512::keypair(Some(KeyGenOption::FromPrivateKey(secret))); - let sig = Ed25519Sha512::sign(MESSAGE_1, &s).unwrap(); - let result = Ed25519Sha512::verify(MESSAGE_1, &sig, &p); - assert!(result.is_ok()); - assert!(result.unwrap()); + let sig = Ed25519Sha512::sign(MESSAGE_1, &s); + Ed25519Sha512::verify(MESSAGE_1, &sig, &p).unwrap(); assert_eq!(sig.len(), ed25519_dalek::SIGNATURE_LENGTH); assert_eq!(hex::encode(sig.as_slice()), SIGNATURE_1); @@ -148,17 +136,16 @@ mod test { //Check if libsodium signs the message and this module still can verify it //And that private keys can sign with other libraries let mut signature = [0u8; ffi::crypto_sign_ed25519_BYTES as usize]; + let s_bytes = s.to_keypair_bytes(); unsafe { ffi::crypto_sign_ed25519_detached( signature.as_mut_ptr(), std::ptr::null_mut(), MESSAGE_1.as_ptr(), MESSAGE_1.len() as u64, - s.payload().as_ptr(), + s_bytes.as_ptr(), ) }; - let result = Ed25519Sha512::verify(MESSAGE_1, &signature, &p); - assert!(result.is_ok()); - assert!(result.unwrap()); + Ed25519Sha512::verify(MESSAGE_1, &signature, &p).unwrap(); } } diff --git a/crypto/src/signature/mod.rs b/crypto/src/signature/mod.rs index 387a939be29..44dde9f5067 100644 --- a/crypto/src/signature/mod.rs +++ b/crypto/src/signature/mod.rs @@ -64,23 +64,19 @@ impl Signature { /// # Errors /// Fails if signing fails #[cfg(any(feature = "std", feature = "import_ffi"))] - pub fn new(key_pair: KeyPair, payload: &[u8]) -> Result { + pub fn new(key_pair: KeyPair, payload: &[u8]) -> Self { let (public_key, private_key) = key_pair.into(); - let algorithm: crate::Algorithm = private_key.digest_function(); - - let signature = match algorithm { - crate::Algorithm::Ed25519 => ed25519::Ed25519Sha512::sign(payload, &private_key), - crate::Algorithm::Secp256k1 => { - secp256k1::EcdsaSecp256k1Sha256::sign(payload, &private_key) - } - crate::Algorithm::BlsSmall => bls::BlsSmall::sign(payload, &private_key), - crate::Algorithm::BlsNormal => bls::BlsNormal::sign(payload, &private_key), - }?; - Ok(Self { + let signature = match private_key { + crate::PrivateKey::Ed25519(sk) => ed25519::Ed25519Sha512::sign(payload, &sk), + crate::PrivateKey::Secp256k1(sk) => secp256k1::EcdsaSecp256k1Sha256::sign(payload, &sk), + crate::PrivateKey::BlsSmall(sk) => bls::BlsSmall::sign(payload, &sk), + crate::PrivateKey::BlsNormal(sk) => bls::BlsNormal::sign(payload, &sk), + }; + Self { public_key, payload: ConstVec::new(signature), - }) + } } /// Verify `message` using signed data and [`KeyPair::public_key`]. @@ -89,21 +85,15 @@ impl Signature { /// Fails if message didn't pass verification #[cfg(any(feature = "std", feature = "import_ffi"))] pub fn verify(&self, payload: &[u8]) -> Result<(), Error> { - let algorithm: crate::Algorithm = self.public_key.digest_function(); - - match algorithm { - crate::Algorithm::Ed25519 => { - ed25519::Ed25519Sha512::verify(payload, self.payload(), &self.public_key) - } - crate::Algorithm::Secp256k1 => { - secp256k1::EcdsaSecp256k1Sha256::verify(payload, self.payload(), &self.public_key) - } - crate::Algorithm::BlsSmall => { - bls::BlsSmall::verify(payload, self.payload(), &self.public_key) + match &self.public_key { + crate::PublicKey::Ed25519(pk) => { + ed25519::Ed25519Sha512::verify(payload, self.payload(), pk) } - crate::Algorithm::BlsNormal => { - bls::BlsNormal::verify(payload, self.payload(), &self.public_key) + crate::PublicKey::Secp256k1(pk) => { + secp256k1::EcdsaSecp256k1Sha256::verify(payload, self.payload(), pk) } + crate::PublicKey::BlsSmall(pk) => bls::BlsSmall::verify(payload, self.payload(), pk), + crate::PublicKey::BlsNormal(pk) => bls::BlsNormal::verify(payload, self.payload(), pk), }?; Ok(()) @@ -215,8 +205,8 @@ impl SignatureOf { /// # Errors /// Fails if signing fails #[cfg(any(feature = "std", feature = "import_ffi"))] - fn from_hash(key_pair: KeyPair, hash: HashOf) -> Result { - Signature::new(key_pair, hash.as_ref()).map(|signature| Self(signature, PhantomData)) + fn from_hash(key_pair: KeyPair, hash: HashOf) -> Self { + Self(Signature::new(key_pair, hash.as_ref()), PhantomData) } /// Verify signature for this hash @@ -238,7 +228,7 @@ impl SignatureOf { /// /// # Errors /// Fails if signing fails - pub fn new(key_pair: KeyPair, value: &T) -> Result { + pub fn new(key_pair: KeyPair, value: &T) -> Self { Self::from_hash(key_pair, HashOf::new(value)) } @@ -490,8 +480,8 @@ impl SignaturesOf { /// /// # Errors /// Forwards [`SignatureOf::new`] errors - pub fn new(key_pair: KeyPair, value: &T) -> Result { - SignatureOf::new(key_pair, value).map(Self::from) + pub fn new(key_pair: KeyPair, value: &T) -> Self { + SignatureOf::new(key_pair, value).into() } /// Verifies all signatures @@ -553,10 +543,9 @@ mod tests { ) .expect("Failed to generate key pair."); let message = b"Test message to sign."; - let signature = - Signature::new(key_pair.clone(), message).expect("Failed to create signature."); + let signature = Signature::new(key_pair.clone(), message); assert!(*signature.public_key() == *key_pair.public_key()); - assert!(signature.verify(message).is_ok()); + signature.verify(message).unwrap(); } #[test] @@ -567,10 +556,9 @@ mod tests { ) .expect("Failed to generate key pair."); let message = b"Test message to sign."; - let signature = - Signature::new(key_pair.clone(), message).expect("Failed to create signature."); + let signature = Signature::new(key_pair.clone(), message); assert!(*signature.public_key() == *key_pair.public_key()); - assert!(signature.verify(message).is_ok()); + signature.verify(message).unwrap(); } #[test] @@ -581,10 +569,9 @@ mod tests { ) .expect("Failed to generate key pair."); let message = b"Test message to sign."; - let signature = - Signature::new(key_pair.clone(), message).expect("Failed to create signature."); + let signature = Signature::new(key_pair.clone(), message); assert!(*signature.public_key() == *key_pair.public_key()); - assert!(signature.verify(message).is_ok()); + signature.verify(message).unwrap(); } #[test] @@ -595,10 +582,9 @@ mod tests { ) .expect("Failed to generate key pair."); let message = b"Test message to sign."; - let signature = - Signature::new(key_pair.clone(), message).expect("Failed to create signature."); + let signature = Signature::new(key_pair.clone(), message); assert!(*signature.public_key() == *key_pair.public_key()); - assert!(signature.verify(message).is_ok()); + signature.verify(message).unwrap(); } #[test] @@ -607,9 +593,9 @@ mod tests { fn signatures_of_deduplication_by_public_key() { let key_pair = KeyPair::generate().expect("Failed to generate keys"); let signatures = [ - SignatureOf::new(key_pair.clone(), &1).expect("Failed to sign"), - SignatureOf::new(key_pair.clone(), &2).expect("Failed to sign"), - SignatureOf::new(key_pair, &3).expect("Failed to sign"), + SignatureOf::new(key_pair.clone(), &1), + SignatureOf::new(key_pair.clone(), &2), + SignatureOf::new(key_pair, &3), ] .into_iter() .collect::>(); @@ -631,7 +617,7 @@ mod tests { .flat_map(|key| { core::iter::repeat_with(move || key.clone()) .zip(0..) - .map(|(key, i)| SignatureOf::new(key, &i).expect("Failed to sign")) + .map(|(key, i)| SignatureOf::new(key, &i)) .take(signatures_per_key) }) .map(SignatureWrapperOf) diff --git a/crypto/src/signature/secp256k1.rs b/crypto/src/signature/secp256k1.rs index 1939213af37..a7bc32fbaa2 100644 --- a/crypto/src/signature/secp256k1.rs +++ b/crypto/src/signature/secp256k1.rs @@ -1,52 +1,54 @@ use self::ecdsa_secp256k1::EcdsaSecp256k1Impl; -use crate::{Algorithm, Error, KeyGenOption, PrivateKey, PublicKey}; +use crate::{Error, KeyGenOption}; pub const PRIVATE_KEY_SIZE: usize = 32; -pub const PUBLIC_KEY_SIZE: usize = 33; - -const ALGORITHM: Algorithm = Algorithm::Secp256k1; pub struct EcdsaSecp256k1Sha256; +pub type PublicKey = k256::PublicKey; +pub type PrivateKey = k256::SecretKey; + impl EcdsaSecp256k1Sha256 { - pub fn keypair(option: Option) -> Result<(PublicKey, PrivateKey), Error> { + pub fn keypair(option: Option) -> (PublicKey, PrivateKey) { EcdsaSecp256k1Impl::keypair(option) } - pub fn sign(message: &[u8], sk: &PrivateKey) -> Result, Error> { + + pub fn sign(message: &[u8], sk: &PrivateKey) -> Vec { EcdsaSecp256k1Impl::sign(message, sk) } - pub fn verify(message: &[u8], signature: &[u8], pk: &PublicKey) -> Result { + + pub fn verify(message: &[u8], signature: &[u8], pk: &PublicKey) -> Result<(), Error> { EcdsaSecp256k1Impl::verify(message, signature, pk) } + + pub fn parse_public_key(payload: &[u8]) -> Result { + EcdsaSecp256k1Impl::parse_public_key(payload) + } + + pub fn parse_private_key(payload: &[u8]) -> Result { + EcdsaSecp256k1Impl::parse_private_key(payload) + } } mod ecdsa_secp256k1 { - use amcl::secp256k1::ecp; use arrayref::array_ref; use digest::Digest as _; - use iroha_primitives::const_vec::ConstVec; use rand::{rngs::OsRng, RngCore, SeedableRng}; use rand_chacha::ChaChaRng; use signature::{Signer as _, Verifier as _}; use zeroize::Zeroize; - use super::{ALGORITHM, PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE}; - use crate::{Error, KeyGenOption, PrivateKey, PublicKey}; + use super::{PrivateKey, PublicKey, PRIVATE_KEY_SIZE}; + use crate::{Error, KeyGenOption}; pub struct EcdsaSecp256k1Impl; type Digest = sha2::Sha256; impl EcdsaSecp256k1Impl { - pub fn public_key_compressed(pk: &PublicKey) -> Vec { - assert_eq!(pk.digest_function, ALGORITHM); - let mut compressed = [0u8; PUBLIC_KEY_SIZE]; - ecp::ECP::frombytes(&pk.payload[..]).tobytes(&mut compressed, true); - compressed.to_vec() - } - - pub fn keypair(option: Option) -> Result<(PublicKey, PrivateKey), Error> { - let signing_key = match option { - Some(mut o) => match o { + pub fn keypair(option: Option) -> (PublicKey, PrivateKey) { + let signing_key = option.map_or_else( + || PrivateKey::random(&mut OsRng), + |mut o| match o { KeyGenOption::UseSeed(ref mut seed) => { let mut s = [0u8; PRIVATE_KEY_SIZE]; let mut rng = ChaChaRng::from_seed(*array_ref!(seed.as_slice(), 0, 32)); @@ -54,50 +56,46 @@ mod ecdsa_secp256k1 { rng.fill_bytes(&mut s); let k = Digest::digest(s); s.zeroize(); - k256::SecretKey::from_slice(k.as_slice())? + PrivateKey::from_slice(k.as_slice()) + .expect("Creating private key from seed should always succeed") } KeyGenOption::FromPrivateKey(ref s) => { - assert_eq!(s.digest_function, ALGORITHM); - k256::SecretKey::from_slice(&s.payload[..])? + let crate::PrivateKey::Secp256k1(s) = s else { + panic!("Wrong private key type, expected `Secp256k1`, got {s:?}") + }; + s.clone() } }, - None => k256::SecretKey::random(&mut OsRng), - }; + ); let public_key = signing_key.public_key(); - let compressed = public_key.to_sec1_bytes(); //serialized as compressed point - Ok(( - PublicKey { - digest_function: ALGORITHM, - payload: ConstVec::new(compressed), - }, - PrivateKey { - digest_function: ALGORITHM, - payload: ConstVec::new(signing_key.to_bytes().to_vec()), - }, - )) + (public_key, signing_key) } - pub fn sign(message: &[u8], sk: &PrivateKey) -> Result, Error> { - assert_eq!(sk.digest_function, ALGORITHM); - let signing_key = k256::SecretKey::from_slice(&sk.payload[..]) - .map_err(|e| Error::Signing(format!("{e:?}")))?; - let signing_key = k256::ecdsa::SigningKey::from(signing_key); + pub fn sign(message: &[u8], sk: &PrivateKey) -> Vec { + let signing_key = k256::ecdsa::SigningKey::from(sk); let signature: k256::ecdsa::Signature = signing_key.sign(message); - Ok(signature.to_bytes().to_vec()) + signature.to_bytes().to_vec() } - pub fn verify(message: &[u8], signature: &[u8], pk: &PublicKey) -> Result { - let compressed_pk = Self::public_key_compressed(pk); - let verifying_key = k256::PublicKey::from_sec1_bytes(&compressed_pk) - .map_err(|e| Error::Signing(format!("{e:?}")))?; + pub fn verify(message: &[u8], signature: &[u8], pk: &PublicKey) -> Result<(), Error> { let signature = k256::ecdsa::Signature::from_slice(signature) .map_err(|e| Error::Signing(format!("{e:?}")))?; - let verifying_key = k256::ecdsa::VerifyingKey::from(verifying_key); + let verifying_key = k256::ecdsa::VerifyingKey::from(pk); - Ok(verifying_key.verify(message, &signature).is_ok()) + verifying_key + .verify(message, &signature) + .map_err(|_| Error::BadSignature) + } + + pub fn parse_public_key(payload: &[u8]) -> Result { + PublicKey::from_sec1_bytes(payload).map_err(|err| Error::Parse(err.to_string())) + } + + pub fn parse_private_key(payload: &[u8]) -> Result { + PrivateKey::from_slice(payload).map_err(|err| Error::Parse(err.to_string())) } } } @@ -127,39 +125,33 @@ mod test { const PRIVATE_KEY: &str = "e4f21b38e005d4f895a29e84948d7cc83eac79041aeb644ee4fab8d9da42f713"; const PUBLIC_KEY: &str = "0242c1e1f775237a26da4fd51b8d75ee2709711f6e90303e511169a324ef0789c0"; + fn private_key() -> PrivateKey { + let payload = hex::decode(PRIVATE_KEY).unwrap(); + EcdsaSecp256k1Sha256::parse_private_key(&payload).unwrap() + } + + fn public_key() -> PublicKey { + let payload = hex::decode(PUBLIC_KEY).unwrap(); + EcdsaSecp256k1Sha256::parse_public_key(&payload).unwrap() + } + fn public_key_uncompressed(pk: &PublicKey) -> Vec { const PUBLIC_UNCOMPRESSED_KEY_SIZE: usize = 65; - assert_eq!(pk.digest_function, ALGORITHM); let mut uncompressed = [0u8; PUBLIC_UNCOMPRESSED_KEY_SIZE]; - ecp::ECP::frombytes(&pk.payload[..]).tobytes(&mut uncompressed, false); + ecp::ECP::frombytes(&pk.to_sec1_bytes()[..]).tobytes(&mut uncompressed, false); uncompressed.to_vec() } - #[test] - #[ignore] - fn create_new_keys() { - let (s, p) = EcdsaSecp256k1Sha256::keypair(None).unwrap(); - - println!("{s:?}"); - println!("{p:?}"); - } - - #[test] - fn secp256k1_load_keys() { - let secret = PrivateKey::from_hex(ALGORITHM, PRIVATE_KEY).unwrap(); - let _sres = - EcdsaSecp256k1Sha256::keypair(Some(KeyGenOption::FromPrivateKey(secret))).unwrap(); - } - #[test] fn secp256k1_compatibility() { - let secret = PrivateKey::from_hex(ALGORITHM, PRIVATE_KEY).unwrap(); - let (p, s) = - EcdsaSecp256k1Sha256::keypair(Some(KeyGenOption::FromPrivateKey(secret))).unwrap(); + let secret = private_key(); + let (p, s) = EcdsaSecp256k1Sha256::keypair(Some(KeyGenOption::FromPrivateKey( + crate::PrivateKey::Secp256k1(secret), + ))); - let _sk = secp256k1::SecretKey::from_slice(s.payload()).unwrap(); - let _pk = secp256k1::PublicKey::from_slice(p.payload()).unwrap(); + let _sk = secp256k1::SecretKey::from_slice(&s.to_bytes()).unwrap(); + let _pk = secp256k1::PublicKey::from_slice(&p.to_sec1_bytes()).unwrap(); let openssl_group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); let mut ctx = BigNumContext::new().unwrap(); @@ -170,16 +162,10 @@ mod test { #[test] fn secp256k1_verify() { - let p = PublicKey::from_hex(ALGORITHM, PUBLIC_KEY).unwrap(); + let p = public_key(); - let result = EcdsaSecp256k1Sha256::verify( - MESSAGE_1, - hex::decode(SIGNATURE_1).unwrap().as_slice(), - &p, - ); - // we are returning a `Result` - // unwrap will catch the `Err(_)`, and assert will catch the `false` - assert!(result.unwrap()); + EcdsaSecp256k1Sha256::verify(MESSAGE_1, hex::decode(SIGNATURE_1).unwrap().as_slice(), &p) + .unwrap(); let context = secp256k1::Secp256k1::new(); let pk = @@ -211,15 +197,13 @@ mod test { #[test] fn secp256k1_sign() { - let secret = PrivateKey::from_hex(ALGORITHM, PRIVATE_KEY).unwrap(); - let (pk, sk) = - EcdsaSecp256k1Sha256::keypair(Some(KeyGenOption::FromPrivateKey(secret))).unwrap(); + let secret = private_key(); + let (pk, sk) = EcdsaSecp256k1Sha256::keypair(Some(KeyGenOption::FromPrivateKey( + crate::PrivateKey::Secp256k1(secret), + ))); - let sig = EcdsaSecp256k1Sha256::sign(MESSAGE_1, &sk).unwrap(); - let result = EcdsaSecp256k1Sha256::verify(MESSAGE_1, &sig, &pk); - // we are returning a `Result` - // unwrap will catch the `Err(_)`, and assert will catch the `false` - assert!(result.unwrap()); + let sig = EcdsaSecp256k1Sha256::sign(MESSAGE_1, &sk); + EcdsaSecp256k1Sha256::verify(MESSAGE_1, &sig, &pk).unwrap(); assert_eq!(sig.len(), 64); @@ -234,8 +218,7 @@ mod test { let msg = secp256k1::Message::from_digest_slice(h.as_slice()).unwrap(); let sig_1 = context.sign_ecdsa(&msg, &sk).serialize_compact(); - let result = EcdsaSecp256k1Sha256::verify(MESSAGE_1, &sig_1, &pk); - assert!(result.unwrap()); + EcdsaSecp256k1Sha256::verify(MESSAGE_1, &sig_1, &pk).unwrap(); let openssl_group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); let mut ctx = BigNumContext::new().unwrap(); @@ -280,12 +263,10 @@ mod test { res }; - let result = EcdsaSecp256k1Sha256::verify(MESSAGE_1, openssl_sig.as_slice(), &pk); - assert!(result.unwrap()); + EcdsaSecp256k1Sha256::verify(MESSAGE_1, openssl_sig.as_slice(), &pk).unwrap(); - let (p, s) = EcdsaSecp256k1Sha256::keypair(None).unwrap(); - let signed = EcdsaSecp256k1Sha256::sign(MESSAGE_1, &s).unwrap(); - let result = EcdsaSecp256k1Sha256::verify(MESSAGE_1, &signed, &p); - assert!(result.unwrap()); + let (p, s) = EcdsaSecp256k1Sha256::keypair(None); + let signed = EcdsaSecp256k1Sha256::sign(MESSAGE_1, &s); + EcdsaSecp256k1Sha256::verify(MESSAGE_1, &signed, &p).unwrap(); } } diff --git a/data_model/src/block.rs b/data_model/src/block.rs index 0eb2cb5079c..fc184015382 100644 --- a/data_model/src/block.rs +++ b/data_model/src/block.rs @@ -200,12 +200,12 @@ impl SignedBlock { /// If given signature doesn't match block hash #[cfg(feature = "std")] #[cfg(feature = "transparent_api")] - pub fn sign(mut self, key_pair: KeyPair) -> Result { - iroha_crypto::SignatureOf::new(key_pair, self.payload()).map(|signature| { - let SignedBlock::V1(block) = &mut self; - block.signatures.insert(signature); - self - }) + #[must_use] + pub fn sign(mut self, key_pair: KeyPair) -> Self { + let signature = iroha_crypto::SignatureOf::new(key_pair, self.payload()); + let SignedBlock::V1(block) = &mut self; + block.signatures.insert(signature); + self } /// Add additional signatures to this block diff --git a/data_model/src/peer.rs b/data_model/src/peer.rs index 866e753a456..1c017f2834f 100644 --- a/data_model/src/peer.rs +++ b/data_model/src/peer.rs @@ -9,7 +9,6 @@ use core::{ }; use derive_more::Display; -use getset::Getters; use iroha_data_model_derive::{model, IdEqOrdHash}; use iroha_primitives::addr::SocketAddr; use iroha_schema::IntoSchema; @@ -27,19 +26,15 @@ pub mod model { /// /// Equality is tested by `public_key` field only. /// Each peer should have a unique public key. - #[derive( - Debug, Display, Clone, Eq, Getters, Decode, Encode, Deserialize, Serialize, IntoSchema, - )] + #[derive(Debug, Display, Clone, Eq, Decode, Encode, Deserialize, Serialize, IntoSchema)] #[display(fmt = "{public_key}@@{address}")] - #[getset(get = "pub")] #[ffi_type] pub struct PeerId { /// Address of the [`Peer`]'s entrypoint. // TODO: Derive with getset once FFI impl is fixed - #[getset(skip)] pub address: SocketAddr, /// Public Key of the [`Peer`]. - pub public_key: PublicKey, + pub public_key: Box, } /// Representation of other Iroha Peer instances running in separate processes. @@ -58,14 +53,21 @@ pub mod model { } impl PeerId { - /// Construct `PeerId` given `public_key` and `address`. + /// Construct [`PeerId`] given `public_key` and `address`. #[inline] - pub fn new(address: &SocketAddr, public_key: &PublicKey) -> Self { + pub fn new(address: SocketAddr, public_key: PublicKey) -> Self { Self { - address: address.clone(), - public_key: public_key.clone(), + address, + public_key: Box::new(public_key), } } + + /// Get public key of the peer. + #[inline] + pub fn public_key(&self) -> &PublicKey { + &self.public_key + } + /// Serialize the data contained in this Id for use in hashing. pub fn payload(&self) -> Vec { let mut data = Vec::new(); diff --git a/data_model/src/predicate.rs b/data_model/src/predicate.rs index 4e5d72d8c0c..87e641da98d 100644 --- a/data_model/src/predicate.rs +++ b/data_model/src/predicate.rs @@ -366,6 +366,8 @@ pub mod string { #[cfg(test)] mod tests { + use iroha_primitives::addr::socket_addr; + use super::*; mod id_box { @@ -496,10 +498,7 @@ pub mod string { let (public_key, _) = iroha_crypto::KeyPair::generate() .expect("Should not panic") .into(); - let id = IdBox::PeerId(peer::PeerId { - address: "localhost:123".parse().unwrap(), - public_key, - }); + let id = IdBox::PeerId(peer::PeerId::new(socket_addr!(127.0.0.1:123), public_key)); assert!(StringPredicate::contains("123").applies(&id)); } } @@ -1155,6 +1154,7 @@ pub mod value { #[cfg(test)] mod test { + use iroha_primitives::addr::socket_addr; use peer::Peer; use prelude::Metadata; @@ -1197,10 +1197,7 @@ pub mod value { assert!( !pred.applies(&Value::Identifiable(IdentifiableBox::Peer(Peer { - id: peer::PeerId { - address: "localhost:123".parse().unwrap(), - public_key - } + id: peer::PeerId::new(socket_addr!(127.0.0.1:123), public_key) }))) ); } diff --git a/data_model/src/query/mod.rs b/data_model/src/query/mod.rs index 6d96d52634f..915ca0dd026 100644 --- a/data_model/src/query/mod.rs +++ b/data_model/src/query/mod.rs @@ -1231,16 +1231,12 @@ pub mod http { /// # Errors /// Fails if signature creation fails. #[inline] - pub fn sign( - self, - key_pair: iroha_crypto::KeyPair, - ) -> Result { - SignatureOf::new(key_pair, &self.payload) - .map(|signature| SignedQueryV1 { - payload: self.payload, - signature, - }) - .map(Into::into) + pub fn sign(self, key_pair: iroha_crypto::KeyPair) -> SignedQuery { + SignedQueryV1 { + signature: SignatureOf::new(key_pair, &self.payload), + payload: self.payload, + } + .into() } } @@ -1352,7 +1348,7 @@ pub mod error { /// Parameter with id `{0}` not found Parameter(ParameterId), /// Failed to find public key: `{0}` - PublicKey(PublicKey), + PublicKey(Box), } } } diff --git a/data_model/src/transaction.rs b/data_model/src/transaction.rs index 9d288239636..83c66f96a1a 100644 --- a/data_model/src/transaction.rs +++ b/data_model/src/transaction.rs @@ -297,19 +297,17 @@ impl SignedTransaction { /// /// Fails if signature creation fails #[cfg(feature = "std")] - pub fn sign( - self, - key_pair: iroha_crypto::KeyPair, - ) -> Result { + #[must_use] + pub fn sign(self, key_pair: iroha_crypto::KeyPair) -> SignedTransaction { let SignedTransaction::V1(mut tx) = self; - let signature = iroha_crypto::SignatureOf::new(key_pair, &tx.payload)?; + let signature = iroha_crypto::SignatureOf::new(key_pair, &tx.payload); tx.signatures.insert(signature); - Ok(SignedTransactionV1 { + SignedTransactionV1 { payload: tx.payload, signatures: tx.signatures, } - .into()) + .into() } /// Add additional signatures to this transaction @@ -750,17 +748,14 @@ mod http { /// /// Fails if signature creation fails #[cfg(feature = "std")] - pub fn sign( - self, - key_pair: iroha_crypto::KeyPair, - ) -> Result { - let signatures = SignaturesOf::new(key_pair, &self.payload)?; + pub fn sign(self, key_pair: iroha_crypto::KeyPair) -> SignedTransaction { + let signatures = SignaturesOf::new(key_pair, &self.payload); - Ok(SignedTransactionV1 { + SignedTransactionV1 { payload: self.payload, signatures, } - .into()) + .into() } } } diff --git a/genesis/src/lib.rs b/genesis/src/lib.rs index d32ebb22405..32d63bbfdb3 100644 --- a/genesis/src/lib.rs +++ b/genesis/src/lib.rs @@ -58,17 +58,15 @@ impl GenesisNetwork { .chain(raw_block.transactions); let transactions = transactions_iter - .enumerate() - .map(|(i, raw_transaction)| { + .map(|raw_transaction| { raw_transaction // FIXME: fix underlying chain of `.sign` so that it doesn't // consume the key pair unnecessarily. It might be costly to clone // the key pair for a large genesis. .sign(genesis_key_pair.clone()) - .map(GenesisTransaction) - .wrap_err_with(|| eyre!("Failed to sign transaction at index {i}")) }) - .collect::>>()?; + .map(GenesisTransaction) + .collect(); Ok(GenesisNetwork { transactions }) } @@ -188,10 +186,7 @@ impl GenesisTransactionBuilder { /// /// # Errors /// Fails if signing or accepting fails. - pub fn sign( - self, - genesis_key_pair: KeyPair, - ) -> core::result::Result { + pub fn sign(self, genesis_key_pair: KeyPair) -> SignedTransaction { TransactionBuilder::new(GENESIS_ACCOUNT_ID.clone()) .with_instructions(self.isi) .sign(genesis_key_pair) diff --git a/p2p/src/network.rs b/p2p/src/network.rs index 51b97d661e2..db58a2644f8 100644 --- a/p2p/src/network.rs +++ b/p2p/src/network.rs @@ -302,7 +302,7 @@ impl NetworkBase { // Peer is not connected but should .filter_map(|(peer, is_active)| ( !self.peers.contains_key(&peer.public_key) - && !self.connecting_peers.values().any(|public_key| peer.public_key == *public_key) + && !self.connecting_peers.values().any(|public_key| peer.public_key() == public_key) && *is_active ).then_some(peer)) .cloned() @@ -320,7 +320,7 @@ impl NetworkBase { } for public_key in to_disconnect { - self.disconnect_peer(&public_key) + self.disconnect_peer(public_key) } } @@ -343,14 +343,14 @@ impl NetworkBase { ); } - fn disconnect_peer(&mut self, public_key: &PublicKey) { - let peer = match self.peers.remove(public_key) { + fn disconnect_peer(&mut self, public_key: PublicKey) { + let peer = match self.peers.remove(&public_key) { Some(peer) => peer, _ => return iroha_logger::warn!(?public_key, "Not found peer to disconnect"), }; iroha_logger::debug!(listen_addr = %self.listen_addr, %peer.conn_id, "Disconnecting peer"); - let peer_id = PeerId::new(&peer.p2p_addr, public_key); + let peer_id = PeerId::new(peer.p2p_addr, public_key); Self::remove_online_peer(&self.online_peers_sender, &peer_id); } @@ -393,7 +393,7 @@ impl NetworkBase { disambiguator, }; let _ = peer_message_sender.send(self.peer_message_sender.clone()); - self.peers.insert(peer_id.public_key.clone(), ref_peer); + self.peers.insert(peer_id.public_key().clone(), ref_peer); self.connecting_peers.remove(&connection_id); Self::add_online_peer(&self.online_peers_sender, peer_id); } @@ -421,7 +421,7 @@ impl NetworkBase { Self::remove_online_peer(&self.online_peers_sender, &peer_id); } } - None if &peer_id.public_key == self.key_pair.public_key() => { + None if peer_id.public_key() == self.key_pair.public_key() => { #[cfg(debug_assertions)] iroha_logger::trace!("Not sending message to myself") } @@ -438,7 +438,7 @@ impl NetworkBase { } = self; peers.retain(|public_key, ref_peer| { if ref_peer.handle.post(data.clone()).is_err() { - let peer_id = PeerId::new(&ref_peer.p2p_addr, public_key); + let peer_id = PeerId::new(ref_peer.p2p_addr.clone(), public_key.clone()); iroha_logger::error!(peer=%peer_id, "Failed to send message to peer"); Self::remove_online_peer(online_peers_sender, &peer_id); false diff --git a/p2p/src/peer.rs b/p2p/src/peer.rs index 182b72e9e7d..7047d3b0539 100644 --- a/p2p/src/peer.rs +++ b/p2p/src/peer.rs @@ -371,7 +371,7 @@ mod state { //! Module for peer stages. use iroha_crypto::{KeyPair, PublicKey, Signature}; - use iroha_primitives::{addr::SocketAddr, const_vec::ConstVec}; + use iroha_primitives::addr::SocketAddr; use super::{cryptographer::Cryptographer, *}; @@ -418,7 +418,7 @@ mod state { }: Self, ) -> Result, crate::Error> { let key_exchange = K::new(); - let (kx_local_pk, kx_local_sk) = key_exchange.keypair(None)?; + let (kx_local_pk, kx_local_sk) = key_exchange.keypair(None); let (algorithm, kx_local_pk_raw) = kx_local_pk.clone().into_raw(); let write_half = &mut connection.write; garbage::write(write_half).await?; @@ -430,9 +430,9 @@ mod state { // Then we have servers public key let mut key = vec![0_u8; 32]; let _ = read_half.read_exact(&mut key).await?; - PublicKey::from_raw(algorithm, ConstVec::new(key)) + PublicKey::from_raw(algorithm, &key)? }; - let shared_key = key_exchange.compute_shared_secret(&kx_local_sk, &kx_remote_pk); + let shared_key = key_exchange.compute_shared_secret(&kx_local_sk, &kx_remote_pk)?; let cryptographer = Cryptographer::new(&shared_key); Ok(SendKey { peer_addr, @@ -463,7 +463,7 @@ mod state { }: Self, ) -> Result, crate::Error> { let key_exchange = K::new(); - let (kx_local_pk, kx_local_sk) = key_exchange.keypair(None)?; + let (kx_local_pk, kx_local_sk) = key_exchange.keypair(None); let (algorithm, kx_local_pk_raw) = kx_local_pk.clone().into_raw(); let read_half = &mut connection.read; let kx_remote_pk = { @@ -471,12 +471,12 @@ mod state { // And then we have clients public key let mut key = vec![0_u8; 32]; let _ = read_half.read_exact(&mut key).await?; - PublicKey::from_raw(algorithm, ConstVec::new(key)) + PublicKey::from_raw(algorithm, &key)? }; let write_half = &mut connection.write; garbage::write(write_half).await?; write_half.write_all(&kx_local_pk_raw).await?; - let shared_key = key_exchange.compute_shared_secret(&kx_local_sk, &kx_remote_pk); + let shared_key = key_exchange.compute_shared_secret(&kx_local_sk, &kx_remote_pk)?; let cryptographer = Cryptographer::new(&shared_key); Ok(SendKey { peer_addr, @@ -513,7 +513,7 @@ mod state { let write_half = &mut connection.write; let payload = create_payload(&kx_local_pk, &kx_remote_pk); - let signature = Signature::new(key_pair, &payload)?; + let signature = Signature::new(key_pair, &payload); let data = signature.encode(); let data = &cryptographer.encrypt(data.as_slice())?; @@ -570,10 +570,7 @@ mod state { let (remote_pub_key, _) = signature.into(); - let peer_id = PeerId { - address: peer_addr, - public_key: remote_pub_key, - }; + let peer_id = PeerId::new(peer_addr, remote_pub_key); Ok(Ready { peer_id, diff --git a/p2p/tests/integration/p2p.rs b/p2p/tests/integration/p2p.rs index b23faff5036..0b45a35cc70 100644 --- a/p2p/tests/integration/p2p.rs +++ b/p2p/tests/integration/p2p.rs @@ -54,10 +54,7 @@ async fn network_create() { tokio::time::sleep(delay).await; info!("Connecting to peer..."); - let peer1 = PeerId { - address: address.clone(), - public_key: public_key.clone(), - }; + let peer1 = PeerId::new(address.clone(), public_key.clone()); let topology = HashSet::from([peer1.clone()]); network.update_topology(UpdateTopology(topology)); tokio::time::sleep(delay).await; @@ -174,14 +171,8 @@ async fn two_networks() { network2.subscribe_to_peers_messages(actor2); info!("Connecting peers..."); - let peer1 = PeerId { - address: address1.clone(), - public_key: public_key1, - }; - let peer2 = PeerId { - address: address2.clone(), - public_key: public_key2, - }; + let peer1 = PeerId::new(address1.clone(), public_key1); + let peer2 = PeerId::new(address2.clone(), public_key2); let topology1 = HashSet::from([peer2.clone()]); let topology2 = HashSet::from([peer1.clone()]); // Connect peers with each other @@ -237,10 +228,7 @@ async fn multiple_networks() { let address = socket_addr!(127.0.0.1: 12_015 + ( i * 5)); let key_pair = KeyPair::generate().unwrap(); let public_key = key_pair.public_key().clone(); - peers.push(PeerId { - address, - public_key, - }); + peers.push(PeerId::new(address, public_key)); key_pairs.push(key_pair); } diff --git a/primitives/src/const_vec.rs b/primitives/src/const_vec.rs index 30974346c16..45cf66e6f9c 100644 --- a/primitives/src/const_vec.rs +++ b/primitives/src/const_vec.rs @@ -92,6 +92,33 @@ impl IntoSchema for ConstVec { } } +impl IntoIterator for ConstVec { + type Item = T; + + type IntoIter = as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.into_vec().into_iter() + } +} + +/// Trait to extend `[T]` with a method to convert it to `ConstVec` by analogy with `[T]::to_vec()`. +pub trait ToConstVecExt { + /// The type of the items in the slice. + type Item; + + /// Copies `self` into a new [`ConstVec`]. + fn to_const_vec(&self) -> ConstVec; +} + +impl ToConstVecExt for [T] { + type Item = T; + + fn to_const_vec(&self) -> ConstVec { + ConstVec::new(self.clone()) + } +} + #[cfg(test)] mod tests { use parity_scale_codec::{Decode, Encode}; diff --git a/smart_contract/executor/derive/src/validate.rs b/smart_contract/executor/derive/src/validate.rs index 3b7df471f0f..2a2ead973c3 100644 --- a/smart_contract/executor/derive/src/validate.rs +++ b/smart_contract/executor/derive/src/validate.rs @@ -41,7 +41,7 @@ impl FromAttributes for ValidateAttribute { // but we still _want_ to validate that each attribute parses successfully // this is to ensure that we provide the user with as much validation as possible, instead of bailing out early // `Option::or_else` would NOT work here, as it would not validate conditions after the first valid one - #[allow(clippy::or_fun_call)] + #[allow(clippy::or_fun_call, clippy::too_many_lines)] fn from_attributes(attrs: &[Attribute]) -> darling::Result { let mut accumulator = darling::error::Accumulator::default(); diff --git a/tools/swarm/src/compose.rs b/tools/swarm/src/compose.rs index 80dc51f7ac4..299021b2a75 100644 --- a/tools/swarm/src/compose.rs +++ b/tools/swarm/src/compose.rs @@ -414,7 +414,7 @@ mod peer_generator { impl Peer { pub fn id(&self) -> PeerId { - PeerId::new(&self.addr(self.port_p2p), self.key_pair.public_key()) + PeerId::new(self.addr(self.port_p2p), self.key_pair.public_key().clone()) } pub fn addr(&self, port: u16) -> SocketAddr {