go-ethereum/consensus/XDPoS/engines/engine_v2/vote_test.go
Wanwiset Peerapatanapokin e324a78d94
fix(consensus): fix private chain initialization (#1987)
* revert: use masternodes from snapshot to verify vote

* fix underflow during chain initialization

* add previously removed test

* rename snapshot > snap for consistency
2026-02-19 01:47:03 +07:00

124 lines
3.2 KiB
Go

package engine_v2
import (
"context"
"log/slog"
"math/big"
"sync"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/params"
"github.com/stretchr/testify/assert"
)
// memoryHandler captures log records for inspection in tests.
type memoryHandler struct {
mu sync.Mutex
attrs []slog.Attr
records []slog.Record
}
func newMemoryHandler() *memoryHandler {
return &memoryHandler{}
}
func (h *memoryHandler) Enabled(_ context.Context, _ slog.Level) bool { return true }
func (h *memoryHandler) Handle(_ context.Context, r slog.Record) error {
clone := r.Clone()
h.mu.Lock()
defer h.mu.Unlock()
h.records = append(h.records, clone)
return nil
}
func (h *memoryHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &memoryHandler{attrs: append(append([]slog.Attr{}, h.attrs...), attrs...)}
}
func (h *memoryHandler) WithGroup(_ string) slog.Handler { return h }
func (h *memoryHandler) Records() []slog.Record {
h.mu.Lock()
defer h.mu.Unlock()
out := make([]slog.Record, len(h.records))
copy(out, h.records)
return out
}
// MockChainReader is a mock implementation of consensus.ChainReader
type MockChainReader struct {
headers map[common.Hash]*types.Header
}
// NewMockChainReader creates a new mock chain reader
func NewMockChainReader() *MockChainReader {
return &MockChainReader{
headers: make(map[common.Hash]*types.Header),
}
}
// AddHeader adds a header to the mock chain
func (m *MockChainReader) AddHeader(header *types.Header) {
m.headers[header.Hash()] = header
}
// Config implements consensus.ChainReader
func (m *MockChainReader) Config() *params.ChainConfig {
return nil
}
// CurrentHeader implements consensus.ChainReader
func (m *MockChainReader) CurrentHeader() *types.Header {
return nil
}
// GetHeader implements consensus.ChainReader
func (m *MockChainReader) GetHeader(hash common.Hash, number uint64) *types.Header {
return nil
}
// GetHeaderByNumber implements consensus.ChainReader
func (m *MockChainReader) GetHeaderByNumber(number uint64) *types.Header {
return nil
}
// GetHeaderByHash implements consensus.ChainReader
func (m *MockChainReader) GetHeaderByHash(hash common.Hash) *types.Header {
return m.headers[hash]
}
// GetBlock implements consensus.ChainReader
func (m *MockChainReader) GetBlock(hash common.Hash, number uint64) *types.Block {
return nil
}
// TestVerifyVoteMessage_VoteRoundTooOld tests that votes with rounds below
// the current round are rejected immediately
func TestVerifyVoteMessage_VoteRoundTooOld(t *testing.T) {
mockChain := NewMockChainReader()
engine := &XDPoS_v2{
currentRound: 10,
lock: sync.RWMutex{},
}
// Create a vote with a round number less than current round
vote := &types.Vote{
ProposedBlockInfo: &types.BlockInfo{
Hash: common.StringToHash("some-block"),
Round: 5, // Less than currentRound (10)
Number: big.NewInt(50),
},
Signature: make([]byte, 65),
GapNumber: 0,
}
verified, err := engine.VerifyVoteMessage(mockChain, vote)
// Should reject the vote without error
assert.False(t, verified, "Should return false for vote with round < currentRound")
assert.NoError(t, err, "Should not return an error for old round votes")
}