Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] #4126: Add chain_id to prevent replay attacks #4185

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading