feat: recalibrate self-test on the fly (#221)

This commit is contained in:
Cassandra Heart 2024-05-26 20:53:38 -05:00 committed by GitHub
parent 99702af0b7
commit 72d730d23f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 79 additions and 37 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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()
}

View File

@ -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,

View File

@ -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
}
}
}

View File

@ -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))
}

View File

@ -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")
}