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

Can we replace LastBlockID in Header with LastBlockHash ? #1605

Closed
ebuchman opened this issue May 22, 2018 · 14 comments
Closed

Can we replace LastBlockID in Header with LastBlockHash ? #1605

ebuchman opened this issue May 22, 2018 · 14 comments
Assignees
Labels
C:consensus Component: Consensus T:breaking Type: Breaking Change T:question Type: Question
Milestone

Comments

@ebuchman
Copy link
Contributor

Is there some reason we need the BlockID in the Header? From what I understand, we only need it in the proposal to support the SWIFT like gossiping. I don't immediately see why we would need the next block to include a commitment to the way the last block was gossiped.

@jaekwon @milosevic

@ebuchman ebuchman added T:question Type: Question C:consensus Component: Consensus labels May 22, 2018
@ebuchman
Copy link
Contributor Author

Also what's the reason for the hash of the header to be a merkle tree of its components ? Is that really worth it when we could simply amino hash the header as is ? Ideally the binary encoding of the header is effectively a protobuf encoding - that would be the simplest thing

@milosevic
Copy link
Contributor

Regarding BlockID, seems reasonable what you are saying. I guess that the role of this field is just to link block at height h with a block at height h-1. For this purpose, hash should do the job, so BlockID is not needed.

Regarding the second question, when we need to prove something (for example with light client or IBC) do we need header to serve as a root hash, or it is sufficient having several root hashes for different aspects of the system. My understanding is that it's not really needed as we prove that header is valid by checking LastCommit, and then we validated various root hashes (validator set, app hash, etc).

@ebuchman
Copy link
Contributor Author

header is valid by checking LastCommit

right, which means we need to hash the header to check the signature. I'm just proposing we make "hash the header" as simple as possible by making it a simple sha256 of the protobuf encoding!

@milosevic
Copy link
Contributor

Understood this. Seems like sha256 of the protobuf encoding is fine.

@ebuchman ebuchman added this to the launch milestone Jul 27, 2018
@ebuchman ebuchman added T:breaking Type: Breaking Change protocol labels Jul 27, 2018
@ebuchman ebuchman added this to Planned in current iteration via automation Aug 6, 2018
@milosevic milosevic self-assigned this Aug 31, 2018
@milosevic milosevic moved this from Planned to Ongoing in current iteration Sep 4, 2018
@ebuchman
Copy link
Contributor Author

ebuchman commented Sep 10, 2018

Ok, so the reason the votes need to sign the BlockID is so that peers can securely download a block using the block-part mechanism just from seeing a commit. This also means the consensus reactor can help catch up old peers by gossiping block-parts (unlike the blockchain reactor sending whole blocks).

Then since the votes sign the BlockID, you need to know the BlockID to verify a commit. Currently the BlockID is in the commit votes and the header, but since we plan to remove it from the commit votes (#1648 ), we'd have to store it somewhere else in the block. It could be in the Commit structure, or else in the Header - I guess it might as well stay in the header.

This also allows us to use the block-part mechanism to securely catchup peers in the consensus reactor - once they verify a commit, they can securely download block parts from different peers because they know the whole BlockID.

If we wanted to get rid of BlockID, we'd probably have to do something like elevate the use of Proposals - you'd need to have the proposal and the commit to download blocks using the block-part mechanism. This is probably not worth the complexity in the short term, but interesting to consider.

@milosevic
Copy link
Contributor

Ok, so it seems we have several different concerns here.

  1. Currently in the block Header, we have the LastBlockID field as a reference to a previous block. The field is of type BlockId and contains hash of the header and the PartSetHeader. Hash of the header is a merkle tree of its components (

    func (h *Header) Hash() cmn.HexBytes {
    ). If the role of this field is just to provide a cryptographic pointer to a previous block, then having LastBlockHash instead of LastBlockID seems sufficient, i.e., there is no need of having PartSetHeader of the previous Block as part of this field as it is useful only in SWIFT gossiping of the Block. Furthermore, it is not clear what is the benefit of computing hash of the header this way; it seems sufficient to compute it as a simple sha256 of the protobuf encoding of the header.

  2. On the other hand, we have BlockID as part of Vote message. The reason why we have BlockID instead of simple block header hash is to enable SWIFT dissemination of the Block in case a process has seen Polka or Commit for some Block, but hasn't received block. Having PartSetHeader enables process to obtain Block faster by fetching parts in parallel from its peers. Note that at the point of Vote creation, process obtains the corresponding PartSetHeader from the ConsensusState, either
    from cs.LockedBlockParts.Header() or cs.ProposalBlockParts.Header().

  3. As @ebuchman mentioned above, BlockID is useful also to securely catchup peers in the consensus reactor - once they verify a commit, they can securely download block parts from different peers because they know the whole BlockID. We achieve this by having BlockID as part of Commit structure.

To sum up, I think that we can replace LastBlockID in the block header with LastBlockHash, and that the hash of the header can be simple sha256 of the protobuf encoding of the header.

@milosevic milosevic moved this from Ongoing to Review Needed in current iteration Sep 11, 2018
@ebuchman
Copy link
Contributor Author

We still need the BlockID to verify commits though - if we remove it from the Header, it still has to be present in the block otherwise no one will be able to verify commits!

@milosevic
Copy link
Contributor

milosevic commented Sep 11, 2018

The question was if we can replace LastBlockId with LastBlockHash. We do need to have BlockId in the block in case we want to support SWIFT dissemination. Could you please be more precise what do you mean by verifying commits?

@ebuchman
Copy link
Contributor Author

Commits are a list of (signature, timestamp) (at least we're planning for that), and to verify the signature you need to know the BlockID (not just the block hash), so we have to store the BlockID somewhere in the block for for Commit to be verified - otherwise folks would need the entire block so they could compute the parts header to verify the commit!

@ebuchman
Copy link
Contributor Author

We could replace BlockID with BlockHash in the header, but then we'd need to make sure the BlockID is still present in the block somehow so the votes can be verified. Since that's the case, it doesn't really feel worth it to remove it from the header...

As for the hash of the header, I think the argument is that if a light client wants to verify something in the header, it doesn't need the whole header (which could be a few hundred bytes) but just the field it's interested in and the Merkle proof. So using a Merkle tree here instead of a raw hash might save a few hundred bytes, but as soon as you want to verify more than one field in the header, those savings probably disappear ...

@milosevic
Copy link
Contributor

We could replace BlockID with BlockHash in the header, but then we'd need to make sure the
BlockID is still present in the block somehow so the votes can be verified. Since that's the case, it
doesn't really feel worth it to remove it from the header...

The gain of removing BlockID from header is small in terms of bytes, but maybe it is cleaner like this.

@ebuchman
Copy link
Contributor Author

So I think in conclusion we are not going to remove the BlockID, so nothing will change here. We should definitely better document why it's there and why we need it.

@xla
Copy link
Contributor

xla commented Sep 12, 2018

Can we create a follow-up issue and close this?

@milosevic
Copy link
Contributor

Here is the issue: https://github.com/tendermint/tendermint/issues/2373.

current iteration automation moved this from Review Needed to Done Sep 12, 2018
firelizzard18 pushed a commit to AccumulateNetwork/tendermint that referenced this issue Feb 1, 2024
* ADR 109: Internalize specific packages to reduce surface area (tendermint#1485)

* consensus: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* evidence: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* inspect: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* state: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* blocksync: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* statesync: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* store: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/async: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/autofile: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/bits: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/clist: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/cmap: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/events: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/fail: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/flowrate: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/os: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/progressbar: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/protoio: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/pubsub: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/net: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/rand: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/service: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/strings: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/sync: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/tempfile: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* libs/timer: Move into internal

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add changelog entries

Signed-off-by: Thane Thomson <connect@thanethomson.com>

---------

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* ADR 109: Modularize test infra (tendermint#1488)

* test/e2e: Split out as separate module

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* test/loadtime: Split out as separate module

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* test/e2e: Remove optimization from Docker image construction

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Ensure that linter covers E2E framework and app

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Update CI linting to cover submodules

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add changelog entry

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Expand linter coverage to loadtime tool

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* Add missing phony entries

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* test/e2e: Sync debug Dockerfile with primary Dockerfile

Signed-off-by: Thane Thomson <connect@thanethomson.com>

---------

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* chore: ADR 109: go mod tidy (tendermint#1606)

* go mod tidy

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* go.mod: Remove patch version

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* go.mod: Remove new toolchain directives

Signed-off-by: Thane Thomson <connect@thanethomson.com>

---------

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* ADR 109: Fix mock generation (tendermint#1607)

* internal: Fix mockery code generation script paths

Signed-off-by: Thane Thomson <connect@thanethomson.com>

* make mockery

Signed-off-by: Thane Thomson <connect@thanethomson.com>

---------

Signed-off-by: Thane Thomson <connect@thanethomson.com>

---------

Signed-off-by: Thane Thomson <connect@thanethomson.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C:consensus Component: Consensus T:breaking Type: Breaking Change T:question Type: Question
Projects
No open projects
Development

No branches or pull requests

3 participants