-
Notifications
You must be signed in to change notification settings - Fork 886
add block number to ReadBlockByHash #3703
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ package littblock | |
|
|
||
| import ( | ||
| "encoding/binary" | ||
| "fmt" | ||
|
|
||
| "github.com/sei-protocol/sei-chain/sei-tendermint/autobahn/types" | ||
| ) | ||
|
|
@@ -63,22 +64,67 @@ func decodeNumberKey(key []byte) types.GlobalBlockNumber { | |
| return decodeKey(key[1:]) | ||
| } | ||
|
|
||
| // encodeBlock marshals a block to the bytes stored as its table value. | ||
| func encodeBlock(blk *types.Block) []byte { | ||
| return types.BlockConv.Marshal(blk) | ||
| // Serialization version for blocks. | ||
| const blockSerializationVersion byte = 1 | ||
|
|
||
| // Serialization version for QCs. | ||
| const qcSerializationVersion byte = 1 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we need qcSerializationVersion to return a block number?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't. I just added a 1 byte serialization version to the data schema since it's good practice to encode serialization versions (and it was originally missing). This way, if we ever decide to alter the serialization schema for a QC in the future, we will be able to do so with little headache. |
||
|
|
||
| // blockValuePrefixLen is the fixed header preceding a block's proto bytes: one | ||
| // version byte followed by the 8-byte big-endian GlobalBlockNumber. | ||
| const blockValuePrefixLen = 1 + 8 | ||
|
wen-coding marked this conversation as resolved.
|
||
|
|
||
| // encodeBlock marshals a block to the bytes stored as its table value. The value | ||
| // is framed as [version:1][GlobalBlockNumber:8 big-endian][proto(Block)]. The | ||
| // number is embedded so a by-hash lookup — which reaches this same shared value | ||
| // through a secondary key that carries only the hash — can still recover it. | ||
| func encodeBlock(n types.GlobalBlockNumber, blk *types.Block) []byte { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought we didn't have to encode block number into the block because it was the key. Would this hurt the performance? I guess not.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is in the primary key, but it's not in the secondary key. LittDB keeps no map from secondary keys to primary keys, and so if we want this data when we get by block hash, it needs to go into the value data. Technically yes, this does add some overhead. But in practical terms, this overhead is negligible. We're adding 8 bytes onto a 2 megabyte block (this is the size of the blocks Greg asked me to simulate in my benchmark). |
||
| proto := types.BlockConv.Marshal(blk) | ||
| value := make([]byte, 0, blockValuePrefixLen+len(proto)) | ||
| value = append(value, blockSerializationVersion) | ||
| value = binary.BigEndian.AppendUint64(value, uint64(n)) | ||
| value = append(value, proto...) | ||
| return value | ||
| } | ||
|
|
||
| // decodeBlock unmarshals a block from its stored table value. | ||
| func decodeBlock(value []byte) (*types.Block, error) { | ||
| return types.BlockConv.Unmarshal(value) | ||
| // decodeBlock unmarshals a block and its embedded GlobalBlockNumber from the | ||
| // value produced by encodeBlock. | ||
| func decodeBlock(value []byte) (types.GlobalBlockNumber, *types.Block, error) { | ||
| if len(value) < blockValuePrefixLen { | ||
| return 0, nil, fmt.Errorf("block value too short: %d bytes", len(value)) | ||
| } | ||
| if value[0] != blockSerializationVersion { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] This introduces a versioned on-disk value format (
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no production use yet There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] This rejects any value whose first byte isn't the current version, including the old unversioned raw-proto format written by the base code. Since littblock is currently only used by blocksim and tests (no live-node consumers of ReadBlockByHash/NewBlockDB), establishing v1 cleanly is reasonable. But please confirm no environment has already persisted littblock blocks/QCs in the old format — otherwise reads will fail post-upgrade and you'd need an unversioned-fallback decode path here (and in decodeQC). |
||
| return 0, nil, fmt.Errorf("unsupported block serialization version %d", value[0]) | ||
| } | ||
| n := types.GlobalBlockNumber(binary.BigEndian.Uint64(value[1:blockValuePrefixLen])) | ||
| blk, err := types.BlockConv.Unmarshal(value[blockValuePrefixLen:]) | ||
| if err != nil { | ||
| return 0, nil, fmt.Errorf("failed to unmarshal block: %w", err) | ||
| } | ||
| return n, blk, nil | ||
| } | ||
|
|
||
| // encodeQC marshals a FullCommitQC to the bytes stored as its table value. | ||
| // encodeQC marshals a FullCommitQC to the bytes stored as its table value, | ||
| // framed as [version:1][proto(FullCommitQC)]. | ||
| func encodeQC(qc *types.FullCommitQC) []byte { | ||
| return types.FullCommitQCConv.Marshal(qc) | ||
| proto := types.FullCommitQCConv.Marshal(qc) | ||
| value := make([]byte, 0, 1+len(proto)) | ||
| value = append(value, qcSerializationVersion) | ||
| value = append(value, proto...) | ||
| return value | ||
| } | ||
|
|
||
| // decodeQC unmarshals a FullCommitQC from its stored table value. | ||
| // decodeQC unmarshals a FullCommitQC from the value produced by encodeQC. | ||
| func decodeQC(value []byte) (*types.FullCommitQC, error) { | ||
| return types.FullCommitQCConv.Unmarshal(value) | ||
| if len(value) < 1 { | ||
| return nil, fmt.Errorf("qc value too short: %d bytes", len(value)) | ||
| } | ||
| if value[0] != qcSerializationVersion { | ||
| return nil, fmt.Errorf("unsupported qc serialization version %d", value[0]) | ||
| } | ||
| qc, err := types.FullCommitQCConv.Unmarshal(value[1:]) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to unmarshal qc: %w", err) | ||
| } | ||
| return qc, nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.