2023-04-10 18:36:45 +08:00
|
|
|
package vote
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
|
|
|
|
lru "github.com/hashicorp/golang-lru"
|
|
|
|
"github.com/tidwall/wal"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
|
|
"github.com/ethereum/go-ethereum/log"
|
|
|
|
"github.com/ethereum/go-ethereum/metrics"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2023-04-24 14:28:28 +08:00
|
|
|
maxSizeOfRecentEntry = 512
|
|
|
|
maliciousVoteSlashScope = 256
|
2023-04-10 18:36:45 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type VoteJournal struct {
|
|
|
|
journalPath string // file path of disk journal for saving the vote.
|
|
|
|
|
|
|
|
walLog *wal.Log
|
|
|
|
|
|
|
|
voteDataBuffer *lru.Cache
|
|
|
|
}
|
|
|
|
|
2023-05-05 14:15:43 +08:00
|
|
|
var voteJournalErrorCounter = metrics.NewRegisteredCounter("voteJournal/error", nil)
|
2023-04-10 18:36:45 +08:00
|
|
|
|
|
|
|
func NewVoteJournal(filePath string) (*VoteJournal, error) {
|
|
|
|
walLog, err := wal.Open(filePath, &wal.Options{
|
|
|
|
LogFormat: wal.JSON,
|
|
|
|
SegmentCacheSize: maxSizeOfRecentEntry,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to open vote journal", "err", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
voteDataBuffer, err := lru.New(maxSizeOfRecentEntry)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
firstIndex, err := walLog.FirstIndex()
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to get first index of votes journal", "err", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
lastIndex, err := walLog.LastIndex()
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to get lastIndex of vote journal", "err", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
voteJournal := &VoteJournal{
|
|
|
|
journalPath: filePath,
|
|
|
|
walLog: walLog,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reload all voteData from journal to lru memory everytime node reboot.
|
|
|
|
for index := firstIndex; index <= lastIndex; index++ {
|
|
|
|
if voteEnvelop, err := voteJournal.ReadVote(index); err == nil && voteEnvelop != nil {
|
|
|
|
voteData := voteEnvelop.Data
|
|
|
|
voteDataBuffer.Add(voteData.TargetNumber, voteData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
voteJournal.voteDataBuffer = voteDataBuffer
|
|
|
|
|
|
|
|
return voteJournal, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (journal *VoteJournal) WriteVote(voteMessage *types.VoteEnvelope) error {
|
|
|
|
walLog := journal.walLog
|
|
|
|
|
|
|
|
vote, err := json.Marshal(voteMessage)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to unmarshal vote", "err", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
lastIndex, err := walLog.LastIndex()
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to get lastIndex of vote journal", "err", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
lastIndex += 1
|
|
|
|
if err = walLog.Write(lastIndex, vote); err != nil {
|
|
|
|
log.Error("Failed to write vote journal", "err", err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
firstIndex, err := walLog.FirstIndex()
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to get first index of votes journal", "err", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if lastIndex-firstIndex+1 > maxSizeOfRecentEntry {
|
|
|
|
if err := walLog.TruncateFront(lastIndex - maxSizeOfRecentEntry + 1); err != nil {
|
|
|
|
log.Error("Failed to truncate votes journal", "err", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
journal.voteDataBuffer.Add(voteMessage.Data.TargetNumber, voteMessage.Data)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (journal *VoteJournal) ReadVote(index uint64) (*types.VoteEnvelope, error) {
|
|
|
|
voteMessage, err := journal.walLog.Read(index)
|
|
|
|
if err != nil && err != wal.ErrNotFound {
|
|
|
|
log.Error("Failed to read votes journal", "err", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var vote *types.VoteEnvelope
|
|
|
|
if voteMessage != nil {
|
|
|
|
vote = &types.VoteEnvelope{}
|
|
|
|
if err := json.Unmarshal(voteMessage, vote); err != nil {
|
|
|
|
log.Error("Failed to read vote from voteJournal", "err", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return vote, nil
|
|
|
|
}
|