Skip to content

Commit

Permalink
Merge pull request #1908 from livepeer/nv/low-tickets
Browse files Browse the repository at this point in the history
pm: low ticket facevalue safeguard
  • Loading branch information
kyriediculous committed Jun 21, 2021
2 parents 6ae59d7 + af4a83a commit c218058
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- \#1879 Add mp4 download of recorded stream (@darkdarkdragon)
- \#1899 Record million pixels processed metric (@yondonfu)
- \#1888 Should not save (when recording) segments with zero video frames (@darkdarkdragon)
- \#1908 Prevent Broadcaster from sending low face value PM tickets (@kyriediculous)

#### Orchestrator

Expand Down
2 changes: 1 addition & 1 deletion core/orch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ func TestSufficientBalance_IsSufficient_ReturnsTrue(t *testing.T) {

err := orch.ProcessPayment(payment, manifestID)
assert.Nil(err)
recipient.On("EV").Return(big.NewRat(100, 1))
recipient.On("EV").Return(big.NewRat(100, 100))
assert.True(orch.SufficientBalance(ethcommon.BytesToAddress(payment.Sender), manifestID))
}

Expand Down
10 changes: 3 additions & 7 deletions pm/recipient.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ var maxWinProb = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewI
var paramsExpirationBlock = big.NewInt(10)
var paramsExpiryBuffer = int64(1)

var evMultiplier = big.NewInt(100)

// Recipient is an interface which describes an object capable
// of receiving tickets
type Recipient interface {
Expand Down Expand Up @@ -229,14 +231,8 @@ func (r *recipient) faceValue(sender ethcommon.Address) (*big.Int, error) {
// faceValue = txCost * txCostMultiplier
faceValue := new(big.Int).Mul(r.txCost(), big.NewInt(int64(r.cfg.TxCostMultiplier)))

// TODO: Consider setting faceValue to some value higher than
// EV in this case where the default faceValue < the desired EV.
// At the moment, for simplicity we just adjust faceValue to the
// desired EV in this case (which would result in winProb = 100%).
// In practice, EV should be smaller than the default faceValue
// so this shouldn't be a problem in most cases
if faceValue.Cmp(r.cfg.EV) < 0 {
faceValue = r.cfg.EV
faceValue = new(big.Int).Mul(r.cfg.EV, evMultiplier)
}

// Fetch current max float for sender
Expand Down
25 changes: 22 additions & 3 deletions pm/recipient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,9 +528,9 @@ func TestTicketParams(t *testing.T) {
params5, err := r.TicketParams(sender, big.NewRat(1, 1))
require.Nil(err)

assert.Equal(cfg.EV, params5.FaceValue)
assert.Equal(maxWinProb, params5.WinProb)

assert.Equal(new(big.Int).Mul(cfg.EV, evMultiplier), params5.FaceValue)
expWinProb := calcWinProb(params5.FaceValue, cfg.EV)
assert.Equal(expWinProb, params5.WinProb)
// Test default faceValue < EV and maxFloat < EV
sm.maxFloat = big.NewInt(0) // Set maxFloat to some value less than EV

Expand Down Expand Up @@ -675,3 +675,22 @@ func TestSenderNoncesCleanupLoop(t *testing.T) {
time.Sleep(20 * time.Millisecond)
assert.True(tm.blockNumSub.(*stubSubscription).unsubscribed)
}

func calcWinProb(faceValue, EV *big.Int) *big.Int {
// Return 0 if faceValue happens to be 0
if faceValue.Cmp(big.NewInt(0)) == 0 {
return big.NewInt(0)
}
// Return maxWinProb if faceValue = EV
if faceValue.Cmp(EV) == 0 {
return maxWinProb
}

m := new(big.Int)
x, m := new(big.Int).DivMod(maxWinProb, faceValue, m)
if m.Int64() != 0 {
return new(big.Int).Mul(EV, x.Add(x, big.NewInt(1)))
}
// Compute winProb as the numerator of a fraction over maxWinProb
return new(big.Int).Mul(EV, x)
}
4 changes: 4 additions & 0 deletions pm/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ func (s *sender) validateTicketParams(ticketParams *TicketParams, numTickets int
return nil
}

if ev.Cmp(new(big.Rat).SetInt(ticketParams.FaceValue)) >= 0 {
return fmt.Errorf("ticket faceValue too low faceValue=%v", ticketParams.FaceValue)
}

info, err := s.senderManager.GetSenderInfo(s.signer.Account().Address)
if err != nil {
return err
Expand Down
22 changes: 19 additions & 3 deletions pm/sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func TestCreateTicketBatch_GetSenderInfoError_ReturnsError(t *testing.T) {
func TestCreateTicketBatch_EVTooHigh_ReturnsError(t *testing.T) {
// Test single ticket EV too high
sender := defaultSender(t)
sender.maxEV = big.NewRat(100, 1)
sender.maxEV = big.NewRat(1, 1)

ticketParams := defaultTicketParams(t, RandAddress())
ticketParams.FaceValue = big.NewInt(202)
Expand All @@ -199,7 +199,7 @@ func TestCreateTicketBatch_EVTooHigh_ReturnsError(t *testing.T) {
assert.EqualError(t, err, expErrStr)

// Test multiple tickets EV too high
sender.maxEV = big.NewRat(102, 1)
sender.maxEV = big.NewRat(200, 1)

expErrStr = maxEVErrStr(ev.Mul(ev, new(big.Rat).SetInt64(2)), 2, sender.maxEV)
_, err = sender.CreateTicketBatch(sessionID, 2)
Expand Down Expand Up @@ -449,7 +449,7 @@ func TestValidateParams_ValidateSender(t *testing.T) {

func TestValidateTicketParams_EVTooHigh_ReturnsError(t *testing.T) {
sender := defaultSender(t)
sender.maxEV = big.NewRat(100, 1)
sender.maxEV = big.NewRat(1, 1)

ticketParams := &TicketParams{
FaceValue: big.NewInt(202),
Expand Down Expand Up @@ -490,6 +490,22 @@ func TestValidateTicketParams_FaceValueTooHigh_ReturnsError(t *testing.T) {
assert.EqualError(err, expErrStr)
}

func TestValidateTicketParams_FaceValueTooLow_ReturnsErr(t *testing.T) {
oldEVMul := evMultiplier
evMultiplier = big.NewInt(1)
sender := defaultSender(t)
sender.maxEV = big.NewRat(1000, 1)

ticketParams := &TicketParams{
FaceValue: big.NewInt(1000),
WinProb: maxWinProb,
ExpirationBlock: big.NewInt(100),
}
err := sender.ValidateTicketParams(ticketParams)
assert.EqualError(t, err, fmt.Sprintf("ticket faceValue too low faceValue=%v", ticketParams.FaceValue))
evMultiplier = oldEVMul
}

func TestValidateTicketParams_ExpiredParams_ReturnsError(t *testing.T) {
sender := defaultSender(t)
senderAddr := sender.signer.Account().Address
Expand Down

0 comments on commit c218058

Please sign in to comment.