Skip to content

Commit a0ffd75

Browse files
authored
winpost: Add more defenses against slashing (#190)
1 parent ecce871 commit a0ffd75

File tree

1 file changed

+27
-7
lines changed

1 file changed

+27
-7
lines changed

tasks/winning/winning_task.go

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,22 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don
105105
Epoch uint64
106106
BlockCIDs []BlockCID
107107
CompTime time.Time
108+
Won bool
108109
}
109110

110111
var details MiningTaskDetails
111112

112113
// First query to fetch from mining_tasks
113-
err = t.db.QueryRow(ctx, `SELECT sp_id, epoch, base_compute_time FROM mining_tasks WHERE task_id = $1`, taskID).Scan(&details.SpID, &details.Epoch, &details.CompTime)
114+
err = t.db.QueryRow(ctx, `SELECT sp_id, epoch, base_compute_time, won FROM mining_tasks WHERE task_id = $1`, taskID).Scan(&details.SpID, &details.Epoch, &details.CompTime, &details.Won)
114115
if err != nil {
115116
return false, xerrors.Errorf("query mining base info fail: %w", err)
116117
}
117118

119+
if details.Won {
120+
log.Errorw("WinPostTask.Do() task already won?!", "taskID", taskID)
121+
return true, nil
122+
}
123+
118124
// Second query to fetch from mining_base_block
119125
rows, err := t.db.Query(ctx, `SELECT block_cid FROM mining_base_block WHERE task_id = $1`, taskID)
120126
if err != nil {
@@ -415,12 +421,17 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don
415421
return false, xerrors.Errorf("failed to marshal block header: %w", err)
416422
}
417423

418-
_, err = t.db.Exec(ctx, `UPDATE mining_tasks
424+
n, err := t.db.Exec(ctx, `UPDATE mining_tasks
419425
SET won = true, mined_cid = $2, mined_header = $3, mined_at = $4
420-
WHERE task_id = $1`, taskID, blockMsg.Header.Cid(), string(bhjson), time.Now().UTC())
426+
WHERE task_id = $1 AND won = false`, taskID, blockMsg.Header.Cid(), string(bhjson), time.Now().UTC())
421427
if err != nil {
422428
return false, xerrors.Errorf("failed to update mining task: %w", err)
423429
}
430+
431+
if n == 0 {
432+
log.Warnw("WinPostTask task already mined?", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "block", blockMsg.Header.Cid())
433+
return true, xerrors.Errorf("block already mined?")
434+
}
424435
}
425436

426437
// wait until block timestamp
@@ -429,6 +440,12 @@ func (t *WinPostTask) Do(taskID harmonytask.TaskID, stillOwned func() bool) (don
429440
time.Sleep(time.Until(time.Unix(int64(blockMsg.Header.Timestamp), 0)))
430441
}
431442

443+
if !stillOwned() {
444+
// make sure no one else got their hands on the task
445+
log.Warnw("WinPostTask task no longer owned", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "block", blockMsg.Header.Cid())
446+
return true, xerrors.Errorf("task no longer owned")
447+
}
448+
432449
// submit block!!
433450
{
434451
log.Infow("WinPostTask submitting block", "tipset", types.LogCids(base.TipSet.Cids()), "miner", maddr, "round", round, "block", blockMsg.Header.Cid())
@@ -522,10 +539,13 @@ func (t *WinPostTask) TypeDetails() harmonytask.TaskTypeDetails {
522539
}
523540

524541
return harmonytask.TaskTypeDetails{
525-
Name: "WinPost",
526-
Max: t.max,
527-
MaxFailures: 3,
528-
Follows: nil,
542+
Name: "WinPost",
543+
Max: t.max,
544+
545+
// We're not allowing retry to be conservative. Retry in winningPoSt done badly can lead to slashing, and
546+
// that is generally worse than not mining a block. In general the task code is heavily defensive, and
547+
// retry should be fine, but not allowing it is just another layer preventing slashing.
548+
MaxFailures: 1,
529549
Cost: resources.Resources{
530550
Cpu: 1,
531551

0 commit comments

Comments
 (0)