Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add backend
Browse files Browse the repository at this point in the history
kanishkatn committed Jul 14, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent ace8c36 commit b0b8ff1
Showing 15 changed files with 1,278 additions and 54 deletions.
151 changes: 151 additions & 0 deletions dot/parachain/dispute/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package dispute

import (
"fmt"
"github.com/ChainSafe/gossamer/dot/parachain/dispute/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/parachain"
"github.com/dgraph-io/badger/v4"
"github.com/google/btree"
"time"
)

const (
earliestSessionKey = "earliestSession"
recentDisputesPrefix = "recentDisputes_"
candidateVotesPrefix = "candidateVotes_"
)

// Backend is the backend for the dispute coordinator module.
type Backend interface {
// GetEarliestSession returns the earliest session index, if any.
GetEarliestSession() (*parachain.SessionIndex, error)
// GetRecentDisputes returns the recent disputes, if any.
GetRecentDisputes() (*btree.BTree, error)
// GetActiveDisputes returns the active disputes, if any.
//GetActiveDisputes(now int64) (*btree.BTree, error)
// GetCandidateVotes returns the votes for the given candidate for the specific session-candidate pair, if any.
GetCandidateVotes(session parachain.SessionIndex, candidateHash common.Hash) (types.CandidateVotes, error)

// SetEarliestSession sets the earliest session index.
SetEarliestSession(session parachain.SessionIndex) error
// SetRecentDisputes sets the recent disputes.
SetRecentDisputes(recentDisputes *btree.BTree) error
// SetCandidateVotes sets the votes for the given candidate for the specific session-candidate pair.
SetCandidateVotes(session parachain.SessionIndex, candidateHash common.Hash, votes types.CandidateVotes) error
}

// DBBackend is the backend for the dispute coordinator module that uses a database.
type DBBackend interface {
Backend
}

// OverlayBackend implements Backend.
type OverlayBackend struct {
inner DBBackend
earliestSession *parachain.SessionIndex
recentDisputes *btree.BTree
candidateVotes map[types.Comparator]types.CandidateVotes
}

func (b *OverlayBackend) GetEarliestSession() (*parachain.SessionIndex, error) {
if b.earliestSession != nil {
return b.earliestSession, nil
}

earliestSession, err := b.inner.GetEarliestSession()
if err != nil {
return nil, fmt.Errorf("get earliest session from db: %w", err)
}

b.earliestSession = earliestSession
return b.earliestSession, nil
}

func (b *OverlayBackend) GetRecentDisputes() (*btree.BTree, error) {
if b.recentDisputes.Len() > 0 {
return b.recentDisputes, nil
}

recentDisputes, err := b.inner.GetRecentDisputes()
if err != nil {
return nil, fmt.Errorf("get recent disputes from db: %w", err)
}

return recentDisputes, nil
}

func (b *OverlayBackend) GetCandidateVotes(session parachain.SessionIndex, candidateHash common.Hash) (types.CandidateVotes, error) {
key := types.Comparator{
SessionIndex: session,
CandidateHash: candidateHash,
}

if v, ok := b.candidateVotes[key]; ok {
return v, nil
}

votes, err := b.inner.GetCandidateVotes(session, candidateHash)
if err != nil {
return types.CandidateVotes{}, fmt.Errorf("get candidate votes from db: %w", err)
}

return votes, nil
}

func (b *OverlayBackend) SetEarliestSession(session parachain.SessionIndex) error {
b.earliestSession = &session
return nil
}

func (b *OverlayBackend) SetRecentDisputes(recentDisputes *btree.BTree) error {
b.recentDisputes = recentDisputes
return nil
}

func (b *OverlayBackend) SetCandidateVotes(session parachain.SessionIndex, candidateHash common.Hash, votes types.CandidateVotes) error {
key := types.Comparator{
SessionIndex: session,
CandidateHash: candidateHash,
}
b.candidateVotes[key] = votes
return nil
}

func (b *OverlayBackend) getActiveDisputes(recentDisputes *btree.BTree) {
b.recentDisputes = recentDisputes
}

// ActiveDuration an arbitrary duration for how long a dispute is considered active.
const ActiveDuration = 180 * time.Second

// GetActiveDisputes returns the active disputes, if any.
func (b *OverlayBackend) GetActiveDisputes(now int64) (*btree.BTree, error) {
activeDisputes := btree.New(32)

b.recentDisputes.Ascend(func(i btree.Item) bool {
dispute, ok := i.(*types.Dispute)
if !ok {
// TODO: do we want to panic here?
panic("invalid dispute type")
}
concludedAt, err := dispute.DisputeStatus.ConcludedAt()
if err == nil && concludedAt != nil && *concludedAt+uint64(ActiveDuration.Seconds()) > uint64(now) {
activeDisputes.ReplaceOrInsert(dispute)
}
return true
})

return activeDisputes, nil
}

var _ Backend = (*OverlayBackend)(nil)

// NewOverlayBackend creates a new OverlayBackend.
func NewOverlayBackend(db *badger.DB) *OverlayBackend {
return &OverlayBackend{
inner: NewDBBackend(db),
recentDisputes: btree.New(32),
candidateVotes: make(map[types.Comparator]types.CandidateVotes),
}
}
151 changes: 151 additions & 0 deletions dot/parachain/dispute/backend_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package dispute

import (
"fmt"
"github.com/ChainSafe/gossamer/dot/parachain/dispute/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/parachain"
"github.com/dgraph-io/badger/v4"
"github.com/google/btree"
"github.com/stretchr/testify/require"
"testing"
)

type disputeStatusEnum uint

const (
DisputeStatusActive disputeStatusEnum = iota
DisputeStatusConcludedFor
DisputeStatusConcludedAgainst
DisputeStatusConfirmed
)

func newTestDispute(session parachain.SessionIndex, candidateHash common.Hash, status disputeStatusEnum) (*types.Dispute, error) {
disputeStatus, err := types.NewDisputeStatus()
if err != nil {
return nil, err
}

switch status {
case DisputeStatusActive:
err := disputeStatus.Set(types.ActiveStatus{})
if err != nil {
return nil, err
}
case DisputeStatusConcludedFor:
err := disputeStatus.Set(types.ConcludedForStatus{})
if err != nil {
return nil, err
}
case DisputeStatusConcludedAgainst:
err := disputeStatus.Set(types.ConcludedAgainstStatus{})
if err != nil {
return nil, err
}
case DisputeStatusConfirmed:
err := disputeStatus.Set(types.ConfirmedStatus{})
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("invalid dispute status")
}

return &types.Dispute{
Comparator: types.Comparator{
SessionIndex: session,
CandidateHash: candidateHash,
},
DisputeStatus: disputeStatus,
}, nil
}

func newTestCandidateVotes() types.CandidateVotes {
receipt := parachain.CandidateReceipt{
Descriptor: parachain.CandidateDescriptor{
ParaID: 1,
RelayParent: common.Hash{2},
Collator: parachain.CollatorID{2},
PersistedValidationDataHash: common.Hash{2},
PovHash: common.Hash{2},
ErasureRoot: common.Hash{2},
Signature: parachain.CollatorSignature{2},
ParaHead: common.Hash{2},
ValidationCodeHash: parachain.ValidationCodeHash{2},
},
CommitmentsHash: common.Hash{1},
}

return types.CandidateVotes{
CandidateReceipt: receipt,
}
}

func TestOverlayBackend_EarliestSession(t *testing.T) {
t.Parallel()

// with
db, err := badger.Open(badger.DefaultOptions(t.TempDir()))
require.NoError(t, err)

// when
backend := NewOverlayBackend(db)
err = backend.SetEarliestSession(1)
require.NoError(t, err)

// then
earliestSession, err := backend.GetEarliestSession()
require.NoError(t, err)

require.Equal(t, parachain.SessionIndex(1), *earliestSession)
}

func TestOverlayBackend_RecentDisputes(t *testing.T) {
t.Parallel()

// with
db, err := badger.Open(badger.DefaultOptions(t.TempDir()))
require.NoError(t, err)

disputes := btree.New(32)

dispute1, err := newTestDispute(1, common.Hash{1}, DisputeStatusActive)
require.NoError(t, err)
disputes.ReplaceOrInsert(dispute1)

dispute2, err := newTestDispute(2, common.Hash{2}, DisputeStatusConcludedFor)
require.NoError(t, err)
disputes.ReplaceOrInsert(dispute2)

// when
backend := NewOverlayBackend(db)
err = backend.SetRecentDisputes(disputes)
require.NoError(t, err)

// then
recentDisputes, err := backend.GetRecentDisputes()
require.NoError(t, err)

require.Equal(t, disputes, recentDisputes)
}

func TestOverlayBackend_CandidateVotes(t *testing.T) {
t.Parallel()

// with
db, err := badger.Open(badger.DefaultOptions(t.TempDir()))
require.NoError(t, err)

candidateVotes1 := newTestCandidateVotes()

// when
backend := NewOverlayBackend(db)
err = backend.SetCandidateVotes(1, common.Hash{1}, candidateVotes1)
require.NoError(t, err)

// then
candidateVotes, err := backend.GetCandidateVotes(1, common.Hash{1})
require.NoError(t, err)

require.Equal(t, candidateVotes1, candidateVotes)
}
Loading

0 comments on commit b0b8ff1

Please sign in to comment.