Skip to content

Commit 6a9bfe4

Browse files
committed
Merge in WIP voting-fixes branch (#2298)
Vote for whole layer if hare results missing Lower tortoise thresholds for testnet Increase verbosity Vote for zero pattern instead Update test to match modified logic Improve log output Stick with old newly good threshold Minor test cleanup Make test more readable Update threshold and test Update votes, test passes Update second test to match new thresholds Cleanup threshold param Cleanup test Gofmt
1 parent 49559f0 commit 6a9bfe4

File tree

4 files changed

+85
-86
lines changed

4 files changed

+85
-86
lines changed

miner/block_builder.go

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -217,30 +217,29 @@ func (t *BlockBuilder) getVotes(id types.LayerID) ([]types.BlockID, error) {
217217
// not genesis, get from hare
218218
bottom, top := calcHdistRange(id, t.hdist)
219219

220-
if res, err := t.hareResult.GetResult(bottom); err != nil { // no result for bottom, take the whole layer
221-
t.With().Warning("Could not get result for bottom layer. Adding the whole layer instead.", log.Err(err),
222-
log.FieldNamed("bottom", bottom), log.FieldNamed("top", top), log.FieldNamed("hdist", t.hdist))
223-
ids, e := t.meshProvider.LayerBlockIds(bottom)
224-
if e != nil {
225-
t.With().Error("could not set votes to whole layer", log.Err(e))
226-
return nil, e
220+
// add votes
221+
// for layers that are missing hare votes, we vote for the "zero pattern", i.e., nothing at all.
222+
// this is the only way to guarantee consensus if the hare isn't working (without self-healing).
223+
// note that "top" here is set to one layer before the current layer, so hare should have
224+
// finished for this layer by now.
225+
for i := bottom; i <= top; i++ {
226+
if res, err := t.hareResult.GetResult(i); err != nil {
227+
t.With().Warning("could not get hare result for layer in hdist range, voting for zero pattern",
228+
i,
229+
log.Err(err),
230+
log.FieldNamed("bottom", bottom),
231+
log.FieldNamed("top", top),
232+
log.FieldNamed("currentLayer", id),
233+
log.FieldNamed("hdist", t.hdist))
234+
// no hare result, don't vote for anything
235+
} else {
236+
// use hare result to set votes
237+
t.With().Info("adding votes for layer (using hare result)",
238+
i,
239+
log.FieldNamed("currentLayer", id),
240+
log.Int("numVotes", len(res)))
241+
votes = append(votes, res...)
227242
}
228-
229-
// set votes to whole layer
230-
votes = ids
231-
} else { // got result, just set
232-
votes = res
233-
}
234-
235-
// add rest of hdist range
236-
for i := bottom + 1; i <= top; i++ {
237-
res, err := t.hareResult.GetResult(i)
238-
if err != nil {
239-
t.With().Warning("could not get result for layer in range", i, log.Err(err),
240-
log.FieldNamed("bottom", bottom), log.FieldNamed("top", top), log.FieldNamed("hdist", t.hdist))
241-
continue
242-
}
243-
votes = append(votes, res...)
244243
}
245244

246245
votes = filterUnknownBlocks(votes, t.meshProvider.GetBlock)

miner/builder_test.go

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,6 @@ func TestBlockBuilder_getVotes(t *testing.T) {
674674

675675
// no bottom
676676
bb.meshProvider = &mockMesh{b: allblocks} // assume all blocks exist in DB --> no filtering applied
677-
allids := []types.BlockID{b1.ID(), b2.ID(), b3.ID(), b4.ID(), b5.ID(), b6.ID(), b7.ID()}
678677
mh = newMockResult()
679678
mh.err = errors.New("no result")
680679
b1arr := mh.set(bottom + 1)
@@ -683,15 +682,9 @@ func TestBlockBuilder_getVotes(t *testing.T) {
683682
b, err = bb.getVotes(id)
684683
r.Nil(err)
685684
var exp []types.BlockID
686-
exp = append(exp, allids...)
687685
exp = append(exp, b1arr...)
688686
exp = append(exp, tarr...)
689687
r.Equal(exp, b)
690-
691-
// errExample on layer request
692-
bb.meshProvider = &mockMesh{b: nil, err: errExample}
693-
b, err = bb.getVotes(id)
694-
r.Equal(errExample, err)
695688
}
696689

697690
func TestBlockBuilder_createBlock(t *testing.T) {
@@ -702,14 +695,14 @@ func TestBlockBuilder_createBlock(t *testing.T) {
702695
block2 := types.NewExistingBlock(6, []byte(rand.String(8)))
703696
block3 := types.NewExistingBlock(6, []byte(rand.String(8)))
704697
bs := []*types.Block{block1, block2, block3}
705-
st := []types.BlockID{block1.ID(), block2.ID(), block3.ID()}
706698
builder1 := createBlockBuilder("a", n1, bs)
707699

700+
// error from hare means no votes
708701
builder1.hareResult = &mockResult{err: errExample, ids: nil}
709702
builder1.AtxDb = atxDbMock{}
710703
b, err := builder1.createBlock(7, types.ATXID{}, types.BlockEligibilityProof{}, nil, nil)
711704
r.Nil(err)
712-
r.Equal(st, b.BlockVotes)
705+
r.Nil(b.BlockVotes) // expect empty slice
713706

714707
builder1.hareResult = &mockResult{err: nil, ids: nil}
715708
b, err = builder1.createBlock(7, types.ATXID{}, types.BlockEligibilityProof{}, nil, nil)

tortoise/ninja_tortoise.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,15 @@ import (
1515
type vec [2]int
1616
type patternID uint32 // this hash does not include the layer id
1717

18-
const ( // Threshold
19-
window = 10
20-
globalThreshold = 0.6
18+
const (
19+
// These two thresholds control how tortoise votes on blocks and layers (patterns).
20+
// The first is used when voting for or against blocks (in a layer/pattern).
21+
// The second is used when voting for or against a layer (to promote it to newly-good as a pbase candidate).
22+
// They should be independent, but the existing tortoise tests expect the second to be a fixed ratio of the first.
23+
// NOTE: These have been lowered (from 0.6 and 0.5, respectively) for the testnet.
24+
globalThreshold = 0.5
25+
newLayerThreshold = 0.5 * (globalThreshold / 0.6) // adjust to match global threshold
26+
window = 10
2127
)
2228

2329
var ( // correction vectors type
@@ -294,7 +300,7 @@ func getIdsFromSet(bids map[types.BlockID]struct{}) patternID {
294300
}
295301

296302
func globalOpinion(v vec, layerSize int, delta float64) vec {
297-
threshold := float64(globalThreshold*delta) * float64(layerSize)
303+
threshold := globalThreshold * delta * float64(layerSize)
298304
if float64(v[0]) > threshold {
299305
return support
300306
} else if float64(v[1]) > threshold {
@@ -383,10 +389,10 @@ func (ni *ninjaTortoise) findMinimalNewlyGoodLayer(lyr *types.Layer) types.Layer
383389
// todo do this as part of previous for if possible
384390
// for each p that was updated and not the good layer of j check if it is the good layer
385391
for p := range sUpdated {
386-
// if a majority supports p (p is good)
392+
// if a minimum threshold supports p (p is good)
387393
// according to tal we dont have to know the exact amount, we can multiply layer size by number of layers
388394
jGood, found := ni.TGood[j]
389-
threshold := 0.5 * float64(types.LayerID(ni.AvgLayerSize)*(ni.Last-p.Layer()))
395+
threshold := newLayerThreshold * float64(types.LayerID(ni.AvgLayerSize)*(ni.Last-p.Layer()))
390396
if (jGood != p || !found) && float64(ni.TSupport[p]) > threshold {
391397
ni.TGood[p.Layer()] = p
392398
// if p is the new minimal good layer

tortoise/ninja_tortoise_test.go

Lines changed: 49 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/spacemeshos/go-spacemesh/mesh"
88
"github.com/spacemeshos/go-spacemesh/rand"
99
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
1011
"math"
1112
"os"
1213
"runtime"
@@ -352,12 +353,12 @@ func createLayer2(index types.LayerID, view *types.Layer, votes []*types.Layer,
352353
for idx, pat := range patterns {
353354
for _, id := range pat {
354355
b := votes[idx].Blocks()[id]
355-
bl.AddVote(types.BlockID(b.ID()))
356+
bl.AddVote(b.ID())
356357
}
357358
}
358359
if view != nil && len(view.Blocks()) > 0 {
359360
for _, prevBloc := range view.Blocks() {
360-
bl.AddView(types.BlockID(prevBloc.ID()))
361+
bl.AddView(prevBloc.ID())
361362
}
362363
}
363364

@@ -374,88 +375,88 @@ func TestNinjaTortoise_LayerWithNoVotes(t *testing.T) {
374375
mdb := getInMemMesh()
375376
alg := newNinjaTortoise(200, mdb, 5, lg)
376377

377-
l := createLayer2(0, nil, []*types.Layer{}, 154)
378+
l := createLayer2(0, nil, []*types.Layer{}, 128)
378379
AddLayer(mdb, l)
379380
alg.handleIncomingLayer(l)
380381

381-
l1 := createLayer2(1, l, []*types.Layer{l}, 141)
382+
l1 := createLayer2(1, l, []*types.Layer{l}, 117)
382383
AddLayer(mdb, l1)
383384
alg.handleIncomingLayer(l1)
384385

385-
l2 := createLayer2(2, l1, []*types.Layer{l1, l}, 129)
386+
l2 := createLayer2(2, l1, []*types.Layer{l1, l}, 107)
386387
AddLayer(mdb, l2)
387388
alg.handleIncomingLayer(l2)
388389

389-
l3 := createLayer2(3, l2, []*types.Layer{l2, l1, l}, 132)
390+
l3 := createLayer2(3, l2, []*types.Layer{l2, l1, l}, 110)
390391
AddLayer(mdb, l3)
391392
alg.handleIncomingLayer(l3)
392393

393-
l4 := createLayer2(4, l3, []*types.Layer{l3, l2, l1, l}, 138)
394+
l4 := createLayer2(4, l3, []*types.Layer{l3, l2, l1, l}, 115)
394395
AddLayer(mdb, l4)
395396
alg.handleIncomingLayer(l4)
396397

397-
l5 := createLayer2(5, l4, []*types.Layer{l4, l3, l2, l1, l}, 158)
398+
l5 := createLayer2(5, l4, []*types.Layer{l4, l3, l2, l1, l}, 131)
398399
AddLayer(mdb, l5)
399400
alg.handleIncomingLayer(l5)
400401

401-
l6 := createLayer2(6, l5, []*types.Layer{l5, l4, l3, l2, l1}, 155)
402+
l6 := createLayer2(6, l5, []*types.Layer{l5, l4, l3, l2, l1}, 129)
402403
AddLayer(mdb, l6)
403404
alg.handleIncomingLayer(l6)
404405
//
405-
l7 := createLayer2(7, l6, []*types.Layer{l6, l5, l4, l3, l2}, 130)
406+
l7 := createLayer2(7, l6, []*types.Layer{l6, l5, l4, l3, l2}, 108)
406407
AddLayer(mdb, l7)
407408
alg.handleIncomingLayer(l7)
408409

409-
l8 := createLayer2(8, l7, []*types.Layer{l6, l5, l4, l3}, 150)
410+
l8 := createLayer2(8, l7, []*types.Layer{l6, l5, l4, l3}, 125)
410411
AddLayer(mdb, l8)
411412
alg.handleIncomingLayer(l8)
412413

413-
l9 := createLayer2(9, l8, []*types.Layer{l8, l7, l6, l5, l4}, 134)
414+
l9 := createLayer2(9, l8, []*types.Layer{l8, l7, l6, l5, l4}, 112)
414415
AddLayer(mdb, l9)
415416
alg.handleIncomingLayer(l9)
416417

417-
l10 := createLayer2(10, l9, []*types.Layer{l9, l8, l7, l6, l5}, 148)
418+
l10 := createLayer2(10, l9, []*types.Layer{l9, l8, l7, l6, l5}, 124)
418419
AddLayer(mdb, l10)
419420
alg.handleIncomingLayer(l10)
420421

421-
l11 := createLayer2(11, l10, []*types.Layer{l10, l9, l8, l7, l6}, 147)
422+
l11 := createLayer2(11, l10, []*types.Layer{l10, l9, l8, l7, l6}, 122)
422423
AddLayer(mdb, l11)
423424
alg.handleIncomingLayer(l11)
424425

425-
assert.True(t, alg.PBase.Layer() == 7)
426+
require.Equal(t, types.LayerID(7), alg.PBase.Layer(), "unexpected pbase")
426427

427-
//now l7 one votes to be contextually valid in the eyes of layer 12 good pattern
428-
l12 := createLayer2(12, l11, []*types.Layer{l11, l10, l9, l8, l7}, 171)
428+
// now l7 one votes to be contextually valid in the eyes of layer 12 good pattern
429+
l12 := createLayer2(12, l11, []*types.Layer{l11, l10, l9, l8, l7}, 142)
429430
AddLayer(mdb, l12)
430431
alg.handleIncomingLayer(l12)
431432

432-
assert.True(t, alg.PBase.Layer() == 7)
433+
require.Equal(t, types.LayerID(7), alg.PBase.Layer(), "unexpected pbase")
433434

434-
l13 := createLayer2(13, l12, []*types.Layer{l12, l11, l10, l9, l8}, 126)
435+
l13 := createLayer2(13, l12, []*types.Layer{l12, l11, l10, l9, l8}, 105)
435436
AddLayer(mdb, l13)
436437
alg.handleIncomingLayer(l13)
437438

438-
//now l7 has the exact amount of votes to be contextually valid which will make 12 good pattern complete
439-
l121 := createLayer2(12, l11, []*types.Layer{l11, l10, l9, l8, l7}, 1)
440-
AddLayer(mdb, l121)
441-
alg.handleIncomingLayer(l121)
439+
// now l7 has the exact amount of votes to be contextually valid which will make 12 good pattern complete
440+
l12b := createLayer2(12, l11, []*types.Layer{l11, l10, l9, l8, l7}, 1)
441+
AddLayer(mdb, l12b)
442+
alg.handleIncomingLayer(l12b)
442443

443-
assert.True(t, alg.PBase.Layer() == 7)
444+
require.Equal(t, types.LayerID(7), alg.PBase.Layer(), "unexpected pbase")
444445

445-
l14 := createLayer2(14, l13, []*types.Layer{l13, l12, l121, l11, l10, l9}, 148)
446+
l14 := createLayer2(14, l13, []*types.Layer{l13, l12, l12b, l11, l10, l9}, 123)
446447
AddLayer(mdb, l14)
447448
alg.handleIncomingLayer(l14)
448449

449-
l15 := createLayer2(15, l14, []*types.Layer{l14, l13, l121, l12, l11, l10}, 150)
450+
l15 := createLayer2(15, l14, []*types.Layer{l14, l13, l12b, l12, l11, l10}, 125)
450451
AddLayer(mdb, l15)
451452
alg.handleIncomingLayer(l15)
452453

453-
assert.True(t, alg.PBase.Layer() == 7)
454+
require.Equal(t, types.LayerID(7), alg.PBase.Layer(), "unexpected pbase")
454455

455-
l16 := createLayer2(16, l15, []*types.Layer{l15, l14, l121, l13, l12, l11}, 121)
456+
l16 := createLayer2(16, l15, []*types.Layer{l15, l14, l12b, l13, l12, l11}, 100)
456457
AddLayer(mdb, l16)
457458
alg.handleIncomingLayer(l16)
458-
assert.True(t, alg.PBase.Layer() == 15)
459+
require.Equal(t, types.LayerID(15), alg.PBase.Layer(), "unexpected pbase")
459460
}
460461

461462
func TestNinjaTortoise_LayerWithNoVotes2(t *testing.T) {
@@ -464,76 +465,76 @@ func TestNinjaTortoise_LayerWithNoVotes2(t *testing.T) {
464465
mdb := getInMemMesh()
465466
alg := newNinjaTortoise(200, mdb, 5, lg)
466467

467-
l := createLayer2(0, nil, []*types.Layer{}, 154)
468+
l := createLayer2(0, nil, []*types.Layer{}, 128)
468469
AddLayer(mdb, l)
469470
alg.handleIncomingLayer(l)
470471

471-
l1 := createLayer2(1, l, []*types.Layer{l}, 141)
472+
l1 := createLayer2(1, l, []*types.Layer{l}, 117)
472473
AddLayer(mdb, l1)
473474
alg.handleIncomingLayer(l1)
474475

475-
l2 := createLayer2(2, l1, []*types.Layer{l1, l}, 129)
476+
l2 := createLayer2(2, l1, []*types.Layer{l1, l}, 107)
476477
AddLayer(mdb, l2)
477478
alg.handleIncomingLayer(l2)
478479

479-
l3 := createLayer2(3, l2, []*types.Layer{l2, l1, l}, 132)
480+
l3 := createLayer2(3, l2, []*types.Layer{l2, l1, l}, 116)
480481
AddLayer(mdb, l3)
481482
alg.handleIncomingLayer(l3)
482483

483-
l4 := createLayer2(4, l3, []*types.Layer{l3, l2, l1, l}, 138)
484+
l4 := createLayer2(4, l3, []*types.Layer{l3, l2, l1, l}, 115)
484485
AddLayer(mdb, l4)
485486
alg.handleIncomingLayer(l4)
486487

487-
l5 := createLayer2(5, l4, []*types.Layer{l4, l3, l2, l1, l}, 158)
488+
l5 := createLayer2(5, l4, []*types.Layer{l4, l3, l2, l1, l}, 132)
488489
AddLayer(mdb, l5)
489490
alg.handleIncomingLayer(l5)
490491

491-
l6 := createLayer2(6, l5, []*types.Layer{l5, l4, l3, l2, l1}, 155)
492+
l6 := createLayer2(6, l5, []*types.Layer{l5, l4, l3, l2, l1}, 129)
492493
AddLayer(mdb, l6)
493494
alg.handleIncomingLayer(l6)
494495
//
495-
l7 := createLayer2(7, l6, []*types.Layer{l6, l5, l4, l3, l2}, 130)
496+
l7 := createLayer2(7, l6, []*types.Layer{l6, l5, l4, l3, l2}, 108)
496497
AddLayer(mdb, l7)
497498
alg.handleIncomingLayer(l7)
498499

499-
l8 := createLayer2(8, l7, []*types.Layer{l6, l5, l4, l3}, 150)
500+
l8 := createLayer2(8, l7, []*types.Layer{l6, l5, l4, l3}, 125)
500501
AddLayer(mdb, l8)
501502
alg.handleIncomingLayer(l8)
502503

503-
l9 := createLayer2(9, l8, []*types.Layer{l8, l7, l6, l5, l4}, 134)
504+
l9 := createLayer2(9, l8, []*types.Layer{l8, l7, l6, l5, l4}, 112)
504505
AddLayer(mdb, l9)
505506
alg.handleIncomingLayer(l9)
506507

507-
l10 := createLayer2(10, l9, []*types.Layer{l9, l8, l7, l6, l5}, 148)
508+
l10 := createLayer2(10, l9, []*types.Layer{l9, l8, l7, l6, l5}, 123)
508509
AddLayer(mdb, l10)
509510
alg.handleIncomingLayer(l10)
510511

511-
l11 := createLayer2(11, l10, []*types.Layer{l10, l9, l8, l7, l6}, 147)
512+
l11 := createLayer2(11, l10, []*types.Layer{l10, l9, l8, l7, l6}, 122)
512513
AddLayer(mdb, l11)
513514
alg.handleIncomingLayer(l11)
514-
assert.True(t, alg.PBase.Layer() == 7)
515+
require.Equal(t, types.LayerID(7), alg.PBase.Layer(), "unexpected pbase")
515516

516517
// l7 is missing exactly 1 vote to be contextually valid which will make 12 complete
517-
l12 := createLayer2(12, l11, []*types.Layer{l11, l10, l9, l8, l7}, 171)
518+
l12 := createLayer2(12, l11, []*types.Layer{l11, l10, l9, l8, l7}, 142)
518519
AddLayer(mdb, l12)
519520
alg.handleIncomingLayer(l12)
520521

521-
l13 := createLayer2(13, l12, []*types.Layer{l12, l11, l10, l9, l8}, 126)
522+
l13 := createLayer2(13, l12, []*types.Layer{l12, l11, l10, l9, l8}, 105)
522523
AddLayer(mdb, l13)
523524
alg.handleIncomingLayer(l13)
524525

525-
l14 := createLayer2(14, l13, []*types.Layer{l13, l12, l11, l10, l9}, 148)
526+
l14 := createLayer2(14, l13, []*types.Layer{l13, l12, l11, l10, l9}, 123)
526527
AddLayer(mdb, l14)
527528
alg.handleIncomingLayer(l14)
528529

529-
l15 := createLayer2(15, l14, []*types.Layer{l14, l13, l12, l11, l10}, 150)
530+
l15 := createLayer2(15, l14, []*types.Layer{l14, l13, l12, l11, l10}, 125)
530531
AddLayer(mdb, l15)
531532
alg.handleIncomingLayer(l15)
532533

533-
l16 := createLayer2(16, l15, []*types.Layer{l15, l14, l13, l12, l11}, 121)
534+
l16 := createLayer2(16, l15, []*types.Layer{l15, l14, l13, l12, l11}, 101)
534535
AddLayer(mdb, l16)
535536
alg.handleIncomingLayer(l16)
536-
assert.True(t, alg.PBase.Layer() == 7)
537+
require.Equal(t, types.LayerID(7), alg.PBase.Layer(), "unexpected pbase")
537538
}
538539

539540
func TestNinjaTortoise_OneMoreLayerWithNoVotes(t *testing.T) {

0 commit comments

Comments
 (0)