Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added avail.test
Binary file not shown.
65 changes: 20 additions & 45 deletions sei-db/ledger_db/block/block_db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ func testPruneStraddleRetainsQC(t *testing.T, build builder) {
require.NoError(t, err)
got, ok := opt.Get()
require.True(t, ok, "straddling QC must be retained")
require.Equal(t, straddled.first, got.QC().GlobalRange(committee).First)
require.Equal(t, straddled.first, got.QC().GlobalRange().First)
}

// testPruneIdempotentMonotonic asserts PruneBefore is idempotent and the
Expand Down Expand Up @@ -485,7 +485,7 @@ func testReverseIteratorOrdering(t *testing.T, build builder) {
}
qc, err := qcIt.QC()
require.NoError(t, err)
first := qc.QC().GlobalRange(committee).First
first := qc.QC().GlobalRange().First
if qcCount == 0 {
require.Equal(t, lastFirst, first, "reverse QCs must surface the last QC first")
}
Expand Down Expand Up @@ -525,8 +525,8 @@ func testResumeAfterRestart(t *testing.T, build builder) {

prevQC, ok := recoverLastQC(t, db)
require.True(t, ok)
require.Equal(t, last.first, prevQC.GlobalRange(committee).First, "recovered QC must be the last persisted QC")
require.Equal(t, last.next, prevQC.GlobalRange(committee).Next)
require.Equal(t, last.first, prevQC.GlobalRange().First, "recovered QC must be the last persisted QC")
require.Equal(t, last.next, prevQC.GlobalRange().Next)

// The recovered QC's upper bound is exactly where the continuation begins;
// writing the next contiguous batch must be accepted.
Expand Down Expand Up @@ -712,7 +712,7 @@ func TestMemblockPruneRemovesBelowWatermark(t *testing.T) {
}
fqc, err := qcIt.QC()
require.NoError(t, err)
require.GreaterOrEqual(t, fqc.QC().GlobalRange(committee).First, watermark,
require.GreaterOrEqual(t, fqc.QC().GlobalRange().First, watermark,
"QC iterator must not surface pruned QCs")
}
require.NoError(t, qcIt.Close())
Expand Down Expand Up @@ -842,13 +842,13 @@ func assertBlocksReadable(t *testing.T, db types.BlockDB, batches []batch) {

func assertQCsReadable(t *testing.T, db types.BlockDB, committee *types.Committee, batches []batch) {
for _, b := range batches {
r := b.qc.QC().GlobalRange(committee)
r := b.qc.QC().GlobalRange()
for n := r.First; n < r.Next; n++ {
opt, err := db.ReadQCByBlockNumber(n)
require.NoError(t, err)
got, ok := opt.Get()
require.True(t, ok, "QC covering %d should exist", n)
gr := got.QC().GlobalRange(committee)
gr := got.QC().GlobalRange()
require.Equal(t, r.First, gr.First)
require.Equal(t, r.Next, gr.Next)
require.Len(t, got.Headers(), len(b.qc.Headers()), "QC must round-trip its full header set")
Expand Down Expand Up @@ -902,7 +902,7 @@ func assertIterators(t *testing.T, db types.BlockDB, committee *types.Committee,
}
qc, err := qcIt.QC()
require.NoError(t, err)
first := qc.QC().GlobalRange(committee).First
first := qc.QC().GlobalRange().First
if haveQC {
require.Greater(t, first, prevFirst, "QCs must iterate ascending by First")
}
Expand Down Expand Up @@ -960,7 +960,7 @@ func buildCommittee() (*types.Committee, []types.SecretKey) {
keys[i] = types.GenSecretKey(rng)
replicas[i] = keys[i].Public()
}
committee := utils.OrPanic1(types.NewRoundRobinElection(replicas, 0, genesisTime))
committee := utils.OrPanic1(types.NewRoundRobinElection(replicas))
return committee, keys
}

Expand All @@ -972,7 +972,7 @@ func generateBatches(committee *types.Committee, keys []types.SecretKey) []batch
batches := make([]batch, 0, numBatches)
for range numBatches {
fqc, blocks := buildFullCommitQC(rng, committee, keys, prev)
r := fqc.QC().GlobalRange(committee)
r := fqc.QC().GlobalRange()
batches = append(batches, batch{first: r.First, next: r.Next, blocks: blocks, qc: fqc})
prev = utils.Some(fqc.QC())
}
Expand All @@ -991,18 +991,12 @@ func buildFullCommitQC(
parent := bs[len(bs)-1]
return types.NewBlock(producer, parent.Header().Next(), parent.Header().Hash(), types.GenPayload(rng))
}
return types.NewBlock(
producer,
types.LaneRangeOpt(prev, producer).Next(),
types.GenBlockHeaderHash(rng),
types.GenPayload(rng),
)
return types.NewBlock(producer, types.LaneRangeOpt(prev, producer).Next(), types.GenBlockHeaderHash(rng), types.GenPayload(rng))
}
for range blocksPerQC {
producer := committee.Lanes().At(rng.Intn(committee.Lanes().Len()))
blocks[producer] = append(blocks[producer], makeBlock(producer))
}

laneQCs := map[types.LaneID]*types.LaneQC{}
var headers []*types.BlockHeader
var blockList []*types.Block
Expand All @@ -1015,35 +1009,16 @@ func buildFullCommitQC(
}
}
}

viewSpec := types.ViewSpec{CommitQC: prev}
leader := committee.Leader(viewSpec.View())
var leaderKey types.SecretKey
for _, k := range keys {
if k.Public() == leader {
leaderKey = k
break
}
}
proposal := utils.OrPanic1(types.NewProposal(
leaderKey,
committee,
viewSpec,
genesisTime,
laneQCs,
func() utils.Option[*types.AppQC] {
if n := types.GlobalRangeOpt(prev, committee).Next; n > 0 {
p := types.NewAppProposal(n-1, viewSpec.View().Index, types.GenAppHash(rng))
return utils.Some(testAppQC(keys, p))
}
return utils.None[*types.AppQC]()
}(),
))
votes := make([]*types.Signed[*types.CommitVote], 0, len(keys))
for _, k := range keys {
votes = append(votes, types.Sign(k, types.NewCommitVote(proposal.Proposal().Msg())))
var appQC utils.Option[*types.AppQC]
if cqc, ok := prev.Get(); ok {
vs := types.ViewSpec{CommitQC: prev}
p := types.NewAppProposal(cqc.GlobalRange().Next-1, vs.View().Index, types.GenAppHash(rng))
appQC = utils.Some(testAppQC(keys, p))
} else {
appQC = utils.None[*types.AppQC]()
}
return types.NewFullCommitQC(types.NewCommitQC(votes), headers), blockList
cqc := types.BuildCommitQC(committee, keys, prev, 0, genesisTime, laneQCs, appQC)
return types.NewFullCommitQC(cqc, headers), blockList
}

func testLaneQC(keys []types.SecretKey, header *types.BlockHeader) *types.LaneQC {
Expand Down
7 changes: 5 additions & 2 deletions sei-db/ledger_db/block/blocksim/block_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (g *BlockGenerator) mainLoop() {

func (g *BlockGenerator) buildBatch() *generatedBatch {
fqc, blocks := g.buildFullCommitQC()
r := fqc.QC().GlobalRange(g.committee)
r := fqc.QC().GlobalRange()
g.prev = utils.Some(fqc.QC())
return &generatedBatch{first: r.First, next: r.Next, blocks: blocks, qc: fqc}
}
Expand Down Expand Up @@ -145,10 +145,13 @@ func (g *BlockGenerator) buildFullCommitQC() (*types.FullCommitQC, []*types.Bloc
leaderKey,
committee,
viewSpec,
0,
genesisTime,
time.Now(),
laneQCs,
func() utils.Option[*types.AppQC] {
if n := types.GlobalRangeOpt(prev, committee).Next; n > 0 {
if cqc, ok := prev.Get(); ok {
n := cqc.GlobalRange().Next
p := types.NewAppProposal(n-1, viewSpec.View().Index, types.GenAppHash(rng))
return utils.Some(testAppQC(keys, p))
}
Expand Down
4 changes: 2 additions & 2 deletions sei-db/ledger_db/block/blocksim/blocksim.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func NewBlockSim(
// last QC's range — the next batch then appends contiguously. Block bytes
// are irrelevant here (this is a DB stress test), so the backfill writes
// freshly generated blocks under the already-persisted QC.
qcRange := prevQC.GlobalRange(committee)
qcRange := prevQC.GlobalRange()
lastQCNext := uint64(qcRange.Next)
firstMissing := uint64(qcRange.First)
if h, ok := highestOpt.Get(); ok {
Expand Down Expand Up @@ -263,7 +263,7 @@ func buildCommittee(rng tmutils.Rng, size int) (*types.Committee, []types.Secret
keys[i] = types.GenSecretKey(rng)
replicas[i] = keys[i].Public()
}
committee, err := types.NewRoundRobinElection(replicas, 0, genesisTime)
committee, err := types.NewRoundRobinElection(replicas)
if err != nil {
return nil, nil, fmt.Errorf("failed to build committee: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions sei-db/ledger_db/block/blocksim/resume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ func TestRecoverResumeState(t *testing.T) {

prevQC, ok := prev.Get()
require.True(t, ok, "recovered prev QC must be present")
require.Equal(t, last.first, prevQC.GlobalRange(committee).First, "recovered QC must be the last persisted QC")
require.Equal(t, last.next, prevQC.GlobalRange(committee).Next)
require.Equal(t, last.first, prevQC.GlobalRange().First, "recovered QC must be the last persisted QC")
require.Equal(t, last.next, prevQC.GlobalRange().Next)

// Empty-store sanity: a fresh dir recovers nothing.
empty, err := openBlockDB(&BlocksimConfig{Backend: "litt", DataDir: t.TempDir(), LittRetentionSeconds: 1})
Expand Down
2 changes: 1 addition & 1 deletion sei-tendermint/autobahn/types/app_proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (m *AppProposal) Verify(c *Committee, qc *CommitQC) error {
if got, want := m.RoadIndex(), qc.Proposal().Index(); got != want {
return fmt.Errorf("roadIndex() = %v, want %v", got, want)
}
if got, want := m.GlobalNumber(), qc.GlobalRange(c); got < want.First || got >= want.Next {
if got, want := m.GlobalNumber(), qc.GlobalRange(); got < want.First || got >= want.Next {
return fmt.Errorf("globalNumber() = %v, want in range [%v,%v)", got, want.First, want.Next)
}
return nil
Expand Down
8 changes: 4 additions & 4 deletions sei-tendermint/autobahn/types/commit_qc.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ func (m *CommitQC) LaneRange(lane LaneID) *LaneRange {
}

// GlobalRange returns the finalized global block range.
func (m *CommitQC) GlobalRange(c *Committee) GlobalRange {
return m.Proposal().GlobalRange(c)
func (m *CommitQC) GlobalRange() GlobalRange {
return m.Proposal().GlobalRange()
}

// Verify verifies the CommitQC against the committee.
Expand All @@ -60,7 +60,7 @@ type FullCommitQC struct {

// NewFullCommitQC constructs a new FullCommitQC.
func NewFullCommitQC(qc *CommitQC, headers []*BlockHeader) *FullCommitQC {
if got, want := len(headers), int(qc.Proposal().globalRangeWithoutOffset.Len()); got != want { //nolint:gosec // total lane range len is a small bounded value representing block count in a QC
if got, want := len(headers), int(qc.GlobalRange().Len()); got != want { //nolint:gosec // total lane range len is a small bounded value representing block count in a QC
panic(fmt.Sprintf("headers length %d != finalized blocks %d", got, want))
}
return &FullCommitQC{qc: qc, headers: headers}
Expand All @@ -83,7 +83,7 @@ func (m *FullCommitQC) Verify(c *Committee) error {
return fmt.Errorf("qC: %w", err)
}
n := uint64(0)
if want, got := int(m.qc.GlobalRange(c).Len()), len(m.headers); want != got { //nolint:gosec // global range len is a small bounded value representing block count in a QC
if want, got := int(m.qc.GlobalRange().Len()), len(m.headers); want != got { //nolint:gosec // global range len is a small bounded value representing block count in a QC
return fmt.Errorf("len(headers) = %d, want %d", got, want)
}
for lane := range c.Lanes().All() {
Expand Down
31 changes: 7 additions & 24 deletions sei-tendermint/autobahn/types/committee.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"iter"
"maps"
"slices"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
Expand All @@ -27,14 +26,6 @@ type Committee struct {
replicas ImSlice[PublicKey]
weights map[PublicKey]uint64
totalWeight uint64
// Number of the first block of the chain.
// TODO: firstBlock is not really a part of the committee,
// but it does belong to a chain spec (or epoch spec/genesis/etc.),
// which should be passed around to verify autobahn messages.
// Once we introduce the chain spec it should wrap Committee and firstBlock.
firstBlock GlobalBlockNumber
// timestamp at genesis. All blocks need to have a timestamp later than genesis.
genesisTimestamp time.Time
}

const MaxValidators = 100
Expand All @@ -55,12 +46,6 @@ func (c *Committee) Lanes() ImSlice[LaneID] { return c.replicas }
// Replicas is the list of nodes which are eligible to participate in the consensus.
func (c *Committee) Replicas() ImSlice[PublicKey] { return c.replicas }

// FirstBlock is the index of the first global block finalized by this committee.
func (c *Committee) FirstBlock() GlobalBlockNumber { return c.firstBlock }

// GenesisTimestamp is the timestamp at genesis.
func (c *Committee) GenesisTimestamp() time.Time { return c.genesisTimestamp }

// Deterministic random oracle selecting a replica with probability proportional to the weight.
func (c *Committee) randomReplica(seed []byte) PublicKey {
h := sha256.Sum256(seed[:])
Expand Down Expand Up @@ -131,7 +116,7 @@ func (c *Committee) LaneQuorum() uint64 {
return c.Faulty() + 1
}

func NewCommittee(weights map[PublicKey]uint64, firstBlock GlobalBlockNumber, genesisTimestamp time.Time) (*Committee, error) {
func NewCommittee(weights map[PublicKey]uint64) (*Committee, error) {
weights = maps.Clone(weights)
totalWeight := uint64(0)
for k, w := range weights {
Expand All @@ -151,19 +136,17 @@ func NewCommittee(weights map[PublicKey]uint64, firstBlock GlobalBlockNumber, ge
}
replicas := slices.SortedFunc(maps.Keys(weights), func(a, b PublicKey) int { return a.Compare(b) })
return &Committee{
replicas: ImSlice[PublicKey]{replicas},
weights: weights,
totalWeight: totalWeight,
firstBlock: firstBlock,
genesisTimestamp: genesisTimestamp,
replicas: ImSlice[PublicKey]{replicas},
weights: weights,
totalWeight: totalWeight,
}, nil
}

// NewRoundRobinElection creates a Committee with round robin election starting at firstBlock.
func NewRoundRobinElection(replicas []PublicKey, firstBlock GlobalBlockNumber, genesisTimestamp time.Time) (*Committee, error) {
// NewRoundRobinElection creates a Committee with equal weights for each replica.
func NewRoundRobinElection(replicas []PublicKey) (*Committee, error) {
weights := map[PublicKey]uint64{}
for _, k := range replicas {
weights[k] = 1
}
return NewCommittee(weights, firstBlock, genesisTimestamp)
return NewCommittee(weights)
}
19 changes: 5 additions & 14 deletions sei-tendermint/autobahn/types/committee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,20 @@ package types
import (
"math"
"testing"
"time"

"github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils"
"github.com/sei-protocol/sei-chain/sei-tendermint/libs/utils/require"
)

func TestNewCommittee_FiltersOutZeroWeightValidators(t *testing.T) {
rng := utils.TestRng()
firstBlock := GenGlobalBlockNumber(rng)
genesisTimestamp := time.Now()
zeroWeightKey := GenPublicKey(rng)
nonZeroWeightKey := GenPublicKey(rng)

committee, err := NewCommittee(map[PublicKey]uint64{
zeroWeightKey: 0,
nonZeroWeightKey: 7,
}, firstBlock, genesisTimestamp)
})
if err != nil {
t.Fatalf("NewCommittee(): %v", err)
}
Expand All @@ -40,43 +37,37 @@ func TestNewCommittee_FiltersOutZeroWeightValidators(t *testing.T) {

func TestNewCommittee_RejectsZeroTotalWeight(t *testing.T) {
rng := utils.TestRng()
firstBlock := GenGlobalBlockNumber(rng)
genesisTimestamp := time.Now()

_, err := NewCommittee(map[PublicKey]uint64{
GenPublicKey(rng): 0,
GenPublicKey(rng): 0,
}, firstBlock, genesisTimestamp)
})
if err == nil {
t.Fatal("NewCommittee() succeeded, want error")
}
}

func TestNewCommittee_RejectsWeightOverflow(t *testing.T) {
rng := utils.TestRng()
firstBlock := GenGlobalBlockNumber(rng)
genesisTimestamp := time.Now()

_, err := NewCommittee(map[PublicKey]uint64{
GenPublicKey(rng): math.MaxUint64,
GenPublicKey(rng): 1,
}, firstBlock, genesisTimestamp)
})
if err == nil {
t.Fatal("NewCommittee() succeeded, want error")
}
}

func TestNewCommittee_RejectsTooManyValidators(t *testing.T) {
rng := utils.TestRng()
firstBlock := GenGlobalBlockNumber(rng)
genesisTimestamp := time.Now()

weights := map[PublicKey]uint64{}
for range MaxValidators + 1 {
weights[GenPublicKey(rng)] = 1
}

_, err := NewCommittee(weights, firstBlock, genesisTimestamp)
_, err := NewCommittee(weights)
if err == nil {
t.Fatal("NewCommittee() succeeded, want error")
}
Expand All @@ -92,7 +83,7 @@ func makeCommittee() (*Committee, []SecretKey) {
keys[0].Public(): 5,
keys[1].Public(): 1,
keys[2].Public(): 1,
}, 0, time.Now())), keys
})), keys
}

func TestLaneQCVerifyChecksWeight(t *testing.T) {
Expand Down
Loading
Loading