diff --git a/node/app/db_console.go b/node/app/db_console.go index 864863a..9c064bc 100644 --- a/node/app/db_console.go +++ b/node/app/db_console.go @@ -886,11 +886,11 @@ func logoVersion(width int) string { out += " ####################################### ########\n" out += " ############################# ##\n" out += " \n" - out += " Quilibrium Node - v1.2.11 – Dawn\n" + out += " Quilibrium Node - v1.2.12 – Dawn\n" out += " \n" out += " DB Console\n" } else { - out = "Quilibrium Node - v1.2.11 – Dawn - DB Console\n" + out = "Quilibrium Node - v1.2.12 – Dawn - DB Console\n" } return out } diff --git a/node/consensus/consensus_engine.go b/node/consensus/consensus_engine.go index ff82928..2afcb43 100644 --- a/node/consensus/consensus_engine.go +++ b/node/consensus/consensus_engine.go @@ -59,5 +59,5 @@ func GetMinimumVersion() []byte { } func GetVersion() []byte { - return []byte{0x01, 0x02, 0x0B} + return []byte{0x01, 0x02, 0x0C} } diff --git a/node/consensus/time/data_time_reel.go b/node/consensus/time/data_time_reel.go index e291ca2..5c14889 100644 --- a/node/consensus/time/data_time_reel.go +++ b/node/consensus/time/data_time_reel.go @@ -280,6 +280,7 @@ func (d *DataTimeReel) runLoop() { } d.addPending(selector, parent, distance, frame) + d.processPending(d.head, frame) continue } @@ -293,24 +294,25 @@ func (d *DataTimeReel) runLoop() { if frame.FrameNumber-d.head.FrameNumber != 1 || parent.Cmp(headSelector) != 0 { d.logger.Debug( - "frame has has gap or is non-descendent, add pending", + "frame has has gap or is non-descendent, fork choice", zap.Bool("has_gap", frame.FrameNumber-d.head.FrameNumber != 1), zap.String("parent_selector", parent.Text(16)), zap.String("head_selector", headSelector.Text(16)), ) - d.addPending(selector, parent, distance, frame) + d.forkChoice(frame, distance) + d.processPending(d.head, frame) continue } // Otherwise set it as the next and process all pending d.setHead(frame, distance) - d.processPending(d.head) + d.processPending(d.head, frame) } else if d.head.FrameNumber == frame.FrameNumber { // frames are equivalent, no need to act if bytes.Equal(d.head.Output, frame.Output) { d.logger.Debug("equivalent frame") - d.processPending(d.head) + d.processPending(d.head, frame) continue } @@ -333,13 +335,13 @@ func (d *DataTimeReel) runLoop() { ) d.totalDistance.Sub(d.totalDistance, d.headDistance) d.setHead(frame, distance) - d.processPending(d.head) + d.processPending(d.head, frame) continue } // Choose fork d.forkChoice(frame, distance) - d.processPending(d.head) + d.processPending(d.head, frame) } else { d.logger.Debug("frame is lower height") @@ -371,7 +373,7 @@ func (d *DataTimeReel) runLoop() { } d.addPending(selector, parent, distance, frame) - d.processPending(d.head) + d.processPending(d.head, frame) } } case <-d.done: @@ -461,23 +463,34 @@ func (d *DataTimeReel) addPending( func (d *DataTimeReel) processPending( frame *protobufs.ClockFrame, + lastReceived *protobufs.ClockFrame, ) { - d.logger.Debug("process pending") + d.logger.Debug( + "process pending", + zap.Int("pending_frame_numbers", len(d.pending)), + ) frameNumbers := []uint64{} for f := range d.pending { frameNumbers = append(frameNumbers, f) + d.logger.Debug( + "pending per frame number", + zap.Uint64("pending_frame_number", f), + zap.Int("pending_frames", len(d.pending[f])), + ) } sort.Slice(frameNumbers, func(i, j int) bool { - return frameNumbers[i] < frameNumbers[j] + return frameNumbers[i] > frameNumbers[j] }) - limit := 8 + + lastSelector, err := lastReceived.GetSelector() + if err != nil { + panic(err) + } + for _, f := range frameNumbers { if f < d.head.FrameNumber { delete(d.pending, f) } - if limit <= 0 { - return - } nextPending := d.pending[f] d.logger.Debug( @@ -495,11 +508,17 @@ func (d *DataTimeReel) processPending( continue } // Pull the next - if nextPending != nil { + for len(nextPending) != 0 { d.logger.Debug("try process next") next := nextPending[0] - d.pending[f] = - d.pending[f][1:] + d.pending[f] = d.pending[f][1:] + if f == lastReceived.FrameNumber && next.selector.Cmp(lastSelector) == 0 { + d.pending[f] = append(d.pending[f], next) + if len(d.pending[f]) == 1 { + nextPending = nil + } + continue + } nextFrame, err := d.clockStore.GetParentDataClockFrame( d.filter, @@ -510,17 +529,17 @@ func (d *DataTimeReel) processPending( panic(err) } if nextFrame != nil { - d.pending[f] = append(d.pending[f], next) d.logger.Debug("next found, send frame back in") go func() { d.frames <- nextFrame }() - limit-- + return } if len(d.pending[f]) == 0 { d.logger.Debug("last next processing, clear list") delete(d.pending, f) + nextPending = nil } } } @@ -642,16 +661,58 @@ func (d *DataTimeReel) forkChoice( leftIndex := d.head rightIndex := frame leftTotal := new(big.Int).Set(d.headDistance) + overweight := big.NewInt(0) rightTotal := new(big.Int).Set(distance) left := d.head.ParentSelector right := frame.ParentSelector rightReplaySelectors := [][]byte{} + for rightIndex.FrameNumber > leftIndex.FrameNumber { + rightReplaySelectors = append( + append( + [][]byte{}, + right, + ), + rightReplaySelectors..., + ) + + rightIndex, err = d.clockStore.GetParentDataClockFrame( + d.filter, + rightIndex.FrameNumber-1, + rightIndex.ParentSelector, + ) + if err != nil { + // If lineage cannot be verified, set it for later + if errors.Is(err, store.ErrNotFound) { + d.addPending(selector, parentSelector, distance, frame) + return + } else { + panic(err) + } + } + + right = rightIndex.ParentSelector + + rightIndexDistance, err := d.GetDistance(rightIndex) + if err != nil { + panic(err) + } + + // We accumulate right on left when right is longer because we cannot know + // where the left will lead and don't want it to disadvantage our comparison + overweight.Add(overweight, rightIndexDistance) + rightTotal.Add(rightTotal, rightIndexDistance) + } + // Walk backwards through the parents, until we find a matching parent // selector: for !bytes.Equal(left, right) { - d.logger.Debug("scan backwards") + d.logger.Debug( + "scan backwards", + zap.String("left_parent", hex.EncodeToString(leftIndex.ParentSelector)), + zap.String("right_parent", hex.EncodeToString(rightIndex.ParentSelector)), + ) rightReplaySelectors = append( append( @@ -703,11 +764,13 @@ func (d *DataTimeReel) forkChoice( frameNumber := rightIndex.FrameNumber + overweight.Add(overweight, leftTotal) + // Choose new fork based on lightest distance sub-tree - if rightTotal.Cmp(leftTotal) > 0 { + if rightTotal.Cmp(overweight) > 0 { d.logger.Debug("proposed fork has greater distance", zap.String("right_total", rightTotal.Text(16)), - zap.String("left_total", leftTotal.Text(16)), + zap.String("left_total", overweight.Text(16)), ) d.addPending(selector, parentSelector, distance, frame) return diff --git a/node/consensus/time/data_time_reel_test.go b/node/consensus/time/data_time_reel_test.go index 05ea84b..19854bc 100644 --- a/node/consensus/time/data_time_reel_test.go +++ b/node/consensus/time/data_time_reel_test.go @@ -1,6 +1,7 @@ package time_test import ( + "bytes" "fmt" "strings" "sync" @@ -213,9 +214,18 @@ func TestDataTimeReel(t *testing.T) { datawg := sync.WaitGroup{} datawg.Add(1) dataFrameCh := d.NewFrameCh() + targetFrameParentSelector := []byte{} go func() { - for i := 0; i < 41; i++ { - dataFrames = append(dataFrames, <-dataFrameCh) + for { + frame := <-dataFrameCh + dataFrames = append(dataFrames, frame) + if frame.FrameNumber == 40 && bytes.Equal( + frame.ParentSelector, + targetFrameParentSelector, + ) { + break + } + } datawg.Done() }() @@ -377,6 +387,9 @@ func TestDataTimeReel(t *testing.T) { 10, ) insertFrames = append(insertFrames, suppressedFrame) + if i == 39 { + targetFrameParentSelector = suppressedFrame.ParentSelector + } frame, err = prover.ProveDataClockFrame( frame, [][]byte{}, @@ -404,18 +417,10 @@ func TestDataTimeReel(t *testing.T) { datawg.Wait() - for i := 0; i < 41; i++ { - assert.NotNil(t, dataFrames[i]) - - if i >= 40 { - assert.Equal(t, uint64(i), dataFrames[i].FrameNumber) - assert.Equal( - t, - optimalKeySet[i-31], - dataFrames[i].GetPublicKeySignatureEd448().PublicKey.KeyValue, - ) - } else { - assert.Equal(t, uint64(i+1), dataFrames[i].FrameNumber) - } - } + assert.Equal(t, uint64(40), dataFrames[len(dataFrames)-1].FrameNumber) + assert.Equal( + t, + optimalKeySet[len(optimalKeySet)-1], + dataFrames[len(dataFrames)-1].GetPublicKeySignatureEd448().PublicKey.KeyValue, + ) } diff --git a/node/main.go b/node/main.go index 8dc5639..bc1f5d5 100644 --- a/node/main.go +++ b/node/main.go @@ -306,5 +306,5 @@ func printLogo() { func printVersion() { fmt.Println(" ") - fmt.Println(" Quilibrium Node - v1.2.11 – Dawn") + fmt.Println(" Quilibrium Node - v1.2.12 – Dawn") } diff --git a/node/store/clock.go b/node/store/clock.go index 9d20847..3e92d74 100644 --- a/node/store/clock.go +++ b/node/store/clock.go @@ -860,7 +860,7 @@ func (p *PebbleClockStore) GetParentDataClockFrame( } else if err != nil && errors.Is(err, pebble.ErrNotFound) { data, closer, err = p.db.Get(clockDataFrameKey(filter, frameNumber)) if err != nil { - return nil, errors.Wrap(err, "get parent data clock frame") + return nil, errors.Wrap(ErrNotFound, "get parent data clock frame") } check = true }