-
Notifications
You must be signed in to change notification settings - Fork 12
review block structure #628
Description
Since the release of the first Rivine-based chain, the Threefold Chain (tfchain in short) the block structure of such chains can be seen as a structure with the following properties in the order as defined:
- ParentID
- Timestamp
- BlockStakeOutputIndexes
- MinerPayouts
- Transactions
The BlockStakeOutputIndexes is defined by the following structure:
// BlockStakeOutputIndexes groups the block height, the transaction index and the output index to uniquely identify a blockstake output.
// These indexes and the value are required for the POBS protocol.
BlockStakeOutputIndexes struct {
BlockHeight BlockHeight
TransactionIndex uint64
OutputIndex uint64
}Each miner payout can be seen as a pair of a Value and an UnlockHash (the address which received the paired Value.
For the sake of this issue each transaction can be seen as an opaque binary object with the first byte indicating its version. How the object is to be decoded is defined by the chain, but not relevant for this issue.
Compared to other chains we make no difference between Header and Body. However given that you could see the first 3 properties as the header, with the next 2 being the body you could go from a conceptual concept to an implemented one. It would require at the very least one adaptation:
- We do not have a property to uniquely and safely identify the transactions of the body. Other chains use often a MerkeRoot hash for this. Nothing prevents us from adding this property once we go for a header-body separation;
Besides this we might also want the following adaptations:
- We could replace the
BlockStakeOutputIndexesproperty with an opaque binary object, which could be called thesettlementor however you wish to call it. The decoding of which depends on a version, either unique to this object or coupled directly to a block version as proposed in the next bullet point; - We currently do not have a block version, making it a lot more difficult to upgrade the block structure (again) in the future.
On top of that some chains also store a Nonce in the header. At the moment I am however not sure why this would be desired, given a Merkle Root hash together with the other properties (e.g. the parent ID) should be enough to make the header unique. If not, I would think you have an issues with your cryptographic primitives used. I might however mis something here.
In Memory we do already have a BlockHeader structure defined as follows:
// A BlockHeader, when encoded, is an 96-byte constant size field
// containing enough information to do headers-first block downloading.
// Hashing the header results in the block ID.
BlockHeader struct {
ParentID
BlockStakeOutputIndexes
Timestamp
MerkleRoot
}This header is derived from the block structure listed earlier. As we already calculate the MerkleRoot based on the transactions, it wouldn't be hard to add it to an actually implementation used block header. Again I would propose to turn the BlockStakeOutputIndexes object into an opaque binary object linked to a version, so we can change the consensus algorithm when needed without hardcoding all these things in such a transparent manner. Further we again miss the version here.
The header is currently already transferred separately from the actual block between peers,
to allow the receiving peer to decide whether or not the actual block is desired.
It it is thus not unreasonable to go the extra mile here and also allow clients to actually
download headers-only by default, and downloading actual blocks only when desired.
That mile is however a long one.
Thus so far a proposal for new structures would be as follows:
type (
BlockHeader struct {
Version byte
ParentID BlockID
Settlement []byte // find a better name
Timestamp Timestamp
MerkleRoot crypto.Hash
}
// Some chains also add a magic No in the front of a block when encoding,
// to make sure during the decoding one can know for sure it is a block,
// irrelevant on its actual version or content. Not sure about
// if we really want this though.
Block struct {
Header BlockHeader
// it is probably even better to remove the miner payouts property,
// and turn them into a transaction (or transactions).
MinerPayouts []MinerPayout
Transactions []Transaction
}
)