Skip to content

Commit

Permalink
add backend
Browse files Browse the repository at this point in the history
  • Loading branch information
kanishkatn committed Jul 3, 2023
1 parent 95947f5 commit 9aa4e94
Show file tree
Hide file tree
Showing 2 changed files with 271 additions and 100 deletions.
129 changes: 29 additions & 100 deletions dot/parachain/dispute/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@ package dispute

import (
"bytes"
"fmt"
"github.com/ChainSafe/gossamer/dot/parachain/dispute/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/parachain"
"github.com/ChainSafe/gossamer/pkg/scale"
"github.com/emirpasic/gods/maps/treemap"
"github.com/google/btree"
"time"
)

// CandidateHash represents a candidate hash.
type CandidateHash common.Hash

// Comparator for ordering of disputes for candidate.
type Comparator struct {
SessionIndex parachain.SessionIndex
CandidateHash CandidateHash
}

// Less returns true if the current item is less than the other item
// it uses the Key to determine the order
// CandidateVotes tracked votes for a candidate, for the purpose of dispute resolution.
type CandidateVotes struct {
candidateReceipt parachain.CandidateReceipt
valid *treemap.Map
invalid *treemap.Map
}

// Less returns true if the current dispute item is less than the other item
// it uses the Comparator to determine the order
func (d Dispute) Less(than btree.Item) bool {
other := than.(*Dispute)

Expand All @@ -30,100 +38,14 @@ func (d Dispute) Less(than btree.Item) bool {
return d.comparator.SessionIndex < other.comparator.SessionIndex
}

type CandidateVotes struct {
candidateReceipt parachain.CandidateReceipt
valid *treemap.Map
invalid *treemap.Map
}

// Dispute is a dispute for a candidate.
// It is used as an item in the btree.BTree ordered by session index and candidate hash.
type Dispute struct {
comparator *Comparator
disputeStatus *Status
}

func NewCandidateVotes() *CandidateVotes {
return &CandidateVotes{
valid: treemap.NewWithIntComparator(),
invalid: treemap.NewWithIntComparator(),
}
}

type Active struct{}

func (Active) Index() uint {
return 0
}

type ConcludedFor struct {
Since uint64
}

func (ConcludedFor) Index() uint {
return 1
}

type ConcludedAgainst struct {
Since uint64
}

func (ConcludedAgainst) Index() uint {
return 2
}

type Confirmed struct{}

func (Confirmed) Index() uint {
return 3
}

type Status scale.VaryingDataType

// Set will set a VaryingDataTypeValue using the underlying VaryingDataType
func (ds *Status) Set(val scale.VaryingDataTypeValue) (err error) {
vdt := scale.VaryingDataType(*ds)
err = vdt.Set(val)
if err != nil {
return fmt.Errorf("setting value to varying data type: %w", err)
}
*ds = Status(vdt)
return nil
}

// Value returns the value from the underlying VaryingDataType
func (ds *Status) Value() (scale.VaryingDataTypeValue, error) {
vdt := scale.VaryingDataType(*ds)
return vdt.Value()
}

// TODO: check if we can avoid using pointers
func (ds *Status) ConcludedAt() (*uint64, error) {
vdt := scale.VaryingDataType(*ds)
val, err := vdt.Value()
if err != nil {
return nil, fmt.Errorf("getting value from DisputeStatus vdt: %w", err)
}

switch v := val.(type) {
case Active, Confirmed:
return nil, nil
case ConcludedFor:
return &v.Since, nil
case ConcludedAgainst:
return &v.Since, nil
default:
return nil, fmt.Errorf("invalid dispute status type")
}
}

func NewDisputeStatus() (Status, error) {
vdt, err := scale.NewVaryingDataType(Active{}, ConcludedFor{}, ConcludedAgainst{}, Confirmed{})
if err != nil {
return Status{}, fmt.Errorf("creating new dispute status vdt: %w", err)
}

return Status(vdt), nil
disputeStatus *types.DisputeStatus
}

// Backend is the backend for the dispute coordinator module.
type Backend interface {
// GetEarliestSession returns the earliest session index, if any.
GetEarliestSession() (*parachain.SessionIndex, error)
Expand All @@ -133,14 +55,15 @@ type Backend interface {
GetCandidateVotes(session parachain.SessionIndex, candidateHash CandidateHash) (*CandidateVotes, error)

// GetActiveDisputes returns the active disputes, if any.
GetActiveDisputes() (*btree.BTree, error)
GetActiveDisputes(now int64) (*btree.BTree, error)

// TODO: need to explore further to see if we need these
SetEarliestSession(session parachain.SessionIndex)
SetRecentDisputes(recentDisputes *btree.BTree)
SetCandidateVotes(session parachain.SessionIndex, candidateHash CandidateHash, votes *CandidateVotes)
}

// backend implements Backend.
type backend struct {
// TODO: db
earliestSession *parachain.SessionIndex
Expand Down Expand Up @@ -200,23 +123,29 @@ func (b backend) SetCandidateVotes(session parachain.SessionIndex, candidateHash
b.candidateVotes[key] = votes
}

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

func (b backend) GetActiveDisputes() (*btree.BTree, error) {
func (b backend) GetActiveDisputes(now int64) (*btree.BTree, error) {
activeDisputes := btree.New(32)

b.recentDisputes.Ascend(func(i btree.Item) bool {
d := i.(*Dispute)
concludedAt, err := d.disputeStatus.ConcludedAt()
if err == nil && concludedAt != nil && *concludedAt+uint64(ActiveDuration.Seconds()) > uint64(time.Now().Unix()) {
activeDisputes.ReplaceOrInsert(d)
dispute, ok := i.(*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
}

// NewBackend creates a new backend.
func NewBackend() Backend {
return &backend{
recentDisputes: btree.New(32),
Expand Down
Loading

0 comments on commit 9aa4e94

Please sign in to comment.