From 72d730d23f91ce9a2bdd55617fb70f7a1193645e Mon Sep 17 00:00:00 2001 From: Cassandra Heart <7929478+CassOnMars@users.noreply.github.com> Date: Sun, 26 May 2024 20:53:38 -0500 Subject: [PATCH] feat: recalibrate self-test on the fly (#221) --- node/config/version.go | 6 ++- node/consensus/master/broadcast_messaging.go | 18 +++---- .../master/master_clock_consensus_engine.go | 30 +++++++++--- node/crypto/frame_prover.go | 2 +- node/crypto/wesolowski_frame_prover.go | 48 +++++++++++++------ node/crypto/wesolowski_frame_prover_test.go | 5 +- node/main.go | 7 ++- 7 files changed, 79 insertions(+), 37 deletions(-) diff --git a/node/config/version.go b/node/config/version.go index c5b5200..f9a1fe2 100644 --- a/node/config/version.go +++ b/node/config/version.go @@ -6,7 +6,7 @@ import ( ) func GetMinimumVersionCutoff() time.Time { - return time.Date(2024, time.May, 24, 4, 0, 0, 0, time.UTC) + return time.Date(2024, time.May, 28, 3, 0, 0, 0, time.UTC) } func GetMinimumVersion() []byte { @@ -27,3 +27,7 @@ func FormatVersion(version []byte) string { version[0], version[1], version[2], ) } + +func GetPatchNumber() byte { + return 0x01 +} diff --git a/node/consensus/master/broadcast_messaging.go b/node/consensus/master/broadcast_messaging.go index bbb4254..7d66767 100644 --- a/node/consensus/master/broadcast_messaging.go +++ b/node/consensus/master/broadcast_messaging.go @@ -132,6 +132,8 @@ func (e *MasterClockConsensusEngine) handleSelfTestReport( e.logger.Warn( "received invalid proof from peer", zap.String("peer_id", peer.ID(peerID).String()), + zap.Int("proof_size", len(report.Proof)), + zap.Uint32("cores", report.Cores), ) e.pubSub.SetPeerScore(peerID, -1000) return errors.Wrap(errors.New("invalid report"), "handle self test report") @@ -148,6 +150,7 @@ func (e *MasterClockConsensusEngine) handleSelfTestReport( return nil } + info.DifficultyMetric = report.DifficultyMetric info.MasterHeadFrame = report.MasterHeadFrame if info.Bandwidth <= 1048576 { @@ -169,7 +172,8 @@ func (e *MasterClockConsensusEngine) handleSelfTestReport( timestamp := binary.BigEndian.Uint64(proof[:8]) proof = proof[8:] - // Ignore outdated reports, give 3 minutes for propagation delay + // Ignore outdated reports, give 3 minutes + proof time for propagation + // delay if int64(timestamp) < (time.Now().UnixMilli() - (480 * 1000)) { return nil } @@ -255,6 +259,7 @@ func (e *MasterClockConsensusEngine) handleSelfTestReport( return nil } +// This does not publish any longer, frames strictly are picked up from sync func (e *MasterClockConsensusEngine) publishProof( frame *protobufs.ClockFrame, ) error { @@ -265,17 +270,6 @@ func (e *MasterClockConsensusEngine) publishProof( e.masterTimeReel.Insert(frame, false) - peers, err := e.GetMostAheadPeers() - if err != nil || len(peers) == 0 { - // publish if we don't see anyone (empty peer list) or if we're the most - // ahead: - e.report.MasterHeadFrame = frame.FrameNumber - - if err := e.publishMessage(e.filter, e.report); err != nil { - e.logger.Debug("error publishing message", zap.Error(err)) - } - } - e.state = consensus.EngineStateCollecting return nil diff --git a/node/consensus/master/master_clock_consensus_engine.go b/node/consensus/master/master_clock_consensus_engine.go index 3394054..bf47b52 100644 --- a/node/consensus/master/master_clock_consensus_engine.go +++ b/node/consensus/master/master_clock_consensus_engine.go @@ -181,7 +181,8 @@ func (e *MasterClockConsensusEngine) Start() <-chan error { panic(err) } - if head.FrameNumber > newFrame.FrameNumber || newFrame.FrameNumber-head.FrameNumber > 128 { + if head.FrameNumber > newFrame.FrameNumber || + newFrame.FrameNumber-head.FrameNumber > 128 { e.logger.Debug( "frame out of range, ignoring", zap.Uint64("number", newFrame.FrameNumber), @@ -238,6 +239,8 @@ func (e *MasterClockConsensusEngine) Start() <-chan error { go func() { // Let it sit until we at least have a few more peers inbound time.Sleep(30 * time.Second) + difficultyMetric := int64(100000) + skew := (difficultyMetric * 12) / 10 for { head, err := e.masterTimeReel.Head() @@ -246,22 +249,31 @@ func (e *MasterClockConsensusEngine) Start() <-chan error { } e.report.MasterHeadFrame = head.FrameNumber + e.report.DifficultyMetric = difficultyMetric parallelism := e.report.Cores - 1 - skew := (e.report.DifficultyMetric * 12) / 10 + challenge := binary.BigEndian.AppendUint64( []byte{}, e.report.MasterHeadFrame, ) challenge = append(challenge, e.pubSub.GetPeerID()...) - ts, proofs, err := e.frameProver.CalculateChallengeProof( - challenge, - parallelism, - skew, - ) + ts, proofs, nextDifficultyMetric, err := + e.frameProver.CalculateChallengeProof( + challenge, + parallelism, + skew, + ) if err != nil { panic(err) } + e.logger.Info( + "recalibrating difficulty metric", + zap.Int64("previous_difficulty_metric", difficultyMetric), + zap.Int64("next_difficulty_metric", nextDifficultyMetric), + ) + difficultyMetric = nextDifficultyMetric + skew = (nextDifficultyMetric * 12) / 10 proof := binary.BigEndian.AppendUint64([]byte{}, uint64(ts)) for i := 0; i < len(proofs); i++ { @@ -391,6 +403,10 @@ func (e *MasterClockConsensusEngine) performVerifyTest( ) e.pubSub.SetPeerScore(challenge.peerID, -1000) } else { + e.logger.Debug( + "received valid proof from peer", + zap.String("peer_id", peer.ID(challenge.peerID).String()), + ) info := e.peerInfoManager.GetPeerInfo(challenge.peerID) info.LastSeen = time.Now().UnixMilli() } diff --git a/node/crypto/frame_prover.go b/node/crypto/frame_prover.go index 71a2e07..778e233 100644 --- a/node/crypto/frame_prover.go +++ b/node/crypto/frame_prover.go @@ -55,7 +55,7 @@ type FrameProver interface { challenge []byte, parallelism uint32, skew int64, - ) (int64, [][]byte, error) + ) (int64, [][]byte, int64, error) VerifyChallengeProof( challenge []byte, timestamp int64, diff --git a/node/crypto/wesolowski_frame_prover.go b/node/crypto/wesolowski_frame_prover.go index 6b04325..ede59b5 100644 --- a/node/crypto/wesolowski_frame_prover.go +++ b/node/crypto/wesolowski_frame_prover.go @@ -15,6 +15,7 @@ import ( "go.uber.org/zap" "golang.org/x/crypto/sha3" "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/vdf" + "source.quilibrium.com/quilibrium/monorepo/node/config" "source.quilibrium.com/quilibrium/monorepo/node/keys" "source.quilibrium.com/quilibrium/monorepo/node/protobufs" "source.quilibrium.com/quilibrium/monorepo/node/tries" @@ -549,7 +550,9 @@ func (w *WesolowskiFrameProver) VerifyWeakRecursiveProof( } filter := proof[:len(frame.Filter)] - check := binary.BigEndian.Uint64(proof[len(frame.Filter) : len(frame.Filter)+8]) + check := binary.BigEndian.Uint64( + proof[len(frame.Filter) : len(frame.Filter)+8], + ) timestamp := binary.BigEndian.Uint64( proof[len(frame.Filter)+8 : len(frame.Filter)+16], ) @@ -600,26 +603,25 @@ func (w *WesolowskiFrameProver) CalculateChallengeProof( challenge []byte, parallelism uint32, skew int64, -) (int64, [][]byte, error) { - now := time.Now().UnixMilli() - input := binary.BigEndian.AppendUint64([]byte{}, uint64(now)) +) (int64, [][]byte, int64, error) { + now := time.Now() + nowMs := now.UnixMilli() + input := binary.BigEndian.AppendUint64([]byte{}, uint64(nowMs)) input = append(input, challenge...) outputs := make([][]byte, parallelism) wg := sync.WaitGroup{} wg.Add(int(parallelism)) + // 4.5 minutes = 270 seconds, one increment should be ten seconds + proofDuration := 270 * 1000 + calibratedDifficulty := (int64(proofDuration) * 10000) / skew for i := uint32(0); i < parallelism; i++ { i := i go func() { instanceInput := binary.BigEndian.AppendUint32([]byte{}, i) instanceInput = append(instanceInput, input...) - b := sha3.Sum256(input) - - // 4.5 minutes = 270 seconds, one increment should be ten seconds - proofDuration := 270 * 1000 - calibratedDifficulty := (int64(proofDuration) / skew) * 10000 - + b := sha3.Sum256(instanceInput) v := vdf.New(uint32(calibratedDifficulty), b) v.Execute() @@ -632,7 +634,10 @@ func (w *WesolowskiFrameProver) CalculateChallengeProof( } wg.Wait() - return now, outputs, nil + after := time.Since(now) + nextSkew := (skew * after.Milliseconds()) / int64(proofDuration) + + return nowMs, outputs, nextSkew, nil } func (w *WesolowskiFrameProver) VerifyChallengeProof( @@ -644,6 +649,10 @@ func (w *WesolowskiFrameProver) VerifyChallengeProof( input := binary.BigEndian.AppendUint64([]byte{}, uint64(timestamp)) input = append(input, challenge...) + if assertedDifficulty < 1 { + return false + } + for i := uint32(0); i < uint32(len(proof)); i++ { if len(proof[i]) != 516 { return false @@ -651,17 +660,28 @@ func (w *WesolowskiFrameProver) VerifyChallengeProof( instanceInput := binary.BigEndian.AppendUint32([]byte{}, i) instanceInput = append(instanceInput, input...) - b := sha3.Sum256(input) + b := sha3.Sum256(instanceInput) // 4.5 minutes = 270 seconds, one increment should be ten seconds proofDuration := 270 * 1000 skew := (assertedDifficulty * 12) / 10 - calibratedDifficulty := (int64(proofDuration) / skew) * 10000 + calibratedDifficulty := (int64(proofDuration) * 10000) / skew v := vdf.New(uint32(calibratedDifficulty), b) check := v.Verify([516]byte(proof[i])) if !check { - return false + // TODO: Remove after 2024-05-28 + if time.Now().Before(config.GetMinimumVersionCutoff()) { + calibratedDifficulty = (int64(proofDuration) / skew) * 10000 + + v = vdf.New(uint32(calibratedDifficulty), sha3.Sum256(input)) + check = v.Verify([516]byte(proof[i])) + if !check { + return false + } + } else { + return false + } } } diff --git a/node/crypto/wesolowski_frame_prover_test.go b/node/crypto/wesolowski_frame_prover_test.go index 78064cb..6bc039c 100644 --- a/node/crypto/wesolowski_frame_prover_test.go +++ b/node/crypto/wesolowski_frame_prover_test.go @@ -30,7 +30,10 @@ func TestMasterProve(t *testing.T) { func TestChallengeProof(t *testing.T) { l, _ := zap.NewProduction() w := crypto.NewWesolowskiFrameProver(l) - now, proofs, err := w.CalculateChallengeProof([]byte{0x01, 0x02, 0x03}, 3, 120000) + now, proofs, nextSkew, err := w.CalculateChallengeProof([]byte{0x01, 0x02, 0x03}, 3, 120000) assert.NoError(t, err) assert.True(t, w.VerifyChallengeProof([]byte{0x01, 0x02, 0x03}, now, 100000, proofs)) + now, proofs, _, err = w.CalculateChallengeProof([]byte{0x01, 0x02, 0x03}, 3, nextSkew*12/10) + assert.NoError(t, err) + assert.True(t, w.VerifyChallengeProof([]byte{0x01, 0x02, 0x03}, now, nextSkew, proofs)) } diff --git a/node/main.go b/node/main.go index 545df52..9d52c0a 100644 --- a/node/main.go +++ b/node/main.go @@ -735,6 +735,11 @@ func printLogo() { } func printVersion() { + patch := config.GetPatchNumber() + patchString := "" + if patch != 0x00 { + patchString = fmt.Sprintf("-p%d", patch) + } fmt.Println(" ") - fmt.Println(" Quilibrium Node - v" + config.GetVersionString() + " – Nebula") + fmt.Println(" Quilibrium Node - v" + config.GetVersionString() + patchString + " – Nebula") }