ceremonyclient/node/consensus/global/frame_chain_checker_test.go
Cassandra Heart 8dc7e0d526
v2.1.0.14 (#484)
* v2.1.0.14

* release notes
2025-12-03 23:56:34 -06:00

181 lines
4.1 KiB
Go

package global
import (
"testing"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/consensus/models"
"source.quilibrium.com/quilibrium/monorepo/protobufs"
"source.quilibrium.com/quilibrium/monorepo/types/store"
)
func TestFrameChainChecker_CanProcessSequentialChain(t *testing.T) {
store := newMockFrameChainStore()
checker := NewFrameChainChecker(store, zap.NewNop())
finalized := newTestFrame(10, nil)
store.addSealed(finalized)
finalizedState := &models.State[*protobufs.GlobalFrame]{
Rank: finalized.Header.Rank,
Identifier: finalized.Identity(),
State: &finalized,
}
candidate := newTestFrame(
11,
[]byte(finalized.Identity()),
)
store.addCandidate(candidate)
proposalFrame := newTestFrame(
12,
[]byte(candidate.Identity()),
)
proposal := &protobufs.GlobalProposal{
State: proposalFrame,
}
require.True(
t,
checker.CanProcessSequentialChain(finalizedState, proposal),
)
}
func TestFrameChainChecker_CanProcessSequentialChainMultipleCandidates(
t *testing.T,
) {
store := newMockFrameChainStore()
checker := NewFrameChainChecker(store, zap.NewNop())
finalized := newTestFrame(20, nil)
store.addSealed(finalized)
finalizedState := &models.State[*protobufs.GlobalFrame]{
Rank: finalized.Header.Rank,
Identifier: finalized.Identity(),
State: &finalized,
}
candidate21 := newTestFrame(
21,
[]byte(finalized.Identity()),
)
store.addCandidate(candidate21)
candidate22 := newTestFrame(
22,
[]byte(candidate21.Identity()),
)
store.addCandidate(candidate22)
proposal := &protobufs.GlobalProposal{
State: newTestFrame(
23,
[]byte(candidate22.Identity()),
),
}
require.True(
t,
checker.CanProcessSequentialChain(finalizedState, proposal),
)
}
func TestFrameChainChecker_CanProcessSequentialChainMissingParent(
t *testing.T,
) {
store := newMockFrameChainStore()
checker := NewFrameChainChecker(store, zap.NewNop())
finalized := newTestFrame(5, nil)
store.addSealed(finalized)
finalizedState := &models.State[*protobufs.GlobalFrame]{
Rank: finalized.Header.Rank,
Identifier: finalized.Identity(),
State: &finalized,
}
// Proposal references a parent that does not exist
proposal := &protobufs.GlobalProposal{
State: newTestFrame(
6,
[]byte("missing-parent"),
),
}
require.False(
t,
checker.CanProcessSequentialChain(finalizedState, proposal),
)
}
type mockFrameChainStore struct {
sealed map[uint64]*protobufs.GlobalFrame
candidates map[uint64]map[string]*protobufs.GlobalFrame
}
func newMockFrameChainStore() *mockFrameChainStore {
return &mockFrameChainStore{
sealed: make(map[uint64]*protobufs.GlobalFrame),
candidates: make(map[uint64]map[string]*protobufs.GlobalFrame),
}
}
func (m *mockFrameChainStore) addSealed(frame *protobufs.GlobalFrame) {
if frame == nil || frame.Header == nil {
return
}
m.sealed[frame.Header.FrameNumber] = frame
}
func (m *mockFrameChainStore) addCandidate(frame *protobufs.GlobalFrame) {
if frame == nil || frame.Header == nil {
return
}
key := frame.Header.FrameNumber
if _, ok := m.candidates[key]; !ok {
m.candidates[key] = make(map[string]*protobufs.GlobalFrame)
}
m.candidates[key][string(frame.Identity())] = frame
}
func (m *mockFrameChainStore) GetGlobalClockFrame(
frameNumber uint64,
) (*protobufs.GlobalFrame, error) {
frame, ok := m.sealed[frameNumber]
if !ok {
return nil, store.ErrNotFound
}
return frame, nil
}
func (m *mockFrameChainStore) GetGlobalClockFrameCandidate(
frameNumber uint64,
selector []byte,
) (*protobufs.GlobalFrame, error) {
candidates := m.candidates[frameNumber]
if candidates == nil {
return nil, store.ErrNotFound
}
frame, ok := candidates[string(selector)]
if !ok {
return nil, store.ErrNotFound
}
return frame, nil
}
func newTestFrame(
frameNumber uint64,
parentSelector []byte,
) *protobufs.GlobalFrame {
header := &protobufs.GlobalFrameHeader{
FrameNumber: frameNumber,
ParentSelector: parentSelector,
Output: []byte{byte(frameNumber)},
Rank: frameNumber,
}
return &protobufs.GlobalFrame{
Header: header,
}
}