Skip to content
This repository has been archived by the owner on Jan 24, 2025. It is now read-only.

Commit

Permalink
Feat: trigger acceptance only after the winning state has been attested
Browse files Browse the repository at this point in the history
  • Loading branch information
hmoog committed Mar 27, 2024
1 parent 615177f commit 7aa74c6
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 6 deletions.
3 changes: 0 additions & 3 deletions pkg/core/acceptance/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ const (
// Pending is the state of pending spenders.
Pending State = iota

// PreAccepted is the state of pre-accepted spenders.
PreAccepted

// Accepted is the state of accepted spenders.
Accepted

Expand Down
15 changes: 15 additions & 0 deletions pkg/core/weight/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ type Value struct {
// validatorsWeight is the second tier which tracks weight in a non-cumulative manner (BFT style).
validatorsWeight int64

// attestorsWeight is the third tier which tracks weight in a non-cumulative manner (BFT style).
attestorsWeight int64

// acceptanceState is the final tier which determines the decision of the spender.
acceptanceState acceptance.State
}
Expand Down Expand Up @@ -55,6 +58,18 @@ func (v Value) SetValidatorsWeight(weight int64) Value {
return v
}

// AttestorsWeight returns the weight of the validators.
func (v Value) AttestorsWeight() int64 {
return v.attestorsWeight
}

// SetAttestorsWeight sets the weight of the attestors and returns the new Value.
func (v Value) SetAttestorsWeight(weight int64) Value {
v.attestorsWeight = weight

return v
}

// AcceptanceState returns the acceptance state of the Value.
func (v Value) AcceptanceState() acceptance.State {
return v.acceptanceState
Expand Down
57 changes: 55 additions & 2 deletions pkg/core/weight/weight.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type Weight struct {
// Voters is the set of voters contributing to the weight
Voters ds.Set[account.SeatIndex]

// Attestors is the set of attestors contributing to the weight.
Attestors ds.Set[account.SeatIndex]

// value is the current weight Value.
value Value

Expand All @@ -27,8 +30,9 @@ type Weight struct {
// New creates a new Weight instance.
func New() *Weight {
w := &Weight{
Voters: ds.NewSet[account.SeatIndex](),
OnUpdate: event.New1[Value](),
Voters: ds.NewSet[account.SeatIndex](),
Attestors: ds.NewSet[account.SeatIndex](),
OnUpdate: event.New1[Value](),
}

return w
Expand Down Expand Up @@ -103,6 +107,41 @@ func (w *Weight) DeleteVoter(seat account.SeatIndex) *Weight {
return w
}

// AddAttestor adds the given voter to the list of Attestors, updates the weight and returns the Weight (for chaining).
func (w *Weight) AddAttestor(seat account.SeatIndex) *Weight {
if added := w.Attestors.Add(seat); added {
if newValue, valueUpdated := w.updateAttestorsWeight(); valueUpdated {
w.OnUpdate.Trigger(newValue)
}
}

return w
}

// DeleteAttestor removes the given voter from the list of Attestors, updates the weight and returns the Weight (for chaining).
func (w *Weight) DeleteAttestor(seat account.SeatIndex) *Weight {
if deleted := w.Attestors.Delete(seat); deleted {
if newValue, valueUpdated := w.updateAttestorsWeight(); valueUpdated {
w.OnUpdate.Trigger(newValue)
}
}

return w
}

// ResetAttestors removes all voters from the list of Attestors, updates the weight and returns the Weight (for chaining).
func (w *Weight) ResetAttestors() *Weight {
if w.Attestors.Size() >= 1 {
w.Attestors.Clear()

if newValue, valueUpdated := w.updateAttestorsWeight(); valueUpdated {
w.OnUpdate.Trigger(newValue)
}
}

return w
}

func (w *Weight) updateValidatorsWeight() (Value, bool) {
w.mutex.Lock()
defer w.mutex.Unlock()
Expand All @@ -117,6 +156,20 @@ func (w *Weight) updateValidatorsWeight() (Value, bool) {
return w.value, false
}

func (w *Weight) updateAttestorsWeight() (Value, bool) {
w.mutex.Lock()
defer w.mutex.Unlock()

newAttestorsWeight := int64(w.Attestors.Size())
if w.value.AttestorsWeight() != newAttestorsWeight {
w.value = w.value.SetAttestorsWeight(newAttestorsWeight)

return w.value, true
}

return w.value, false
}

// AcceptanceState returns the acceptance state of the weight.
func (w *Weight) AcceptanceState() acceptance.State {
w.mutex.RLock()
Expand Down
13 changes: 12 additions & 1 deletion pkg/protocol/engine/mempool/spenddag/spenddagv1/spender.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func NewSpender[SpenderID, ResourceID spenddag.IDType, VoteRank spenddag.VoteRan
c.preferredInstead = c

c.unhookAcceptanceMonitoring = c.Weight.OnUpdate.Hook(func(value weight.Value) {
if value.AcceptanceState().IsPending() && value.ValidatorsWeight() >= c.acceptanceThreshold() {
if value.AcceptanceState().IsPending() && value.ValidatorsWeight() >= c.acceptanceThreshold() && value.AttestorsWeight() >= c.acceptanceThreshold() {
c.setAcceptanceState(acceptance.Accepted)
}
}).Unhook
Expand Down Expand Up @@ -219,6 +219,17 @@ func (c *Spender[SpenderID, ResourceID, VoteRank]) ApplyVote(vote *vote.Vote[Vot
// update the latest vote
c.LatestVotes.Set(vote.Voter, vote)

// track attestors when we reach the acceptance threshold
if c.Weight.Value().ValidatorsWeight() > c.acceptanceThreshold() {
if vote.IsLiked() {
c.Weight.AddAttestor(vote.Voter)
} else {
c.Weight.DeleteAttestor(vote.Voter)
}
} else {
c.Weight.ResetAttestors()
}

// abort if the vote does not change the opinion of the validator
if exists && latestVote.IsLiked() == vote.IsLiked() {
return
Expand Down

0 comments on commit 7aa74c6

Please sign in to comment.