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

refactor!: Use hash to validate genesis block #4793

Closed
wants to merge 4 commits into from

Conversation

dima74
Copy link
Contributor

@dima74 dima74 commented Jul 1, 2024

Description

  • Added genesis.hash config parameter and removed genesis.public_key
  • Added genesis field (account id) to genesis.json. It will be authority of genesis transactions
  • Genesis transactions will not be signed (actually signed by some random key pair and peers will ignore signatory for genesis transactions and instead check genesis block hash match)
  • Replaced kagami genesis sign with kagami genesis prepare which converts genesis.json to genesis.scale (SignedBlock) and outputs genesis hash to stdout
  • Genesis domain and account is now added to the World after receiving genesis block (since we now don't know genesis account id at startup but need to use authority of genesis block)

Todo:

  • Fix docker-compose. Currently each peer generates genesis block independently, so its time and hash will be different

Deployment changes

  1. Add genesis field to genesis.json (genesis account id)
  2. Change kagami genesis sign invocation

Before:

kagami genesis sign genesis.json --public-key ... --private-key ... --out-file ...

After:

kagami genesis prepare ... --out-file ...

This will also output genesis hash

  1. Provide genesis hash using GENESIS_HASH environment variable to irohad

Linked issue

Closes #4555

Benefits

Checklist

  • I've read CONTRIBUTING.md
  • I've used the standard signed-off commit format (or will squash just before merging)
  • All applicable CI checks pass (or I promised to make them pass later)
  • (optional) I've written unit tests for the code changes
  • I replied to all comments after code review, marking all implemented changes with thumbs up

@dima74 dima74 self-assigned this Jul 1, 2024
@github-actions github-actions bot added the config-changes Changes in configuration and start up of the Iroha label Jul 1, 2024
Copy link

github-actions bot commented Jul 1, 2024

@BAStos525

@Erigara Erigara self-assigned this Jul 1, 2024
configs/swarm/docker-compose.local.yml Outdated Show resolved Hide resolved
tools/kagami/src/genesis.rs Outdated Show resolved Hide resolved
tools/kagami/src/genesis.rs Outdated Show resolved Hide resolved
client/examples/register_1000_triggers.rs Outdated Show resolved Hide resolved
config/iroha_test_config.toml Outdated Show resolved Hide resolved
configs/swarm/docker-compose.local.yml Outdated Show resolved Hide resolved
@Erigara
Copy link
Contributor

Erigara commented Jul 1, 2024

So if we still have to use genesis account afaik what benefits do we gain by now also checking hash of genesis block?

@dima74
Copy link
Contributor Author

dima74 commented Jul 1, 2024

So if we still have to use genesis account afaik what benefits do we gain by now also checking hash of genesis block?

As I see kagami genesis sign might be simplified a bit (no genesis key pair needed) if we figure out how to do it. But I don't know how genesis account id can be removed altogether since it is used in genesis.json instructions like Transfer

Signed-off-by: Dmitry Murzin <diralik@yandex.ru>
Signed-off-by: Dmitry Murzin <diralik@yandex.ru>
Signed-off-by: Dmitry Murzin <diralik@yandex.ru>
Signed-off-by: Dmitry Murzin <diralik@yandex.ru>
@github-actions github-actions bot added the api-changes Changes in the API for client libraries label Jul 2, 2024
@dima74 dima74 requested review from Erigara and mversic July 2, 2024 15:06
Comment on lines +12 to 13
Prepare(prepare::Args),
Generate(generate::Args),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Prepare(prepare::Args),
Generate(generate::Args),
Prepare(prepare::Args),
Generate(generate::Args),

do we need both?

@@ -329,7 +339,6 @@ mod candidate {
impl SignedTransactionCandidate {
fn validate(self) -> Result<SignedTransactionV1, &'static str> {
self.validate_instructions()?;
self.validate_signature()?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this. Transactions with incorrect signatures might creep into Iroha. I'd rather that you make another SignedTransactionCandidate in data_model::block which will have different implementation, i.e. it will not verify genesis signatures

Copy link
Contributor Author

@dima74 dima74 Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but what to do with validate_transaction in executor? It will deserialize SignedTransaction at WASM side, so it will fail for genesis transaction if we keep validate signature during deserialization

One option I can think of is to make SignedTransaction validation #[cfg(not(target_family = "wasm"))]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One option I can think of is to make SignedTransaction validation #[cfg(not(target_family = "wasm"))]

I like this suggestion. Can we say that since data model entities are always provided by the host there is no need to validate them on the wasm side? If so, we can can open a ticket to generalize this approach to all entities, although it seems kinda hacky and I'm not sure if this won't bite us in the ass

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another possibility is to hardcode some public key as genesis and say that every Iroha deployment will have this as genesis account_id. In that case we can remove account from genesis.json. This is more or less how we had it except that kagami wouldn't have to receive private key but it already knows it's value. @Erigara @s8sato ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's discuss tomorrow on standup

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use the same multihash format but with identity code

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussion it was noted that we still want genesis to sign transaction/block in which case my proposal is not relevant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The complexity seems to come from trying to strip away the functionality of digital signatures while taking advantage of the transaction processing flow.
There might not have been any problem in the first place: if the privilege comes from block height 0, what is the actual harm even if the genesis account with the correct private key appears after genesis?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the actual harm even if the genesis account with the correct private key appears after genesis?

I like this point. Especially if someone has misconfigured the blockchain in genesis.json, for example they haven't transfered ownership

Copy link
Contributor

@s8sato s8sato Jul 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, currently custom genesis can grant any privileges to any account. But they agreed anyway. They joined the network while being able to see that genesis block

Comment on lines +373 to +375
if transaction.value.authority().domain.name.as_ref() != "genesis" {
return Err("Genesis transaction authority must be from `genesis` domain");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should care about this?

Copy link
Contributor

@mversic mversic Jul 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can remove GENESIS_DOMAIN_ID check from this module. Also I think that we can remove this constant alltogether and with it the notion of "genesis" domain

Copy link
Contributor Author

@dima74 dima74 Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are checks like "not allowed to register account in genesis domain" in impl Execute for Register<Account>. Should we keep those checks?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to keep those checks. Because if we do, we have to track it in runtime which is complicated. Remove checks and open an issue to discuss

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, after genesis there doesn't seem to be any particular harm in referring a domain named "genesis"

@@ -139,5 +139,6 @@
}
}
],
"topology": []
"topology": [],
"genesis": "ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4@genesis"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"genesis": "ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4@genesis"
"account": "ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4@genesis"

let's rename, it looks silly. Sorry, it was my initial suggestion

TRUSTED_PEERS: '[{"address":"irohad2:1339","public_key":"ed01204EE2FCD53E1730AF142D1E23951198678295047F9314B4006B0CB61850B1DB10"},{"address":"irohad0:1337","public_key":"ed0120A98BAFB0663CE08D75EBD506FEC38A84E576A7C9B0897693ED4B04FD9EF2D18D"},{"address":"irohad3:1340","public_key":"ed0120CACF3A84B8DC8710CE9D6B968EE95EC7EE4C93C85858F026F3B4417F569592CE"}]'
TOPOLOGY: '[{"address":"irohad2:1339","public_key":"ed01204EE2FCD53E1730AF142D1E23951198678295047F9314B4006B0CB61850B1DB10"},{"address":"irohad1:1338","public_key":"ed01209897952D14BDFAEA780087C38FF3EB800CB20B882748FC95A575ADB9CD2CB21D"},{"address":"irohad0:1337","public_key":"ed0120A98BAFB0663CE08D75EBD506FEC38A84E576A7C9B0897693ED4B04FD9EF2D18D"},{"address":"irohad3:1340","public_key":"ed0120CACF3A84B8DC8710CE9D6B968EE95EC7EE4C93C85858F026F3B4417F569592CE"}]'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what was the reason you introduced genesis to all peers? is it because of the integration tests?
On the other hand, I would prefer keeping it as simple as possible because of external users

@@ -417,7 +412,7 @@ mod valid {
block: SignedBlock,
topology: &Topology,
expected_chain_id: &ChainId,
genesis_account: &AccountId,
genesis_hash: &HashOf<SignedBlock>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
genesis_hash: &HashOf<SignedBlock>,
genesis_hash: HashOf<SignedBlock>,

it's a copy type

Comment on lines +697 to +703
fn genesis_account_and_domain(public_key: PublicKey) -> (Account, Domain) {
let genesis_account_id =
AccountId::new(iroha_genesis::GENESIS_DOMAIN_ID.clone(), public_key);
let genesis_account = Account::new(genesis_account_id).into_account();
let genesis_domain =
Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()).build(&genesis_account.id);
(genesis_account, genesis_domain)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn genesis_account_and_domain(public_key: PublicKey) -> (Account, Domain) {
let genesis_account_id =
AccountId::new(iroha_genesis::GENESIS_DOMAIN_ID.clone(), public_key);
let genesis_account = Account::new(genesis_account_id).into_account();
let genesis_domain =
Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()).build(&genesis_account.id);
(genesis_account, genesis_domain)
fn genesis_account_and_domain(public_key: PublicKey) -> (Account, Domain) {
let genesis_account_id =
AccountId::new(iroha_genesis::GENESIS_DOMAIN_ID.clone(), public_key);
let genesis_account = Account::new(genesis_account_id).into_account();
let genesis_domain =
Domain::new(iroha_genesis::GENESIS_DOMAIN_ID.clone()).build(&genesis_account.id);
(genesis_account, genesis_domain)

IMO we should take both from the genesis authority. And remove the GENESIS_DOMAIN_ID

}

/// Extracts inner `SignedBlock`
pub fn into_inner(self) -> SignedBlock {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just implement From<GenesisBlock> for SignedBlock

@@ -23,14 +23,31 @@ pub static GENESIS_DOMAIN_ID: Lazy<DomainId> = Lazy::new(|| "genesis".parse().un
/// First transaction should contain single [`Upgrade`] instruction to set executor.
/// Second transaction should contain all other instructions.
/// If there are no other instructions, second transaction will be omitted.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Encode)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need Encode now?

Comment on lines +170 to +173
// There is no need to sign genesis transactions since we check it by hash,
// but our data model requires SignedTransaction to be signed,
// so we sign it with random key pair
.sign(KeyPair::random().private_key())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to bring attention for other reviewers. This is a bit contentious, but I think it's a good approach.

Comment on lines +60 to +61
/// Genesis account
genesis: AccountId,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please put it after chain. Generated json file will look nicer

@dima74
Copy link
Contributor Author

dima74 commented Jul 4, 2024

Discussed with @mversic and decided that currently it is not worth to implement #4555. Originally it was expected that it will simplify things, but looks like there is no good and clean implementation, so will keep current approach with genesis public and private key (note that genesis private key is used only in kagami)

@dima74 dima74 closed this Jul 4, 2024
@dima74 dima74 mentioned this pull request Jul 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-changes Changes in the API for client libraries config-changes Changes in configuration and start up of the Iroha
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Don't sign genesis blocks
4 participants