claimtrie/commit.go
2021-05-24 16:33:20 -04:00

151 lines
2.8 KiB
Go

package claimtrie
import (
"bytes"
"encoding/gob"
"sync"
"github.com/lbryio/claimtrie/claim"
"github.com/lbryio/claimtrie/trie"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/pkg/errors"
"github.com/syndtr/goleveldb/leveldb"
)
// CommitVisit ...
type CommitVisit func(c *Commit)
// CommitMeta represent the meta associated with each commit.
type CommitMeta struct {
Height claim.Height
}
func newCommit(head *Commit, meta CommitMeta, h *chainhash.Hash) *Commit {
return &Commit{
MerkleRoot: h,
Meta: meta,
}
}
// Commit ...
type Commit struct {
MerkleRoot *chainhash.Hash
Meta CommitMeta
}
// CommitMgr ...
type CommitMgr struct {
sync.RWMutex
db *leveldb.DB
commits []*Commit
head *Commit
}
// NewCommitMgr ...
func NewCommitMgr(db *leveldb.DB) *CommitMgr {
head := newCommit(nil, CommitMeta{0}, trie.EmptyTrieHash)
cm := CommitMgr{
db: db,
head: head,
}
cm.commits = append(cm.commits, head)
return &cm
}
// Head ...
func (cm *CommitMgr) Head() *Commit {
cm.RLock()
defer cm.RUnlock()
return cm.head
}
// Commit ...
func (cm *CommitMgr) Commit(ht claim.Height, merkle *chainhash.Hash) {
if ht == 0 {
return
}
cm.Lock()
defer cm.Unlock()
c := newCommit(cm.head, CommitMeta{ht}, merkle)
cm.commits = append(cm.commits, c)
cm.head = c
}
// Reset ...
func (cm *CommitMgr) Reset(ht claim.Height) {
cm.Lock()
defer cm.Unlock()
for i := len(cm.commits) - 1; i >= 0; i-- {
c := cm.commits[i]
if c.Meta.Height <= ht {
cm.head = c
cm.commits = cm.commits[:i+1]
break
}
}
if cm.head.Meta.Height == ht {
return
}
cm.Commit(ht, cm.head.MerkleRoot)
}
// Save ...
func (cm *CommitMgr) Save() error {
cm.Lock()
defer cm.Unlock()
exported := struct {
Commits []*Commit
Head *Commit
}{
Commits: cm.commits,
Head: cm.head,
}
buf := bytes.NewBuffer(nil)
if err := gob.NewEncoder(buf).Encode(exported); err != nil {
return errors.Wrapf(err, "gob.Encode(): %s", err)
}
if err := cm.db.Put([]byte("CommitMgr"), buf.Bytes(), nil); err != nil {
return errors.Wrapf(err, "db.Put(CommitMgr)")
}
return nil
}
// Load ...
func (cm *CommitMgr) Load() error {
cm.Lock()
defer cm.Unlock()
exported := struct {
Commits []*Commit
Head *Commit
}{}
data, err := cm.db.Get([]byte("CommitMgr"), nil)
if err == leveldb.ErrNotFound {
return nil
} else if err != nil {
return errors.Wrapf(err, "db.Get(CommitMgr)")
}
if err := gob.NewDecoder(bytes.NewBuffer(data)).Decode(&exported); err != nil {
return errors.Wrapf(err, "gob.Encode(): %s", err)
}
cm.commits = exported.Commits
cm.head = exported.Head
return nil
}
// Log ...
func (cm *CommitMgr) Log(ht claim.Height, visit CommitVisit) {
cm.RLock()
defer cm.RUnlock()
for i := len(cm.commits) - 1; i >= 0; i-- {
c := cm.commits[i]
if c.Meta.Height > ht {
continue
}
visit(c)
}
}