-
Notifications
You must be signed in to change notification settings - Fork 140
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
HIP-1056: Block Streams #1056
base: main
Are you sure you want to change the base?
HIP-1056: Block Streams #1056
Conversation
✅ Deploy Preview for hedera-hips ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
retain on disk, without breaking the cryptographic integrity of the block stream. Different block node operators in | ||
different legal jurisdictions can make different decisions about what data to retain. Or, block node operators may | ||
subset the stream to retain minimal data and minimize storage costs. | ||
- **Errata Handling**: Errata refers to software bugs where a node executed the transaction correctly but did not record |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative option would be the given block node to be temporarily excluded from the network of block nodes and it's block stream invalidated. Then, upon fixing the bug, the block node operator could again join the network, sync the state and continue it's normal work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errata are needed when a bug on the consensus node produces incorrect output. This section refers to the necessity to change the recorded block chain. I believe this has been necessary a small number of times since genesis on mainnet.
Block Nodes are not a network; each node is completely independent. Those details will be specified in a different HIP.
``` | ||
|
||
```protobuf | ||
message CryptoTransferOutput { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The crypto create might result in auto account creation. Shouldn't we also add a record for it for those cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The account creation will be present in StateChanges, no additional output is needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to call out parent child considerations more explicitly. Might be done but noting here as a reminder to come back in case there's room for improvement
Signed-off-by: Nana Essilfie-Conduah <[email protected]>
Signed-off-by: Nana Essilfie-Conduah <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few typos and wording suggestions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got to Design for Verifiability
. Will finish the rest when I can.
HIP/hip-1056.md
Outdated
|
||
With the record stream design nodes had to independently upload record streams and their signatures files. | ||
These were stored in varied public cloud buckets and anyone in the world could download the files | ||
e.g. Mirror Nodes do this today to support queries. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
formatting nit
e.g. Mirror Nodes do this today to support queries. | |
(e.g. Mirror Nodes do this today to support queries.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
HIP/hip-1056.md
Outdated
- **Signature Files**: Each node currently produces a v6 record stream with a corresponding signature file for | ||
verification. Mirror nodes must download a strong minority (1/3 of consensus weight) worth of these signature files, | ||
along with the record file, to verify v6 records. This frequent access to cloud storage (GCS/S3) is costly. To reduce | ||
this expense and complexity, blocks include a TSS signature from the majority weight of consensus nodes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Today the plan is to make the threshold a majority, but it could change in the future. There are various levels of guarantees that can be made by thresholds of 1/3, >1/2, and >2/3. I suggest:
this expense and complexity, blocks include a TSS signature from the majority weight of consensus nodes. | |
this expense and complexity, blocks include a TSS signature from a threshold (minimum of 1/3) weight of consensus nodes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is important for the diagram to match the verbage, it should include the other stream files that are being consolidated: sidecar files and signature files
HIP/hip-1056.md
Outdated
simplify node software by allowing nodes to reconnect with a block node, instead of another consensus node. This | ||
is a significant and important enhancement to reconnect that is needed for permissionless nodes. | ||
- **Query Endpoints:** As Block Nodes or other consumers of the Block Stream can maintain a live state in sync with the | ||
consensus nodes, they can answer any queries against state. They can also produce cryptograph state proofs to prove |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consensus nodes, they can answer any queries against state. They can also produce cryptograph state proofs to prove | |
consensus nodes, they can answer any queries against state. They can also produce cryptographic state proofs to prove |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
HIP/hip-1056.md
Outdated
``` | ||
NOTE: Update: Add missing Round header after Block Header to image | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be removed since the diagram has the Round Header
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The label for the Input Block Items Merkle Tree is labeled "Output"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TTS Signature -> TSS Signature
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments:
- Where is the Round Header included?
- It might be worth stating explicitly that the order in which the items appear in the block merkle tree are not the same as the order in which they are streamed. This is implied in the description that says the items are organized into items known pre-execution and those known after, but better to state it explicitly IMO.
- Maybe this comes later, but if not, I think it's worth explaining why the hash of the state merkle tree from the beginning of the block is included in the tree rather than the hash that results from all the stuff included in that block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made the requested changes and inclusions
HIP/hip-1056.md
Outdated
Each block within the block stream is a self-contained entity, including every event and all transactions that were | ||
part of the block, the state changes as a result of those transactions, and a | ||
_network signature_ (using a BLS threshold signature scheme we call TSS) that proves the block was signed by a | ||
threshold of the network. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
threshold of the network. | |
subset of nodes whose stake accounts for more than 1/3 of the network's consensus weight. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adopted
HIP/hip-1056.md
Outdated
The block stream has a separate `block stream merkle tree` which is a merkle tree of all the items in the block stream | ||
over the entire history of the stream. Of course, older blocks can be represented by their hash rather than the actual | ||
full contents, so it is possible to have a very large block stream merkle tree *in concept* that is still very efficient | ||
to use. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The block stream has a separate `block stream merkle tree` which is a merkle tree of all the items in the block stream | |
over the entire history of the stream. Of course, older blocks can be represented by their hash rather than the actual | |
full contents, so it is possible to have a very large block stream merkle tree *in concept* that is still very efficient | |
to use. | |
At each block boundary, the state merkle tree is a depth two *subtree* of a `block merkle tree`, which is a binary merkle tree whose four subtrees at depth two also include the block's input items, the block's output items, and the merkle tree of the previous block. (Of course hash computation for the block `N` merkle tree reuses the hash already computed for the block `N-1` merkle tree.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adopted
HIP/hip-1056.md
Outdated
Block streams are built out of Items. Those items can be delivered in a continuous stream containing one or more blocks. | ||
Or they can be delivered as a single block using the `Block` message type container. If a single block is delivered as | ||
a file it expected to be either a raw protobuf `Block` message with file name of `<BLOCK_NUMBER>.blk` e.g. | ||
`0000000000000000001.blk` . With total of 19 digits to allow for 2^63 blocks. Or a compressed file e.g. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently Block file names are 36-digit strings padded with leading zeroes. FileBlockItemWriter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jsync-swirlds does this match with latest considerations or are these separate node (CN & BN) considerations?
Should this HIP at all mention/reference the behaviors around consensus node to block node communication? Will this information be covered in the block node HIP? |
HIP/hip-1056.md
Outdated
Record files are streams in a blockchain, with the hash of previous file included in the next record file, ensuring | ||
immutability. Additionally, each consensus node on the Hedera mainnet signs a hash of the record files as they are |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Record files are streams in a blockchain, with the hash of previous file included in the next record file, ensuring | |
immutability. Additionally, each consensus node on the Hedera mainnet signs a hash of the record files as they are | |
Record file contents are summarized in a chain of hashes, with each file's contents containing the hash of the previous file; so that the hash of the current file depends on the contents of the entire record stream. Additionally, each consensus node on the Hedera mainnet signs the hash of each record file as it is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...containing the hash of the previous file...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got further, but still not done.
can authenticate transactional data without the cost and complexity of downloading multiple signature files for | ||
verification. | ||
3. As a verifier for a network, I want a single, self-contained stream of data with aggregated signature signed by a | ||
threshold of stake weights, so that I can cost-effectively confirm that the stream represents the consensus output of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should really decide what terms we are going to use. Some places use stake weights and others simply use weight. Either is fine as long as it is used consistently. Inconsistent use will contribute to confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to stake weight
HIP/hip-1056.md
Outdated
The Block Stream will be defined by the continuous transmission of Block information defined as follows. | ||
- Each `Block` represents all inputs and outputs of the consensus network for zero or more whole `Rounds`. The maximum | ||
number of rounds per block will be configurable and start at 1 round per block. It could be increased later if | ||
necessary for performance reasons or if round become very frequent and small. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
necessary for performance reasons or if round become very frequent and small. | |
necessary for performance reasons or if rounds become very frequent and small. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
number of rounds per block will be configurable and start at 1 round per block. It could be increased later if | ||
necessary for performance reasons or if round become very frequent and small. | ||
- A block *may* be zero rounds because the last block prior to a network "freeze" must communicate the state hash | ||
(via a block proof) of the state immediately prior to network shutdown. This is only possible with an extra empty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by the (via a block proof)
part here. In normal cases, the state hash is part of the block merkle tree and the block proof has an aggregated signature of the block merkle tree root (although now I realize that this has not been explicitly state in the HIP thus far, and it probably should be). Is this section saying that in a block with no rounds, the proof is simply on the hash of the state root and not a merkle tree root hash that also includes previous block root hash and null padding?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A block with no rounds is the same as any other block except for the lack of any content between block header and block proof.
The bullet point is describing a specific case where a block with no rounds may
be required, and describing how that "empty" block resolves a sequence problem.
The wording is intended to explain that, because block proof contains the state hash at the beginning of the block (end of the prior block), the state hash at end-of-block is communicated in the block stream as a field in the block proof for the following block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the state hash at end-of-block is communicated in the block stream as a field in the block proof
Aha! I had not gotten to the BlockProof proto yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think no change is needed here
HIP/hip-1056.md
Outdated
- A `Round` is a sequence of zero or more `Events` that have reached consensus at the same time. Round length in real | ||
world time depends on internet latency and event creation rate. In Hedera mainnet today it is around 1 round per | ||
second. In the future this could be shorter to lower latency, but would never make sense to be less than ping time. | ||
- An `Event` is a sequence of zero or more `EventTransactions` submitted by a single node. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A round is more than just its events, and an event is more than just its transactions. Perhaps it is simplest to say "contains" instead of "is" so we don't have to explain the metadata that comes with each.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
HIP/hip-1056.md
Outdated
These concepts are core to the Hashgraph consensus mechanism and have direct impacts on the structure of data in the | ||
streams. | ||
|
||
Block streams are built out of Items. Those items can be delivered in a continuous stream containing one or more blocks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand. If a block is made up of items, how can those items be streamed such that they contain one or more blocks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The stream contains one or more blocks, which are made up of many items (often thousands).
Agree that this wording is a little awkward at expressing that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
HIP/hip-1056.md
Outdated
|
||
Block streams are built out of Items. Those items can be delivered in a continuous stream containing one or more blocks. | ||
Or they can be delivered as a single block using the `Block` message type container. If a single block is delivered as | ||
a file it expected to be either a raw protobuf `Block` message with file name of `<BLOCK_NUMBER>.blk` e.g. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a file it expected to be either a raw protobuf `Block` message with file name of `<BLOCK_NUMBER>.blk` e.g. | |
a file it must be either a raw protobuf `Block` message with file name of `<BLOCK_NUMBER>.blk` e.g. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
HIP/hip-1056.md
Outdated
Block streams are built out of Items. Those items can be delivered in a continuous stream containing one or more blocks. | ||
Or they can be delivered as a single block using the `Block` message type container. If a single block is delivered as | ||
a file it expected to be either a raw protobuf `Block` message with file name of `<BLOCK_NUMBER>.blk` e.g. | ||
`0000000000000000001.blk` . With total of 19 digits to allow for 2^63 blocks. Or a compressed file e.g. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
incomplete sentences here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
HIP/hip-1056.md
Outdated
} | ||
``` | ||
|
||
The order of block items in a block stream is important in some cases it designates useful positions in the stream that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The order of block items in a block stream is important in some cases it designates useful positions in the stream that | |
The order of block items in a block stream is important and in some cases it designates useful positions in the stream that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
HIP/hip-1056.md
Outdated
first transaction. Block items for one block can also be packaged in a `Block` message so they can be stored in a | ||
protobuf file and served as a single entity if desired. | ||
|
||
![Block Stream Items](../assets/hip-blocks/block-stream-items.svg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some cases where multiple changes made to a singleton state type over the course of a round will be consolidated into a single change set for that singleton type for the round instead. How do those fit into the stream and block merkle tree? Do they need to be placed somewhere specific both in the stream and in the tree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question.
@jsync-swirlds or @tinker-michaelj any thoughts here?
HIP/hip-1056.md
Outdated
#### BlockHeader | ||
|
||
### BlockHeader |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate Blockheader section titles
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
De-duplicated
This is currently documented in the block node codebase, and should be included, to the extent appropriate, in the forthcoming "Block Node" HIP. That HIP will cover all of the Block Node APIs, not just the publishBlockStream API used by consensus nodes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slow progress :-)
HIP/hip-1056.md
Outdated
🚨 **Open Detail:** How do we define the `first_transaction_consensus_time` of a block with no transactions? Can it be | ||
first event consensus time if no transactions? what about empty block with no events? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some details and edge cases:
- For "normal" rounds that contain events and transactions, the round timestamp is equal to the timestamp of the last transaction in the round
- For empty rounds (no events), the round timestamp is equal to the timestamp of the last transaction in the previous round plus 1,000 nanos, rounded up to the nearest 1,000 nanos. Is there is no previous round, then the round timestamp is the median of the judge created times.
To me, #1 makes no sense at all. An event's consensus timestamp is equal to the timestamp of the FIRST transaction it contains. If the round follow the same rules, the round's timestamp would be equal to the timestamp of the first event and transaction it contains. As far as I'm aware, there is no reason we cannot change the round timestamp to work this way if we want. If we did that, we could just put the round timestamp in this protobuf instead of the first transaction timestamp and call it a day.
If we can't do that or don't want to do that, then we have some other options:
- Keep the transaction timestamp but make it nullable. If there are no transactions in the round, it is null.
- Keep the timestamp and make it always populated (with the one exception of an empty block sent after the freeze round), but for rounds with no transactions, it will be the round timestamp
- Add a second field for the round timestamp which will always be populated (with the one exception of an empty block sent after the freeze round).
None of these sound good because there are exceptions and edge cases that need to be explained.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting points.
@jasperpotts what are your thoughts here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also thanks for the clarity @poulok. Noted, those details for clarity
#### EventHeader | ||
|
||
The event header depicts the beginning of an event. All items after this and before the next `EventHeader` or | ||
`BlockProof` are part of this event or results of execution of this event’s contents. It is designed so that it is easy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't a third "event end" marker would be the non-transactional state changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no mechanism for a reader to differentiate non-transactional state changes from transactional state changes. To a reader of the Block Stream, those are just additional state changes in the event.
Is it important to differentiate those in some way? We might be missing another header if that's the case...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yes, that makes sense. I suppose the only part that is not correct is that if there are non-transaction state changes (i.e. like platform state changes or the sum of all changes to the transaction receipt queue), those are not a result of the last event's execution or contents. They are a consequence of the round as a whole. But I may be nit-picking wording here where not necessary. Feel free to close this comment if you think the distinction is not important.
HIP/hip-1056.md
Outdated
|
||
The event header depicts the beginning of an event. All items after this and before the next `EventHeader` or | ||
`BlockProof` are part of this event or results of execution of this event’s contents. It is designed so that it is easy | ||
to reconstruct `GossipEvent`’s as needed for hashgraph reconstruction from the contents in the block stream. To make |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to reconstruct `GossipEvent`’s as needed for hashgraph reconstruction from the contents in the block stream. To make | |
to reconstruct `GossipEvent`s as needed for hashgraph reconstruction from the contents in the block stream. To make |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
HIP/hip-1056.md
Outdated
of user and system transactions, and matches the structure of Events within the hashgraph algorithm. A state signature | ||
system transaction is a non-user transaction internally created to carry out a state change in conformance with network | ||
hashgraph logic. This transaction does not count towards network TPS and can only be issued internally via the consensus | ||
platform. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's future proof this a bit
platform. | |
node software. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
The block stream supports complete reconstruction of the consensus event stream in a simple and secure manner. Any | ||
entity wishing to reconstruct the event stream may begin with the `EventHeader` and append each following | ||
`EventTransaction` *in order* as it appears in the Block Stream. The event ends when the next `EventHeader` or | ||
`Block Proof` is encountered in the stream. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or non-transactional state changes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this related to your previous question.
Following a non-transaction state change should still be an EventHeader
or a Block Proof
Signed-off-by: Nana Essilfie-Conduah <[email protected]>
…ick and Joseph Signed-off-by: Nana Essilfie-Conduah <[email protected]>
Signed-off-by: Nana Essilfie-Conduah <[email protected]>
HIP/hip-1056.md
Outdated
message BlockItem { | ||
oneof item { | ||
/** | ||
* An header for the block, marking the start of a new block. | ||
*/ | ||
com.hedera.hapi.block.stream.output.BlockHeader block_header = 1; | ||
|
||
/** | ||
* An header emitted at the start of a new network "event". | ||
*/ | ||
com.hedera.hapi.block.stream.input.EventHeader event_header = 2; | ||
|
||
/** | ||
* An header emitted at the start of a new consensus "round". | ||
* <p> | ||
* This item SHALL contain the properties relevant to a single | ||
* consensus round. | ||
*/ | ||
com.hedera.hapi.block.stream.input.RoundHeader round_header = 3; | ||
|
||
/** | ||
* A single transaction. | ||
*/ | ||
com.hedera.hapi.platform.event.EventTransaction event_transaction = 4; | ||
|
||
/** | ||
* The result of running a transaction. | ||
*/ | ||
com.hedera.hapi.block.stream.output.TransactionResult transaction_result = 5; | ||
|
||
/** | ||
* A transaction output. | ||
*/ | ||
com.hedera.hapi.block.stream.output.TransactionOutput transaction_output = 6; | ||
|
||
/** | ||
* A set of state changes. | ||
*/ | ||
com.hedera.hapi.block.stream.output.StateChanges state_changes = 7; | ||
|
||
/** | ||
* Verification data for an item filtered from the stream.<br/> | ||
* This is a hash for a merkle tree node where the contents of that | ||
* part of the merkle tree have been removed from this stream. | ||
*/ | ||
FilteredItemHash filtered_item_hash = 8; | ||
|
||
/** | ||
* A signed block proof.<br/> | ||
* The signed merkle proof for this block. This will validate | ||
* a "virtual" merkle tree containing the previous block "virtual" | ||
* root, an "input" subtree, an "output" subtree, and | ||
* a "state changes" subtree. | ||
*/ | ||
BlockProof block_proof = 9; | ||
|
||
/** | ||
* A record file and associated data. | ||
*/ | ||
RecordFileItem record_file = 10; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "fixed" indent is using the wrong settings:
- indentation should be 4 spaces, not 2
- comments should line up on
*
, so every line after the first should be indented one extra space.
Example
/**
* A signed block proof.<br/>
* The signed merkle proof for this block. This will validate
* a "virtual" merkle tree containing the previous block "virtual"
* root, an "input" subtree, an "output" subtree, and
* a "state changes" subtree.
*/
BlockProof block_proof = 9;
/**
* A record file and associated data.
*/
RecordFileItem record_file = 10;
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got up to BlockHeader with the above. Will circle back to complete
HIP/hip-1056.md
Outdated
- [ ] Q: Are the current block sizes acceptable to not need to adopt an entity state changes splitting strategy? | ||
- [ ] Q: Is it clear which block contains a block proof and state hash regarding that block? | ||
- Currently a block will contain it's own proof. The state hash a block contains however is the hash at the begining of the block/end | ||
of the previous block. The state hash representing the changes to state made by a given block are found in the next block. | ||
State proofs for block N thus require the state following block N, and the block proof from block N+1. | ||
- [ ] Q: There are some cases where multiple changes made to a singleton state type over the course of a round will be consolidated into a single change set for that singleton type for the round instead. How do those fit into the stream and block merkle tree? Do they need to be placed somewhere specific both in the stream and in the tree? | ||
- [ ] Q: Should TransferList be included in the TransactionResult object or not. It seems closer to an input and output. | ||
- Couldn't a MN that parses the block stream take changes to the transferList in the TransactionResult? | ||
There are considerations for emergent transfer logic that isn’t in the transaction body which may prevent this. | ||
- [ ] Q: Is the current algorithm for Block hash calculation accurate based on latest services explorations? | ||
- [ ] Q: Should we include a string error message in the block stream to give improved DexExp when troubleshooting transaction failures? | ||
- [ ] Q: Are redaction and errata and how FilteredBlockItem can be used to achieve them adequately described? | ||
- [ ] Q: Can we change EthereumOutput to EVMOutput? it seems more accurate to cover EVM transactions | ||
- An argument against this is that Outputs match to the transaction input. Since there's a ContractCall, ContractCreate and | ||
EThereumTransaction it would be inconsistent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- [ ] Q: Are the current block sizes acceptable to not need to adopt an entity state changes splitting strategy? | |
- [ ] Q: Is it clear which block contains a block proof and state hash regarding that block? | |
- Currently a block will contain it's own proof. The state hash a block contains however is the hash at the begining of the block/end | |
of the previous block. The state hash representing the changes to state made by a given block are found in the next block. | |
State proofs for block N thus require the state following block N, and the block proof from block N+1. | |
- [ ] Q: There are some cases where multiple changes made to a singleton state type over the course of a round will be consolidated into a single change set for that singleton type for the round instead. How do those fit into the stream and block merkle tree? Do they need to be placed somewhere specific both in the stream and in the tree? | |
- [ ] Q: Should TransferList be included in the TransactionResult object or not. It seems closer to an input and output. | |
- Couldn't a MN that parses the block stream take changes to the transferList in the TransactionResult? | |
There are considerations for emergent transfer logic that isn’t in the transaction body which may prevent this. | |
- [ ] Q: Is the current algorithm for Block hash calculation accurate based on latest services explorations? | |
- [ ] Q: Should we include a string error message in the block stream to give improved DexExp when troubleshooting transaction failures? | |
- [ ] Q: Are redaction and errata and how FilteredBlockItem can be used to achieve them adequately described? | |
- [ ] Q: Can we change EthereumOutput to EVMOutput? it seems more accurate to cover EVM transactions | |
- An argument against this is that Outputs match to the transaction input. Since there's a ContractCall, ContractCreate and | |
EThereumTransaction it would be inconsistent. | |
- [ ] Q: Is the current compression approach sufficient to maintain manageable | |
block size and avoid adopting an entity state changes splitting strategy? | |
- [ ] Q: Are there additional protocol buffer changes needed to reduce the | |
stream overhead for Events, Transactions, or other frequent entities. | |
- [ ] Q: Is it clear which block contains a block proof and state hash regarding | |
that block? | |
- A block will contain it's own proof. The state hash a block contains | |
however is the hash at the begining of the block/end of the previous block. | |
The state hash representing the changes to state made by a given block are | |
found in the next block. State proofs after block N thus require the state | |
at the end of block N, and the block proof from block N+1. | |
- [ ] Q: There are some cases where multiple changes made to a singleton state | |
type over the course of a round will be consolidated into a single change | |
set for that singleton type for the round instead. How do those fit into | |
the stream and block merkle tree? Do they need to be placed somewhere | |
specific both in the stream and in the tree? | |
- Actually, it's possible these must be reported multiple times instead of | |
holding them to a single later update. The reason being that out-of-order | |
state changes, or skipped state changes could produce a different total | |
order of updates, and change the state hash.<br/> This needs to be verified | |
with the services, platform, and platform-data experts. | |
- [ ] Q: Should TransferList be included in the TransactionResult object or not. | |
It contains redundant data, but makes mirror node processing much simpler. | |
- Could a MN that parses the block stream reconstruct the transfer list from | |
state changes?<br/> There are considerations for emergent transfer logic | |
that isn’t in the transaction body which may prevent this. | |
- [ ] Q: Is the current algorithm for Block hash calculation accurate based on | |
latest services explorations? | |
- [ ] Q: Should we include a string error message in the block stream to give | |
improved DexExp when troubleshooting transaction failures? | |
- Should we, alternatively, offer a detail error _number_, defined on a | |
per-result-code basis, and published as a set of enumerations? This | |
numeric option would greatly reduce extra data in the block stream and | |
avoid unclear or "bit-rotted" error strings. | |
- [ ] Q: Are redaction and errata and how FilteredBlockItem can be used to | |
achieve them adequately described? | |
- [ ] Q: Can we change EthereumOutput to EVMOutput? it seems more accurate to | |
cover EVM transactions | |
- An argument against this is that Outputs match to the transaction input. | |
Since there's a ContractCall, ContractCreate and EthereumTransaction it | |
would be inconsistent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adopted.
@jsync-swirlds I also added the following. Does that sound correct?
- Q: Is it clear which block contains a block proof and state hash regarding
that block?
...
Verified state proofs for changes in block N thus require the state hash in block N+1.
HIP/hip-1056.md
Outdated
In the past the exchange_rate details were in the transaction record. However, this information doesn't change often and is based on | ||
the value of a system file. Changes to this file are externalized to the MN and thus the current exchange rate value is always available | ||
via a MN (or future BN). Thus the exchange rate is readily available from the MN when using the transaction timestamp as input. | ||
This allowed us to removing it and reduce unnecessary data from the block stream. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the past the exchange_rate details were in the transaction record. However, this information doesn't change often and is based on | |
the value of a system file. Changes to this file are externalized to the MN and thus the current exchange rate value is always available | |
via a MN (or future BN). Thus the exchange rate is readily available from the MN when using the transaction timestamp as input. | |
This allowed us to removing it and reduce unnecessary data from the block stream. | |
In the past the exchange_rate details were in the transaction record. | |
However, this information doesn't change often and is based on the value | |
of a system file. Changes to this file are also externalized in the | |
Block Stream, thus the current exchange rate value is available in the state | |
changes and we can remove it from the `TransactionResult` to reduce unnecessary | |
data in the block stream. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the change to got from 120 chars in width to 80 right? I'm gonna have to do it for the whole doc at some point.
Personally I prefer the 120 length. For a doc like this 80 is gonna make it really long 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I see change removing the MN inclusion.
Is there a reason we don't want to note the data availability in the MN?
We can definitely update it to highlight it's availability in the stream, and BN and MN states
* <p> | ||
* These fees SHALL be present in the full transfer list for the transaction. | ||
*/ | ||
repeated proto.AssessedCustomFee assessed_custom_fees = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HIP-991 will enable custom fees for topics, and most likely this field will be used to externalize the custom fees charged for a consensus submit message transaction. Should we take the opportunity and rename CryptoTransferOutput
to be generic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is probably more appropriate, as part of HIP-991
, to add assessed_custom_fees
to SubmitMessageOutput
and include that message in TransactionOutput
(to be generated when a consensus submit charges custom fees).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Xin is saying that since now AssessedCustomFee
will appear in at least two transaction types and probably more in the future, instead of adding it to each different output we should consider pulling it up to the TransactionResult
since it's more common. This is important to consider now, not in HIP-991 since it's a breaking change later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The design thus far has been to add outputs for a given transaction to a transaction-specific output (for the few transactions that have outputs).
Changing that, at least in this case, would make it much harder to filter the block stream to, for example, only show the activity for consensus service transactions, as you would also need to read all of a "generic" set of outputs and tie them to specific transactions (so the filter would need to be stateful).
It's certainly an option, but I would suggest that maximizing the options for stateless filters in the Block Stream is worth the minor duplication of concepts (no data would be duplicated, of course).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once an output starts appearing on more than one transaction type and if we think it might appear in more we should consider moving it to TransactionResult
. This is already the case for fields in the TransactionResult
and we'd be following the same model. TokenTransferList
appears in the result but it only applies to TokenReject
, TokenAirdrop
, and CryptoTransfer
. TokenAssociation
in the result only applies to TokenCreate
and CryptoTransfer
. There is strong precedent to consider AssessedCustomFee
to be the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not how I view it, but it's entirely possible my view is not correct.
As I view these items, TransactionResult
is mostly items that are global to all transactions, as well as a few items that are currently expected by Mirror Node in the record stream but would be difficult to move into individual transaction results.
Ideally, in my opinion, we would move those "extra" items into individual transaction results, or state changes, and TransactionResult
would be reduced to just the properly "global" items like status, consensus timestamps, congestion multiplier, and transaction fee; perhaps paid staking rewards (not sure if that's universal or specific) as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are valid considerations.
Personally I wish TransactionResult
was not needed or at the least was exactly as @jsync-swirlds noted and didn't include transfer_list
, token_transfer_lists
or automatic_token_associations
which are present because they are results from transactions that differ from their usual associated transaction type.
@steven-sheehy and @xin-hedera are right that there's a precedence here to add assessed_custom_fees
.
My concern is what else does that open the door and how do we ensure consistency and ability to manage future pivots.
However, if we rely on future move of items in a specific output type to the general TransactionResult
we might require multiple deprecations of those fields to avoid duplication. For instance if Block Stream were already in use we'd now be considering how to stop using CryptoTransferOutput.assessed_custom_fees
and how to start using TransactionResult.assessed_custom_fees
. This will certainly happen again for other fields as HAPI continues to mature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jsync-swirlds how does the inclusion of assessed_custom_fees
impact filtering functionality in a way different from say token_transfer_lists
or automatic_token_associations
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@steven-sheehy what's the downside if assessed_custom_fees
appeared in both CryptoTransferOutput
and TokenAirdropOutput
? Is it just the duplication the protobuf and the parsing consideration to look in more than one object for the same value depending on transaction type? Is this better as it ensure transaction types are more self contained?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Token transfer list and automatic associations are both present elsewhere; their inclusion here is duplicative, and actually very hard to remove if a redaction were to become necessary. Both of the mentioned current fields are already a problem for redaction and filtering. Basically we can redact whole messages and preserve the block proof, but if we change a single message (say, to remove custom fees related to a redacted message), the block proof will not validate, so we could remove the message that related to a custom fee (perhaps an HCS submit), but could not remove the related custom fees from this message, which limits how thoroughly we could comply with a redaction request.
For general filtering it's possible to filter out the state change, transaction, and outputs that duplicate the data here; but this data would likely remain in the stream unless this output message was also filtered (which would require a complex filter, and associated higher charge).
Part of the reason for being very careful to minimize fields in this message is to reduce cases where redaction is limited and filtering is more complex.
HIP/hip-1056.md
Outdated
Or they can be delivered as a single block using the `Block` message type container. If a single block is delivered as | ||
a file it must be either a raw protobuf `Block` message with file name of `<BLOCK_NUMBER>.blk` e.g. | ||
`0000000000000000001.blk`. The file name contains a total of 19 digits to allow for 2^63 blocks. The file format may | ||
also be a compressed file e.g. `0000000000000000001.blk.gz` or `0000000000000000001.blk.ztsd`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The common extension for zstd is .zst
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is merely an example, and probably shouldn't be in this HIP at all, given the storage of files (or use of files at all) is an implementation detail of a Block Node that should not actually be exposed in the API.
HIP/hip-1056.md
Outdated
`0000000000000000001.blk`. The file name contains a total of 19 digits to allow for 2^63 blocks. The file format may | ||
also be a compressed file e.g. `0000000000000000001.blk.gz` or `0000000000000000001.blk.ztsd`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is ambiguous. The HIP presents three possible names for the block file in the bucket: 0000000000000000001.blk, 0000000000000000001.blk.gz, or 0000000000000000001.blk.zst. Since the filename is the API for how mirror nodes will consume it from the bucket, we have to explicitly pick one of these. Consensus nodes will only upload one of these three formats and if mirror nodes pick the wrong format then it won't work or it has to waste effort and try all 3 names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The file name is not an API. We'll work out the temporary storage of files in buckets independent of the Block Stream HIP, as the HIP should neither mandate nor envision file-in-bucket storage as a long-term solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just got out of a meeting where we discussed phase 1 being block streams in the bucket as the canonical source. So the file name will very much be an API if that's the case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say we need to reconsider that, as the block stream is not a file, definitionally.
If we want a separate HIP, that offers temporary public access, then that might make sense, but making a temporary transitional access part of the permanent public API is not what I would consider well designed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe there's a needed separation of the API the HIP is proposing and the API the team requires to achieve this goal in the interim to meet a schedule.
MNs consuming buckets is a project phasing optimization to get this out soon but is not an intention of this HIPs specification nor of long term usage.
For instance a non Hedera run Mirror Node would consider this HIP functional and implemented when they can read from the block stream and should hopefully never have to pull from the cloud bucket.
However, for the project I do believe we must commit to one file format so MNs early on can commit to one format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, @steven-sheehy's suggestion is fair.
Assuming the suggestion is CN will really have 2 output formats for Block Items ( (i) block streams and ii) BlockFile for special cases) and we should specify both for clarity.
@jasperpotts what do you think?
HIP/hip-1056.md
Outdated
Or they can be delivered as a single block using the `Block` message type container. If a single block is delivered as | ||
a file it must be either a raw protobuf `Block` message with file name of `<BLOCK_NUMBER>.blk` e.g. | ||
`0000000000000000001.blk`. The file name contains a total of 19 digits to allow for 2^63 blocks. The file format may | ||
also be a compressed file e.g. `0000000000000000001.blk.gz` or `0000000000000000001.blk.ztsd`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In addition to the filename, this HIP needs to define the path in the bucket where this file will reside. I suggest /blocks/0000000000000000001.blk.zst
but open to alternatives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be defined in the HIP, as we should not be storing these files in public buckets long-term.
The Block Nodes will provide the blocks via an API call, as a bytestream, based on block number; not as files.
Alternatively, for clients like mirror node, block stream data should (mostly) be obtained from the stream API, not as whole blocks.
HIP/hip-1056.md
Outdated
Or they can be delivered as a single block using the `Block` message type container. If a single block is delivered as | ||
a file it must be either a raw protobuf `Block` message with file name of `<BLOCK_NUMBER>.blk` e.g. | ||
`0000000000000000001.blk`. The file name contains a total of 19 digits to allow for 2^63 blocks. The file format may |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somewhere we need to define which buckets the data will reside. It should hopefully just be in the same buckets as the current record streams just in different paths.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Different lifecycle, different access (never public, or only temporarily public), and therefore different buckets.
Also, it's not, in my opinion, properly part of the HIP; it's an internal decision for any entity that wishes to run a private block node to create a (private) archival bucket.
@@ -0,0 +1,4 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the bottom left diagram under Root of Input Merkle Tree the one describing input block items.
I guess the most bottom left Output Block Items Merkle Tree should be renamed to Input Block Items Merkle Tree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I understand you.
Yes, the added label beneath the input items subtree should be corrected:
Output Block Items Merkle Tree
Input Block Items Merkle Tree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've fixed this
…en-sheehy, xin-hedera, tinker-michaelj, derektriley and IvanKavaldzhiev Signed-off-by: Nana Essilfie-Conduah <[email protected]>
* This SHALL be the timestamp assigned by the hashgraph consensus | ||
* algorithm to the first transaction of this block. | ||
*/ | ||
proto.Timestamp first_transaction_consensus_time = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jasperpotts & @jsync-swirlds for a block with an empty round there will be no transactions so should we rename this property to be more general?
We'd then note that normally it's the first transaction consensus timestamp and in an empty round case it's equal to the last transaction that reached consensus (in the previous round) plus 1,000 nanos, rounded up to the nearest 1,000 nanos (thanks @poulok for the summary).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a bit tricky, in my view.
- It's hard to see what the name should be if we change it.
- Ideally such empty rounds will be vanishingly rare (I, for one, certainly hope that network usage rises to the point that we never actually see one).
- Also, this time could be seen as a "virtual" first transaction time for an empty round (essentially lock-step incrementing consensus time 1 microsecond at a time until the network has real transactions that set the next consensus time).
Not sure the best choice. We could rename to round_start_consensus_time
, but I'm not sure that's accurate either.
HIP/hip-1056.md
Outdated
|
||
With the record stream design nodes had to independently upload record streams and their signatures files. | ||
These were stored in varied public cloud buckets and anyone in the world could download the files | ||
e.g. Mirror Nodes do this today to support queries. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
HIP/hip-1056.md
Outdated
- **Signature Files**: Each node currently produces a v6 record stream with a corresponding signature file for | ||
verification. Mirror nodes must download a strong minority (1/3 of consensus weight) worth of these signature files, | ||
along with the record file, to verify v6 records. This frequent access to cloud storage (GCS/S3) is costly. To reduce | ||
this expense and complexity, blocks include a TSS signature from the majority weight of consensus nodes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
* <p> | ||
* These fees SHALL be present in the full transfer list for the transaction. | ||
*/ | ||
repeated proto.AssessedCustomFee assessed_custom_fees = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@steven-sheehy what's the downside if assessed_custom_fees
appeared in both CryptoTransferOutput
and TokenAirdropOutput
? Is it just the duplication the protobuf and the parsing consideration to look in more than one object for the same value depending on transaction type? Is this better as it ensure transaction types are more self contained?
HIP/hip-1056.md
Outdated
`0000000000000000001.blk`. The file name contains a total of 19 digits to allow for 2^63 blocks. The file format may | ||
also be a compressed file e.g. `0000000000000000001.blk.gz` or `0000000000000000001.blk.ztsd`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe there's a needed separation of the API the HIP is proposing and the API the team requires to achieve this goal in the interim to meet a schedule.
MNs consuming buckets is a project phasing optimization to get this out soon but is not an intention of this HIPs specification nor of long term usage.
For instance a non Hedera run Mirror Node would consider this HIP functional and implemented when they can read from the block stream and should hopefully never have to pull from the cloud bucket.
However, for the project I do believe we must commit to one file format so MNs early on can commit to one format.
HIP/hip-1056.md
Outdated
`0000000000000000001.blk`. The file name contains a total of 19 digits to allow for 2^63 blocks. The file format may | ||
also be a compressed file e.g. `0000000000000000001.blk.gz` or `0000000000000000001.blk.ztsd`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, @steven-sheehy's suggestion is fair.
Assuming the suggestion is CN will really have 2 output formats for Block Items ( (i) block streams and ii) BlockFile for special cases) and we should specify both for clarity.
@jasperpotts what do you think?
@@ -0,0 +1,4 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've fixed this
Signed-off-by: Nana Essilfie-Conduah <[email protected]>
Signed-off-by: Nana Essilfie-Conduah <[email protected]>
Signed-off-by: Nana Essilfie-Conduah <[email protected]>
Signed-off-by: Nana Essilfie-Conduah <[email protected]>
Has there been any research/discussion into using/implementing this as a substream https://thegraph.com/blog/subgraphs-substreams-firehose-explained/? Not saying that firehose/substreams are "the" standard, but it would allow integration into TheGraph/Subgraphs, which is (I think) decently popular. |
There's a typo in the HIP: "For emptu rounds with no eents" |
There has been some limited thought in this area; the Block Stream cannot initially be delivered in this format and still be verifiable and self-contained, but it might be possible in the future. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few possible improvements.
Merkle Item Order vs Block Item Order: The order in which items appear in the block merkle tree are not the same | ||
order in which are streamed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't entirely accurate. The left-to-right order of merkle leaves must be the order in which those items are encountered in the stream. The only change is that some items are in the "input" subtree and some are in the "output" subtree, but within those trees the order will need to be the same as stream-order.
changes of the block and hash them, waiting for this process would result in the block creation time being held up. | ||
Quick block times is a goal of the block stream and thus a tradeoff is made to ensure fast block times. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor wording adjustment:
changes of the block and hash them, waiting for this process would result in the block creation time being held up. | |
Quick block times is a goal of the block stream and thus a tradeoff is made to ensure fast block times. | |
changes of the block and hash them, waiting for this process would result in the block delivery time being held up. | |
rapid block delivery is a goal of the block stream and thus a tradeoff is made to ensure minimum latency. |
To make each block within a block stream independently verifiable, the use of an aggregated signature is leveraged. | ||
Aggregated signatures allow the verification of multiple node signatures to be processed using a single public key. | ||
Therefore, a user can verify that a block was signed by a sufficient stake weight within the network by verifying a single | ||
aggregated signature. TSS-BLS signatures were selected because of their ability to support partial share signatures | ||
assigned on a weighted number of shares per node with a threshold value required for verification. Additionally, | ||
adopting TSS-BLS allows the network to utilize a single semi-permanent public ledger id to verify the signature. This | ||
offers a valuable system optimization as no matter how much the network node membership changes, the same ledger id can | ||
be used to verify the aggregated signature. TSS-BLS signatures are also reasonably efficient when used within smart | ||
contracts. A state proof based on this signature can be verified by a smart contract on another ledger for little cost. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small changes for readability
To make each block within a block stream independently verifiable, the use of an aggregated signature is leveraged. | |
Aggregated signatures allow the verification of multiple node signatures to be processed using a single public key. | |
Therefore, a user can verify that a block was signed by a sufficient stake weight within the network by verifying a single | |
aggregated signature. TSS-BLS signatures were selected because of their ability to support partial share signatures | |
assigned on a weighted number of shares per node with a threshold value required for verification. Additionally, | |
adopting TSS-BLS allows the network to utilize a single semi-permanent public ledger id to verify the signature. This | |
offers a valuable system optimization as no matter how much the network node membership changes, the same ledger id can | |
be used to verify the aggregated signature. TSS-BLS signatures are also reasonably efficient when used within smart | |
contracts. A state proof based on this signature can be verified by a smart contract on another ledger for little cost. | |
To make each block within a block stream independently verifiable, we make use of | |
an aggregated signature. | |
Aggregated signatures allow the verification of a single signature that can | |
only be produced by cryptographically aggregating signatures from nodes | |
representing a sufficient stake weight of the network. Therefore, any entity can | |
verify that a block was signed by a sufficient stake weight within the network | |
with a single signature verification, significantly reducing complexity. | |
TSS-BLS signatures are selected for this purpose because of the ability to | |
support partial share signatures assigned as a weighted number of shares per | |
node with a threshold value required for verification. | |
Additionally, adopting TSS-BLS allows the network to provide a single semi- | |
permanent public ledger id, which is the public key needed to verify the network | |
signature. This offers a valuable system optimization as no matter how much the | |
network node membership changes, the same ledger id can be used to verify | |
the aggregated signature. TSS-BLS signatures are also reasonably efficient | |
when used within smart contracts. A state proof based on this signature can be | |
verified by a smart contract on another ledger for little cost when compared | |
to many other alternatives. |
Notably the block stream will enable 3 types of proof | ||
1. Block Proof - This proof proves the blockchain and illustrats that given a starting block and it proof, a given | ||
set of transactions inpput and outputs as well as resulting state changes had a final merkle root hash that was agreed | ||
upon my the majority stake weight of the network. | ||
2. Block Item Proof - This proof proves that a given object was present in a Block. This is just as powerful as noting | ||
a value was in state. | ||
3. State Proof - This proof proves a given state value at a given block number was agreed upon my the majority stake | ||
weight of the network. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spelling corrections
Notably the block stream will enable 3 types of proof | |
1. Block Proof - This proof proves the blockchain and illustrats that given a starting block and it proof, a given | |
set of transactions inpput and outputs as well as resulting state changes had a final merkle root hash that was agreed | |
upon my the majority stake weight of the network. | |
2. Block Item Proof - This proof proves that a given object was present in a Block. This is just as powerful as noting | |
a value was in state. | |
3. State Proof - This proof proves a given state value at a given block number was agreed upon my the majority stake | |
weight of the network. | |
Notably the block stream will enable 3 types of proof | |
1. Block Proof - This proof proves the blockchain and illustrates that given a | |
starting block and its proof, a given set of transaction inputs and outputs | |
as well as resulting state changes and a final merkle root hash was agreed | |
upon my the majority stake weight of the network. | |
2. Block Item Proof - This proof proves that a given object was present in a | |
Block. This is just as powerful as proving a value was in state. | |
3. State Proof - This proof proves a given state value at a given block number | |
was agreed upon by a majority stake weight of the network. |
| I submitted a transaction in block-number `x`. I want to prove that. | Block Item Proof (`x`) | | ||
| I submitted a transaction in block-number `x`. I want to prove my balance after that transaction. | Block Item Proof (`x`) | | ||
| I submitted a transaction in block-number `x`. I didn’t make any transaction until block-number y; y >> x. I want to prove my balance as of block-number y. | State Proof (`y`) | | ||
| I sent an HCS message to a topic in block-number `x`. I want to prove that my message was included in the topic. | Block Item Proof (`x`) | | ||
| I sent an HCS message to a topic in block-number `x`. Nobody sent any messages to that topic until block-number y; y >> x. I want to prove that my message was included in the topic | Block Item Proof (`x`) | | ||
| I sent an HCS message to a topic in block-number x. Many people sent many messages to that topic until block-number y; y >> x. I want to prove that my original message at block-number x was included in the topic. | Block Item Proof (`x`) | | ||
| I executed a Smart Contract call in block-number x. I want to prove that *all* actions triggered by that smart contract (e.g. other contracts it called, HTS or Account transactions it generated) took place | Block Item Proof(s) (`x`) | | ||
| I submitted (or signed) a scheduled transaction in block-number x. My scheduled-transaction executed as in block-number y; y >> x. I want to prove that my transaction executed at y. | Block Item Proof (`x`) / State Proof (`y`) | | ||
| I submitted (or signed) a scheduled transaction in block-number x. My scheduled-transaction expired because it didn’t get sufficient signatures. I want to prove that my scheduled transaction expired at y. | Block Item Proof (`x`) / State Proof (`y`) | | ||
| I did a transaction in block-number x. I didn’t make any transaction until block-number y; y >> x. My entity (account, contract, file, token, topic, or schedule) expired at y. I want to prove that my entity expired at y. | Block Item Proof (`x`) | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spelling corrections, wording adjustments, a couple proof type corrections, and two added scenarios.
| I submitted a transaction in block-number `x`. I want to prove that. | Block Item Proof (`x`) | | |
| I submitted a transaction in block-number `x`. I want to prove my balance after that transaction. | Block Item Proof (`x`) | | |
| I submitted a transaction in block-number `x`. I didn’t make any transaction until block-number y; y >> x. I want to prove my balance as of block-number y. | State Proof (`y`) | | |
| I sent an HCS message to a topic in block-number `x`. I want to prove that my message was included in the topic. | Block Item Proof (`x`) | | |
| I sent an HCS message to a topic in block-number `x`. Nobody sent any messages to that topic until block-number y; y >> x. I want to prove that my message was included in the topic | Block Item Proof (`x`) | | |
| I sent an HCS message to a topic in block-number x. Many people sent many messages to that topic until block-number y; y >> x. I want to prove that my original message at block-number x was included in the topic. | Block Item Proof (`x`) | | |
| I executed a Smart Contract call in block-number x. I want to prove that *all* actions triggered by that smart contract (e.g. other contracts it called, HTS or Account transactions it generated) took place | Block Item Proof(s) (`x`) | | |
| I submitted (or signed) a scheduled transaction in block-number x. My scheduled-transaction executed as in block-number y; y >> x. I want to prove that my transaction executed at y. | Block Item Proof (`x`) / State Proof (`y`) | | |
| I submitted (or signed) a scheduled transaction in block-number x. My scheduled-transaction expired because it didn’t get sufficient signatures. I want to prove that my scheduled transaction expired at y. | Block Item Proof (`x`) / State Proof (`y`) | | |
| I did a transaction in block-number x. I didn’t make any transaction until block-number y; y >> x. My entity (account, contract, file, token, topic, or schedule) expired at y. I want to prove that my entity expired at y. | Block Item Proof (`x`) | | |
| I submitted a transaction in block-number `x`. I want to prove that transaction. | Block Item Proof (`x`) | | |
| I submitted a transaction in block-number `x`. I want to prove my balance after that transaction. | Block Item Proof (`x`) | | |
| I submitted multiple transactions in block-number `x`. I want to prove my balance after _each_ transaction. | Block Item Proof (`x`), one per transaction | | |
| I submitted a transaction in block-number `x`. I didn’t submit any further transactions until after block-number y; y >> x. I want to prove my balance as of block-number y. | State Proof (`y`) | | |
| I sent an HCS message to a topic in block-number `x`. I want to prove that my message was written to the topic. | Block Item Proof (`x`) | | |
| I sent an HCS message to a topic in block-number `x`. Nobody sent any messages to that topic until block-number y; y >> x. I want to prove that my message was written to the topic | Block Item Proof (`x`) | | |
| I sent an HCS message to a topic in block-number x. Many people sent many messages to that topic through block-number y; y >> x. I want to prove that my original message at block-number x was written to the topic. | Block Item Proof (`x`) | | |
| I executed a Smart Contract call in block-number x. I want to prove that *all* actions triggered by that smart contract (e.g. other contracts it called, HTS or Account transactions it generated) took place | Block Item Proof(s) (`x`) | | |
| I submitted (or signed) a scheduled transaction in block-number x. My scheduled-transaction executed in block-number y; y >> x. I want to prove that my transaction was submitted/signed at x _and_ executed at y. | Block Item Proof (`x`) / Block Item Proof (`y`) | | |
| I submitted (or signed) a scheduled transaction in block-number x. My scheduled-transaction expired because it didn’t get sufficient signatures. I want to prove that my scheduled transaction was signed/submitted at x _and_ expired at y. | Block Item Proof (`x`) / Block Item Proof (`y`) | | |
| I submitted a transaction in block-number x. I didn’t submit any further transactions until block-number y; y >> x. My entity (account, contract, file, token, topic, or schedule) expired at y. I want to prove that my entity expired at y. | Block Item Proof (`y`) | | |
| I do not know the last transaction affecting an entity (Account, Smart Contract, File, Token, etc...). I want to prove the state of that entity as of block-number x. | State Proof (`x`) | |
All items in the block other than the final `BlockProof` and `FilteredItemHash` are either Input or Output items. This | ||
separation is important as they are hashed into two different sub-merkle-trees in the final proof. We did not want to | ||
carry in each block item data representing which tree it was hashed in as that would waste a lot of bytes. So which tree | ||
is defined by the protobuf package that message type is in. The reason they are split into input and output item sets is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is defined by the protobuf package that message type is in. The reason they are split into input and output item sets is | |
is defined _both_ by the protobuf package that message type is in and by a | |
convention for the _field number_ assigned to the item in this message. | |
The reason they are split into input and output item sets is |
this opens the door for using the block stream format to describe the data passed from consensus to transaction | ||
processing. | ||
|
||
The division into block items also enables filtering of data based on data type. For example a downstream services could |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The division into block items also enables filtering of data based on data type. For example a downstream services could | |
The division into block items also enables filtering of data based on data type or | |
fine-grained content filters. For example a downstream services could |
* This SHALL be the timestamp assigned by the hashgraph consensus | ||
* algorithm to the first transaction of this block. | ||
*/ | ||
proto.Timestamp first_transaction_consensus_time = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a bit tricky, in my view.
- It's hard to see what the name should be if we change it.
- Ideally such empty rounds will be vanishingly rare (I, for one, certainly hope that network usage rises to the point that we never actually see one).
- Also, this time could be seen as a "virtual" first transaction time for an empty round (essentially lock-step incrementing consensus time 1 microsecond at a time until the network has real transactions that set the next consensus time).
Not sure the best choice. We could rename to round_start_consensus_time
, but I'm not sure that's accurate either.
/** | ||
* Version of the active address book of the network at the time relating to the network public key | ||
*/ | ||
proto.SemanticVersion address_book_version = 7; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is removed from the latest updates.
<aside> | ||
Timestamp on empty round: Currently, for rounds with events and transactions, the round timestamp is equal to the | ||
timestamp of the last transaction in the round. For emptu rounds with no eents, the round timestamp is equal to the | ||
timestamp of the last transaction in the previous round plus 1,000 nanos, rounded up to the nearest 1,000 nanos. | ||
If there is no previous round, then the round timestamp is the median of the judge created times. | ||
This logic disticts the value of the 1BlockHeader` item `first_transaction_consensus_time` property. | ||
</aside> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor update
<aside> | |
Timestamp on empty round: Currently, for rounds with events and transactions, the round timestamp is equal to the | |
timestamp of the last transaction in the round. For emptu rounds with no eents, the round timestamp is equal to the | |
timestamp of the last transaction in the previous round plus 1,000 nanos, rounded up to the nearest 1,000 nanos. | |
If there is no previous round, then the round timestamp is the median of the judge created times. | |
This logic disticts the value of the 1BlockHeader` item `first_transaction_consensus_time` property. | |
</aside> | |
<blockquote><aside> | |
Timestamp on empty round: Currently, for rounds with events and transactions, | |
the round timestamp is equal to the timestamp of the last transaction in the | |
round. For empty rounds with no events, the round timestamp is equal to the | |
timestamp of the last transaction in the previous round plus 1,000 nanoseconds, | |
rounded up to the nearest 1,000 nanoseconds. | |
If there is no previous round, then the round timestamp is the median of the | |
judge created times. This logic derives the value of the `BlockHeader` item | |
`first_transaction_consensus_time` property. | |
</aside></blockquote> |
Abstract
This HIP introduces a new output data format for consensus nodes, called Block Streams, that replaces the existing
event streams, record streams, state files, and signature files with one single stream.
Each block within the block stream is a self-contained entity, including every event and all transactions that were
part of the block, the state changes as a result of those transactions, and a
network signature (using a BLS threshold signature scheme we call TSS) that proves the block was signed by a
threshold of the network.
By including state changes, downstream services can seamlessly maintain and verify state alongside consensus nodes. This
enhancement fosters greater transparency and trust within the Hedera network. Any downstream service can independently
rebuild and verify the state of consensus nodes at the time of the block, verified by the TSS network signature. Using
this state, they can provide additional services such as state proofs, state snapshots, and more.
With the event information within blocks, downstream services can be reconstructed and the hashgraph algorithm replayed,
permitting anyone to verify the correct execution of the consensus algorithm and removes the need for the extra event
stream. In doing so Hedera users gain comprehensive visibility into network activities through an easily consumable
format that can be delivered with low latency.
A key design criteria is for the block stream to be easily consumed by any programming language, and with minimal
complexity or dependencies. For example, state data can be utilized for basic queries without having to reconstruct a
merkle tree.
Block streams are an upgrade to the existing RecordStream V6. Block streams restructure and aggregate the multiple
record types in record streams, including
EventStream
,RecordStream
, and Hedera state data to produce a singleunified stream of
items
.The key enhancements offered by block streams include:
single cohesive data stream.
consensus and state changes.
consensus nodes are clearly specified, paving the way for future implementations and greater client diversity.
With the adoption of block streams, data output by Hedera consensus nodes will be consolidated into a single, verifiable
chronicle of the network, thereby strengthening the integrity and transparency of the Hedera ecosystem.