Skip to content

Commit

Permalink
[feature] #4126: Add chain_id to prevent replay attacks
Browse files Browse the repository at this point in the history
Signed-off-by: Marin Veršić <[email protected]>
  • Loading branch information
mversic committed Jan 9, 2024
1 parent f147295 commit 04a5362
Show file tree
Hide file tree
Showing 43 changed files with 764 additions and 542 deletions.
22 changes: 14 additions & 8 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ impl Iroha {
let kura_thread_handler = Kura::start(Arc::clone(&kura));

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

let gossiper = TransactionGossiper::from_configuration(
config.chain_id,
&config.sumeragi,
network.clone(),
Arc::clone(&queue),
Expand Down Expand Up @@ -300,6 +302,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 @@ -579,7 +582,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 @@ -630,21 +633,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(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
17 changes: 14 additions & 3 deletions cli/src/samples.rs
Original file line number Diff line number Diff line change
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<u16>,
key_pair: Option<KeyPair>,
) -> ConfigurationProxy {
let chain_id = chain_id.unwrap_or(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<u16>,
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
22 changes: 17 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,11 @@ 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 configuration = get_config(
unique_vec![peer.id.clone()],
Some(get_chain_id()),
Some(get_key_pair()),
);

let rt = Runtime::test();
let genesis = GenesisNetwork::new(
Expand All @@ -45,6 +49,7 @@ fn query_requests(criterion: &mut Criterion) {
construct_executor("../default_executor").expect("Failed to construct executor"),
)
.build(),
get_chain_id(),
&get_genesis_key_pair(&configuration),
)
.expect("genesis creation failed");
Expand Down Expand Up @@ -76,7 +81,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 +136,11 @@ 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 configuration = get_config(
unique_vec![peer.id.clone()],
Some(get_chain_id()),
Some(get_key_pair()),
);
let genesis = GenesisNetwork::new(
RawGenesisBlockBuilder::default()
.domain("wonderland".parse().expect("Valid"))
Expand All @@ -143,6 +153,7 @@ fn instruction_submits(criterion: &mut Criterion) {
construct_executor("../default_executor").expect("Failed to construct executor"),
)
.build(),
get_chain_id(),
&get_genesis_key_pair(&configuration),
)
.expect("failed to create genesis");
Expand All @@ -159,7 +170,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
4 changes: 3 additions & 1 deletion 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 = 0;

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

Expand Down
11 changes: 8 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,13 @@ 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 configuration = get_config(
unique_vec![peer.id.clone()],
Some(get_chain_id()),
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), get_chain_id(), &{
let private_key = configuration
.genesis
.private_key
Expand Down
7 changes: 6 additions & 1 deletion client/src/client.rs
Original file line number Diff line number Diff line change
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: u16,
/// 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,
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, self.account_id.clone());

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

let cfg = ConfigurationProxy {
chain_id: Some(0),
public_key: Some(public_key),
private_key: Some(private_key),
account_id: Some(
Expand Down Expand Up @@ -1706,6 +1710,7 @@ mod tests {
};

let cfg = ConfigurationProxy {
chain_id: Some(0),
public_key: Some(
"ed01207233BFC89DCBD68C19FDE6CE6158225298EC1131B6A130D1AEB454C1AB5183C0"
.parse()
Expand Down
3 changes: 2 additions & 1 deletion client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ pub mod samples {
};

/// Get sample client configuration.
pub fn get_client_config(key_pair: &KeyPair) -> Configuration {
pub fn get_client_config(chain_id: u16, 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 = 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
2 changes: 1 addition & 1 deletion client/tests/integration/burn_public_keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn submit(
eyre::Result<HashOf<TransactionPayload>>,
) {
let tx = if let Some((account_id, keypair)) = submitter {
TransactionBuilder::new(account_id)
TransactionBuilder::new(0, account_id)
.with_instructions(instructions)
.sign(keypair)
.unwrap()
Expand Down
5 changes: 3 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 = 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 @@ -181,7 +182,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(0, bob_id.clone())
.with_instructions([
Register::asset_definition(coin),
Register::asset_definition(store),
Expand Down
10 changes: 5 additions & 5 deletions client/tests/integration/permissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fn permissions_disallow_asset_transfer() {
quantity,
alice_id.clone(),
);
let transfer_tx = TransactionBuilder::new(mouse_id)
let transfer_tx = TransactionBuilder::new(0, mouse_id)
.with_instructions([transfer_asset])
.sign(mouse_keypair)
.expect("Failed to sign mouse transaction");
Expand Down Expand Up @@ -144,7 +144,7 @@ fn permissions_disallow_asset_burn() {
quantity,
AssetId::new(asset_definition_id, mouse_id.clone()),
);
let burn_tx = TransactionBuilder::new(mouse_id)
let burn_tx = TransactionBuilder::new(0, mouse_id)
.with_instructions([burn_asset])
.sign(mouse_keypair)
.expect("Failed to sign mouse transaction");
Expand Down Expand Up @@ -226,7 +226,7 @@ fn permissions_differ_not_only_by_names() {
alice_id.clone(),
);

let grant_hats_access_tx = TransactionBuilder::new(mouse_id.clone())
let grant_hats_access_tx = TransactionBuilder::new(0, mouse_id.clone())
.with_instructions([allow_alice_to_set_key_value_in_hats])
.sign(mouse_keypair.clone())
.expect("Failed to sign mouse transaction");
Expand Down Expand Up @@ -263,7 +263,7 @@ fn permissions_differ_not_only_by_names() {
alice_id,
);

let grant_shoes_access_tx = TransactionBuilder::new(mouse_id)
let grant_shoes_access_tx = TransactionBuilder::new(0, mouse_id)
.with_instructions([allow_alice_to_set_key_value_in_shoes])
.sign(mouse_keypair)
.expect("Failed to sign mouse transaction");
Expand Down Expand Up @@ -313,7 +313,7 @@ fn stored_vs_granted_token_payload() -> Result<()> {
alice_id,
);

let transaction = TransactionBuilder::new(mouse_id)
let transaction = TransactionBuilder::new(0, mouse_id)
.with_instructions([allow_alice_to_set_key_value_in_mouse_asset])
.sign(mouse_keypair)
.expect("Failed to sign mouse transaction");
Expand Down
2 changes: 1 addition & 1 deletion client/tests/integration/roles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ fn register_and_grant_role_for_metadata_access() -> Result<()> {

// Mouse grants role to Alice
let grant_role = Grant::role(role_id.clone(), alice_id.clone());
let grant_role_tx = TransactionBuilder::new(mouse_id.clone())
let grant_role_tx = TransactionBuilder::new(0, mouse_id.clone())
.with_instructions([grant_role])
.sign(mouse_key_pair)?;
test_client.submit_transaction_blocking(&grant_role_tx)?;
Expand Down
13 changes: 5 additions & 8 deletions client/tests/integration/unregister_peer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,11 @@ fn init() -> Result<(
let asset_definition_id: AssetDefinitionId = "xor#domain".parse()?;
let create_asset =
Register::asset_definition(AssetDefinition::quantity(asset_definition_id.clone()));
let instructions = parameters.into_iter().chain(
[
create_domain.into(),
create_account.into(),
create_asset.into(),
]
.into_iter(),
);
let instructions = parameters.into_iter().chain([
create_domain.into(),
create_account.into(),
create_asset.into(),
]);
client.submit_all_blocking(instructions)?;
iroha_logger::info!("Init");
Ok((
Expand Down
Loading

0 comments on commit 04a5362

Please sign in to comment.