Heimdall and Bor
Heimdall’s bor
module is responsible for managing span intervals and coordinating interactions with the Bor chain. Specifically, it determines when a new span can be proposed on Heimdall based on the current block number n
and the current span span
.
A new span proposal is permissible when the current Bor chain block number n
falls within the range of span.StartBlock
and span.EndBlock
(inclusive of StartBlock
and exclusive of EndBlock
). Validators on the Heimdall chain can propose a new span when these conditions are met.
Messages¶
MsgProposeSpan
¶
The MsgProposeSpan
message plays a crucial role in setting up the validator committee for a specific span and records a new span in the Heimdall state. This message is detailed in the Heimdall source code at bor/handler.go#L27.
// MsgProposeSpan creates msg propose span
type MsgProposeSpan struct {
ID uint64 `json:"span_id"`
Proposer hmTypes.HeimdallAddress `json:"proposer"`
StartBlock uint64 `json:"start_block"`
EndBlock uint64 `json:"end_block"`
ChainID string `json:"bor_chain_id"`
}
Selection of producers¶
The process for choosing producers from among all validators involves a two-step mechanism:
-
Slot allocation based on validator power: Each validator is assigned a number of slots proportional to their power. For instance, a validator with a power rating of 10 will receive 10 slots, while one with a power rating of 20 will receive 20 slots. This method ensures that validators with higher power have a correspondingly higher chance of being selected.
-
Shuffling and selection: All allocated slots are then shuffled using a
seed
derived from the Ethereum (ETH 1.0) block hash corresponding to each spann
. The firstproducerCount
producers are selected from this shuffled list. Thebor
module on Heimdall employs the Ethereum 2.0 shuffle algorithm for this selection process. The algorithm’s implementation can be viewed at bor/selection.go.
This method of selection ensures that the process is both fair and weighted according to the validators’ power, thereby maintaining a balanced and proportional representation in the span committee.
// SelectNextProducers selects producers for the next span by converting power to slots
// spanEligibleVals - all validators eligible for next span
func SelectNextProducers(blkHash common.Hash, spanEligibleVals []hmTypes.Validator, producerCount uint64) (selectedIDs []uint64, err error) {
if len(spanEligibleVals) <= int(producerCount) {
for _, val := range spanEligibleVals {
selectedIDs = append(selectedIDs, uint64(val.ID))
}
return
}
// extract seed from hash
seed := helper.ToBytes32(blkHash.Bytes()[:32])
validatorIndices := convertToSlots(spanEligibleVals)
selectedIDs, err = ShuffleList(validatorIndices, seed)
if err != nil {
return
}
return selectedIDs[:producerCount], nil
}
// converts validator power to slots
func convertToSlots(vals []hmTypes.Validator) (validatorIndices []uint64) {
for _, val := range vals {
for val.VotingPower >= types.SlotCost {
validatorIndices = append(validatorIndices, uint64(val.ID))
val.VotingPower = val.VotingPower - types.SlotCost
}
}
return validatorIndices
}
Types¶
Here are the span details that Heimdall uses:
// Span structure
type Span struct {
ID uint64 `json:"span_id" yaml:"span_id"`
StartBlock uint64 `json:"start_block" yaml:"start_block"`
EndBlock uint64 `json:"end_block" yaml:"end_block"`
ValidatorSet ValidatorSet `json:"validator_set" yaml:"validator_set"`
SelectedProducers []Validator `json:"selected_producers" yaml:"selected_producers"`
ChainID string `json:"bor_chain_id" yaml:"bor_chain_id"`
}
Parameters¶
The Bor module contains the following parameters:
Key | Type | Default value | Duration (*) |
---|---|---|---|
SprintDuration. | uint64 | 16 blocks | 32 seconds |
SpanDuration | uint64 | 100 * SprintDuration = 1,600 blocks | 3,200 seconds (53min and 20s) |
ProducerCount | uint64 | 4 blocks | 8 seconds |
(*): Given that blocks are produced every 2 seconds on Bor.
Sprint length
Previously, a sprint would last 64 blocks but it was agreed to decrease this number to 16 blocks on the Delhi hard fork of January 17th, 2023, precisely starting at block number 38,189,056.
CLI commands¶
Span propose tx¶
heimdallcli tx bor propose-span \
--start-block <start-block> \
--chain-id <heimdall-chain-id>
Query current span¶
heimdallcli query bor span latest-span --chain-id <heimdall-chain-id>
Expected output:
{
"span_id":2,
"start_block":6656,
"end_block":13055,
"validator_set":{
"validators":[
{
"ID":1,
"startEpoch":0,
"endEpoch":0,
"power":1,
"pubKey":"0x04b12d8b2f6e3d45a7ace12c4b2158f79b95e4c28ebe5ad54c439be9431d7fc9dc1164210bf6a5c3b8523528b931e772c86a307e8cff4b725e6b4a77d21417bf19",
"signer":"0x6c468cf8c9879006e22ec4029696e005c2319c9d",
"last_updated":"",
"accum":0
}
],
"proposer":{
"ID":1,
"startEpoch":0,
"endEpoch":0,
"power":1,
"pubKey":"0x04b12d8b2f6e3d45a7ace12c4b2158f79b95e4c28ebe5ad54c439be9431d7fc9dc1164210bf6a5c3b8523528b931e772c86a307e8cff4b725e6b4a77d21417bf19",
"signer":"0x6c468cf8c9879006e22ec4029696e005c2319c9d",
"last_updated":"",
"accum":0
}
},
"selected_producers":[
{
"ID":1,
"startEpoch":0,
"endEpoch":0,
"power":1,
"pubKey":"0x04b12d8b2f6e3d45a7ace12c4b2158f79b95e4c28ebe5ad54c439be9431d7fc9dc1164210bf6a5c3b8523528b931e772c86a307e8cff4b725e6b4a77d21417bf19",
"signer":"0x6c468cf8c9879006e22ec4029696e005c2319c9d",
"last_updated":"",
"accum":0
}
],
"bor_chain_id":"15001"
}
Query span by ID¶
heimdallcli query bor span --span-id <span-id> --chain-id <heimdall-chain-id>
It prints the result in same format as above.
Parameters¶
Use the following command to print all params:
heimdalldcli query bor params
Expected result:
sprint_duration: 16
span_duration: 1600
producer_count: 4
REST APIs¶
Name | Method | Endpoint |
---|---|---|
Span details | GET | /bor/span/span-id |
Get latest span | GET | /bor/latest-span |
Get params | GET | /bor/params |