Skip to content

Commit

Permalink
[feature] hyperledger-iroha#4126: Add chain_id to prevent replay atta…
Browse files Browse the repository at this point in the history
…cks (hyperledger-iroha#4185)

* [feature] hyperledger-iroha#4126: Add chain_id to prevent replay attacks

Signed-off-by: Marin Veršić <[email protected]>

* [feature]: add implementation for Box<str> FFI conversion

Signed-off-by: Marin Veršić <[email protected]>
Signed-off-by: Asem-Abdelhady <[email protected]>
  • Loading branch information
mversic authored and Asem-Abdelhady committed Jan 22, 2024
1 parent 5e7efb7 commit b55b484
Show file tree
Hide file tree
Showing 59 changed files with 1,130 additions and 735 deletions.
22 changes: 14 additions & 8 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ impl Iroha {
let kura_thread_handler = Kura::start(Arc::clone(&kura));

let sumeragi = SumeragiHandle::start(SumeragiStartArgs {
chain_id: config.chain_id.clone(),
configuration: &config.sumeragi,
events_sender: events_sender.clone(),
wsv,
Expand All @@ -272,6 +273,7 @@ impl Iroha {
.start();

let gossiper = TransactionGossiper::from_configuration(
config.chain_id.clone(),
&config.sumeragi,
network.clone(),
Arc::clone(&queue),
Expand Down Expand Up @@ -301,6 +303,7 @@ impl Iroha {
let kiso = KisoHandle::new(config.clone());

let torii = Torii::new(
config.chain_id,
kiso.clone(),
&config.torii,
Arc::clone(&queue),
Expand Down Expand Up @@ -583,7 +586,7 @@ pub fn read_config(
.wrap_err("Invalid genesis configuration")?
{
Some(
GenesisNetwork::new(raw_block, &key_pair)
GenesisNetwork::new(raw_block, &config.chain_id, &key_pair)
.wrap_err("Failed to construct the genesis")?,
)
} else {
Expand Down Expand Up @@ -634,21 +637,24 @@ mod tests {
use super::*;

fn config_factory() -> Result<ConfigurationProxy> {
let mut base = ConfigurationProxy::default();

let key_pair = KeyPair::generate()?;

base.public_key = Some(key_pair.public_key().clone());
base.private_key = Some(key_pair.private_key().clone());
let mut base = ConfigurationProxy {
chain_id: Some(ChainId::new("0")),

let torii = base.torii.as_mut().unwrap();
torii.p2p_addr = Some(socket_addr!(127.0.0.1:1337));
torii.api_url = Some(socket_addr!(127.0.0.1:1337));
public_key: Some(key_pair.public_key().clone()),
private_key: Some(key_pair.private_key().clone()),

..ConfigurationProxy::default()
};
let genesis = base.genesis.as_mut().unwrap();
genesis.private_key = Some(Some(key_pair.private_key().clone()));
genesis.public_key = Some(key_pair.public_key().clone());

let torii = base.torii.as_mut().unwrap();
torii.p2p_addr = Some(socket_addr!(127.0.0.1:1337));
torii.api_url = Some(socket_addr!(127.0.0.1:1337));

Ok(base)
}

Expand Down
19 changes: 15 additions & 4 deletions cli/src/samples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use iroha_config::{
torii::{uri::DEFAULT_API_ADDR, DEFAULT_TORII_P2P_ADDR},
};
use iroha_crypto::{KeyPair, PublicKey};
use iroha_data_model::{peer::PeerId, prelude::*};
use iroha_data_model::{peer::PeerId, prelude::*, ChainId};
use iroha_primitives::unique_vec::UniqueVec;

/// Get sample trusted peers. The public key must be the same as `configuration.public_key`
Expand Down Expand Up @@ -52,12 +52,19 @@ pub fn get_trusted_peers(public_key: Option<&PublicKey>) -> HashSet<PeerId> {
///
/// # Panics
/// - when [`KeyPair`] generation fails (rare case).
pub fn get_config_proxy(peers: UniqueVec<PeerId>, key_pair: Option<KeyPair>) -> ConfigurationProxy {
pub fn get_config_proxy(
peers: UniqueVec<PeerId>,
chain_id: Option<ChainId>,
key_pair: Option<KeyPair>,
) -> ConfigurationProxy {
let chain_id = chain_id.unwrap_or_else(|| ChainId::new("0"));

let (public_key, private_key) = key_pair
.unwrap_or_else(|| KeyPair::generate().expect("Key pair generation failed"))
.into();
iroha_logger::info!(%public_key);
ConfigurationProxy {
chain_id: Some(chain_id),
public_key: Some(public_key.clone()),
private_key: Some(private_key.clone()),
sumeragi: Some(Box::new(iroha_config::sumeragi::ConfigurationProxy {
Expand Down Expand Up @@ -94,8 +101,12 @@ pub fn get_config_proxy(peers: UniqueVec<PeerId>, key_pair: Option<KeyPair>) ->
///
/// # Panics
/// - when [`KeyPair`] generation fails (rare case).
pub fn get_config(trusted_peers: UniqueVec<PeerId>, key_pair: Option<KeyPair>) -> Configuration {
get_config_proxy(trusted_peers, key_pair)
pub fn get_config(
trusted_peers: UniqueVec<PeerId>,
chain_id: Option<ChainId>,
key_pair: Option<KeyPair>,
) -> Configuration {
get_config_proxy(trusted_peers, chain_id, key_pair)
.build()
.expect("Iroha config should build as all required fields were provided")
}
Expand Down
26 changes: 21 additions & 5 deletions client/benches/torii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use iroha_client::{
use iroha_genesis::{GenesisNetwork, RawGenesisBlockBuilder};
use iroha_primitives::unique_vec;
use iroha_version::Encode;
use test_network::{get_key_pair, Peer as TestPeer, PeerBuilder, TestRuntime};
use test_network::{get_chain_id, get_key_pair, Peer as TestPeer, PeerBuilder, TestRuntime};
use tokio::runtime::Runtime;

const MINIMUM_SUCCESS_REQUEST_RATIO: f32 = 0.9;
Expand All @@ -30,7 +30,13 @@ fn get_genesis_key_pair(config: &iroha_config::iroha::Configuration) -> KeyPair

fn query_requests(criterion: &mut Criterion) {
let mut peer = <TestPeer>::new().expect("Failed to create peer");
let configuration = get_config(unique_vec![peer.id.clone()], Some(get_key_pair()));

let chain_id = get_chain_id();
let configuration = get_config(
unique_vec![peer.id.clone()],
Some(chain_id.clone()),
Some(get_key_pair()),
);

let rt = Runtime::test();
let genesis = GenesisNetwork::new(
Expand All @@ -45,6 +51,7 @@ fn query_requests(criterion: &mut Criterion) {
construct_executor("../default_executor").expect("Failed to construct executor"),
)
.build(),
&chain_id,
&get_genesis_key_pair(&configuration),
)
.expect("genesis creation failed");
Expand Down Expand Up @@ -76,7 +83,8 @@ fn query_requests(criterion: &mut Criterion) {
quantity,
AssetId::new(asset_definition_id, account_id.clone()),
);
let mut client_config = iroha_client::samples::get_client_config(&get_key_pair());
let mut client_config =
iroha_client::samples::get_client_config(get_chain_id(), &get_key_pair());

client_config.torii_api_url = format!("http://{}", peer.api_address).parse().unwrap();

Expand Down Expand Up @@ -130,7 +138,13 @@ fn instruction_submits(criterion: &mut Criterion) {
println!("instruction submits");
let rt = Runtime::test();
let mut peer = <TestPeer>::new().expect("Failed to create peer");
let configuration = get_config(unique_vec![peer.id.clone()], Some(get_key_pair()));

let chain_id = get_chain_id();
let configuration = get_config(
unique_vec![peer.id.clone()],
Some(chain_id.clone()),
Some(get_key_pair()),
);
let genesis = GenesisNetwork::new(
RawGenesisBlockBuilder::default()
.domain("wonderland".parse().expect("Valid"))
Expand All @@ -143,6 +157,7 @@ fn instruction_submits(criterion: &mut Criterion) {
construct_executor("../default_executor").expect("Failed to construct executor"),
)
.build(),
&chain_id,
&get_genesis_key_pair(&configuration),
)
.expect("failed to create genesis");
Expand All @@ -159,7 +174,8 @@ fn instruction_submits(criterion: &mut Criterion) {
.into();
let create_account = Register::account(Account::new(account_id.clone(), [public_key])).into();
let asset_definition_id = AssetDefinitionId::new("xor".parse().expect("Valid"), domain_id);
let mut client_config = iroha_client::samples::get_client_config(&get_key_pair());
let mut client_config =
iroha_client::samples::get_client_config(get_chain_id(), &get_key_pair());
client_config.torii_api_url = format!("http://{}", peer.api_address).parse().unwrap();
let iroha_client = Client::new(&client_config).expect("Invalid client configuration");
thread::sleep(std::time::Duration::from_millis(5000));
Expand Down
7 changes: 5 additions & 2 deletions client/benches/tps/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ impl MeasurerUnit {

/// Spawn who periodically submits transactions
fn spawn_transaction_submitter(&self, shutdown_signal: mpsc::Receiver<()>) -> JoinHandle<()> {
let chain_id = ChainId::new("0");

let submitter = self.client.clone();
let interval_us_per_tx = self.config.interval_us_per_tx;
let instructions = self.instructions();
Expand All @@ -218,8 +220,9 @@ impl MeasurerUnit {
for instruction in instructions {
match shutdown_signal.try_recv() {
Err(mpsc::TryRecvError::Empty) => {
let mut transaction = TransactionBuilder::new(alice_id.clone())
.with_instructions([instruction]);
let mut transaction =
TransactionBuilder::new(chain_id.clone(), alice_id.clone())
.with_instructions([instruction]);
transaction.set_nonce(nonce); // Use nonce to avoid transaction duplication within the same thread

let transaction = submitter
Expand Down
13 changes: 10 additions & 3 deletions client/examples/million_accounts_genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use iroha_data_model::isi::InstructionBox;
use iroha_genesis::{GenesisNetwork, RawGenesisBlock, RawGenesisBlockBuilder};
use iroha_primitives::unique_vec;
use test_network::{
get_key_pair, wait_for_genesis_committed, Peer as TestPeer, PeerBuilder, TestRuntime,
get_chain_id, get_key_pair, wait_for_genesis_committed, Peer as TestPeer, PeerBuilder,
TestRuntime,
};
use tokio::runtime::Runtime;

Expand Down Expand Up @@ -36,9 +37,15 @@ fn generate_genesis(num_domains: u32) -> RawGenesisBlock {

fn main_genesis() {
let mut peer = <TestPeer>::new().expect("Failed to create peer");
let configuration = get_config(unique_vec![peer.id.clone()], Some(get_key_pair()));

let chain_id = get_chain_id();
let configuration = get_config(
unique_vec![peer.id.clone()],
Some(chain_id.clone()),
Some(get_key_pair()),
);
let rt = Runtime::test();
let genesis = GenesisNetwork::new(generate_genesis(1_000_000_u32), &{
let genesis = GenesisNetwork::new(generate_genesis(1_000_000_u32), &chain_id, &{
let private_key = configuration
.genesis
.private_key
Expand Down
9 changes: 7 additions & 2 deletions client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ use crate::{
prelude::*,
query::{Pagination, Query, Sorting},
transaction::TransactionPayload,
BatchedResponse, ValidationFail,
BatchedResponse, ChainId, ValidationFail,
},
http::{Method as HttpMethod, RequestBuilder, Response, StatusCode},
http_default::{self, DefaultRequestBuilder, WebSocketError, WebSocketMessage},
Expand Down Expand Up @@ -344,6 +344,8 @@ impl_query_output! {
)]
#[display(fmt = "{}@{torii_url}", "key_pair.public_key()")]
pub struct Client {
/// Unique id of the blockchain. Used for simple replay attack protection.
pub chain_id: ChainId,
/// Url for accessing iroha node
pub torii_url: Url,
/// Accounts keypair
Expand Down Expand Up @@ -440,6 +442,7 @@ impl Client {
}

Ok(Self {
chain_id: configuration.chain_id.clone(),
torii_url: configuration.torii_api_url.clone(),
key_pair: KeyPair::new(
configuration.public_key.clone(),
Expand All @@ -466,7 +469,7 @@ impl Client {
instructions: impl Into<Executable>,
metadata: UnlimitedMetadata,
) -> Result<SignedTransaction> {
let tx_builder = TransactionBuilder::new(self.account_id.clone());
let tx_builder = TransactionBuilder::new(self.chain_id.clone(), self.account_id.clone());

let mut tx_builder = match instructions.into() {
Executable::Instructions(instructions) => tx_builder.with_instructions(instructions),
Expand Down Expand Up @@ -1666,6 +1669,7 @@ mod tests {
let (public_key, private_key) = KeyPair::generate().unwrap().into();

let cfg = ConfigurationProxy {
chain_id: Some(ChainId::new("0")),
public_key: Some(public_key),
private_key: Some(private_key),
account_id: Some(
Expand Down Expand Up @@ -1708,6 +1712,7 @@ mod tests {
};

let cfg = ConfigurationProxy {
chain_id: Some(ChainId::new("0")),
public_key: Some(
"ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
.parse()
Expand Down
4 changes: 3 additions & 1 deletion client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ pub mod samples {
use crate::{
config::{torii::DEFAULT_API_ADDR, Configuration, ConfigurationProxy},
crypto::KeyPair,
data_model::ChainId,
};

/// Get sample client configuration.
pub fn get_client_config(key_pair: &KeyPair) -> Configuration {
pub fn get_client_config(chain_id: ChainId, key_pair: &KeyPair) -> Configuration {
let (public_key, private_key) = key_pair.clone().into();
ConfigurationProxy {
chain_id: Some(chain_id),
public_key: Some(public_key),
private_key: Some(private_key),
account_id: Some(
Expand Down
10 changes: 6 additions & 4 deletions client/tests/integration/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,12 @@ fn find_rate_and_make_exchange_isi_should_succeed() {
alice_id.clone(),
);

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");
let chain_id = ChainId::new("0");
let grant_asset_transfer_tx =
TransactionBuilder::new(chain_id, asset_id.account_id().clone())
.with_instructions([allow_alice_to_transfer_asset])
.sign(owner_keypair)
.expect("Failed to sign seller transaction");

test_client
.submit_transaction_blocking(&grant_asset_transfer_tx)
Expand Down
4 changes: 3 additions & 1 deletion client/tests/integration/burn_public_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ fn submit(
HashOf<SignedTransaction>,
eyre::Result<HashOf<TransactionPayload>>,
) {
let chain_id = ChainId::new("0");

let tx = if let Some((account_id, keypair)) = submitter {
TransactionBuilder::new(account_id)
TransactionBuilder::new(chain_id, account_id)
.with_instructions(instructions)
.sign(keypair)
.unwrap()
Expand Down
7 changes: 5 additions & 2 deletions client/tests/integration/domain_owner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ fn domain_owner_asset_definition_permissions() -> Result<()> {
let (_rt, _peer, test_client) = <PeerBuilder>::new().with_port(11_085).start_with_runtime();
wait_for_genesis_committed(&[test_client.clone()], 0);

let chain_id = ChainId::new("0");
let kingdom_id: DomainId = "kingdom".parse()?;
let bob_id: AccountId = "bob@kingdom".parse()?;
let rabbit_id: AccountId = "rabbit@kingdom".parse()?;
Expand All @@ -122,7 +123,7 @@ fn domain_owner_asset_definition_permissions() -> Result<()> {

// register asset definitions by "bob@kingdom" so he is owner of it
let coin = AssetDefinition::quantity(coin_id.clone());
let transaction = TransactionBuilder::new(bob_id.clone())
let transaction = TransactionBuilder::new(chain_id, bob_id.clone())
.with_instructions([Register::asset_definition(coin)])
.sign(bob_keypair)?;
test_client.submit_transaction_blocking(&transaction)?;
Expand Down Expand Up @@ -161,6 +162,8 @@ fn domain_owner_asset_definition_permissions() -> Result<()> {

#[test]
fn domain_owner_asset_permissions() -> Result<()> {
let chain_id = ChainId::new("0");

let (_rt, _peer, test_client) = <PeerBuilder>::new().with_port(11_090).start_with_runtime();
wait_for_genesis_committed(&[test_client.clone()], 0);

Expand All @@ -181,7 +184,7 @@ fn domain_owner_asset_permissions() -> Result<()> {
// register asset definitions by "bob@kingdom" so he is owner of it
let coin = AssetDefinition::quantity(coin_id.clone());
let store = AssetDefinition::store(store_id.clone());
let transaction = TransactionBuilder::new(bob_id.clone())
let transaction = TransactionBuilder::new(chain_id, bob_id.clone())
.with_instructions([
Register::asset_definition(coin),
Register::asset_definition(store),
Expand Down
Loading

0 comments on commit b55b484

Please sign in to comment.