Skip to content

Commit

Permalink
add backend
Browse files Browse the repository at this point in the history
  • Loading branch information
kanishkatn committed Jun 23, 2023
1 parent 95947f5 commit e4e82d7
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 95 deletions.
128 changes: 33 additions & 95 deletions dot/parachain/dispute/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@ 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
// 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 +31,30 @@ 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()
disputeStatus *types.Status
}

// 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")
}
// CandidateVotes is a struct containing the votes for a candidate.
type CandidateVotes struct {
candidateReceipt parachain.CandidateReceipt
valid *treemap.Map
invalid *treemap.Map
}

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)
// NewCandidateVotesFromReceipt creates a new CandidateVotes from a candidate receipt.
func NewCandidateVotesFromReceipt(receipt parachain.CandidateReceipt) CandidateVotes {
return CandidateVotes{
valid: treemap.NewWithIntComparator(),
invalid: treemap.NewWithIntComparator(),
candidateReceipt: receipt,
}

return Status(vdt), nil
}

// 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 +64,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 +132,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
92 changes: 92 additions & 0 deletions dot/parachain/dispute/types/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package types

import (
"fmt"
"github.com/ChainSafe/gossamer/pkg/scale"
)

// Active is the status when the dispute is active.
type Active struct{}

// Index returns the index of the type Active.
func (Active) Index() uint {
return 0
}

// ConcludedFor is the status when the dispute is concluded for the candidate.
type ConcludedFor struct {
Since uint64
}

// Index returns the index of the type ConcludedFor.
func (ConcludedFor) Index() uint {
return 1
}

// ConcludedAgainst is the status when the dispute is concluded against the candidate.
type ConcludedAgainst struct {
Since uint64
}

// Index returns the index of the type ConcludedAgainst.
func (ConcludedAgainst) Index() uint {
return 2
}

// Confirmed is the status when the dispute is confirmed.
type Confirmed struct{}

// Index returns the index of the type Confirmed.
func (Confirmed) Index() uint {
return 3
}

// Status is the status of a dispute.
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()
}

// ConcludedAt returns the time the dispute was concluded, if it is concluded.
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")
}
}

// NewDisputeStatus creates a new dispute status.
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
}

0 comments on commit e4e82d7

Please sign in to comment.