Skip to content

Commit 610869a

Browse files
authored
feat: mini pomw proofs as part of peer manifest (#191)
1 parent b53616c commit 610869a

File tree

10 files changed

+326
-150
lines changed

10 files changed

+326
-150
lines changed

node/app/node.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"source.quilibrium.com/quilibrium/monorepo/node/consensus"
99
"source.quilibrium.com/quilibrium/monorepo/node/consensus/master"
1010
"source.quilibrium.com/quilibrium/monorepo/node/execution"
11-
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony"
1211
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony/application"
1312
"source.quilibrium.com/quilibrium/monorepo/node/keys"
1413
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
@@ -44,17 +43,14 @@ func newNode(
4443
clockStore store.ClockStore,
4544
keyManager keys.KeyManager,
4645
pubSub p2p.PubSub,
47-
ceremonyExecutionEngine *ceremony.CeremonyExecutionEngine,
46+
// execution engines wire in here
4847
engine consensus.ConsensusEngine,
4948
) (*Node, error) {
5049
if engine == nil {
5150
return nil, errors.New("engine must not be nil")
5251
}
5352

5453
execEngines := make(map[string]execution.ExecutionEngine)
55-
if ceremonyExecutionEngine != nil {
56-
execEngines[ceremonyExecutionEngine.GetName()] = ceremonyExecutionEngine
57-
}
5854

5955
return &Node{
6056
logger,

node/app/wire_gen.go

Lines changed: 2 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node/consensus/master/broadcast_messaging.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,21 @@ func (e *MasterClockConsensusEngine) handleSelfTestReport(
124124
return errors.Wrap(err, "handle self test report")
125125
}
126126

127+
// minimum proof size is one timestamp, one vdf proof, must match one fewer
128+
// than core count
129+
if len(report.Proof) < 516+8 ||
130+
((len(report.Proof)-8)/516) != int(report.Cores-1) {
131+
return errors.Wrap(errors.New("invalid report"), "handle self test report")
132+
}
133+
127134
info := e.peerInfoManager.GetPeerInfo(peerID)
128135
if info != nil {
136+
if (time.Now().UnixMilli() - info.LastSeen) < (270 * 1000) {
137+
return nil
138+
}
139+
129140
info.MasterHeadFrame = report.MasterHeadFrame
141+
130142
if info.Bandwidth <= 1048576 {
131143
go func() {
132144
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Minute)
@@ -141,6 +153,36 @@ func (e *MasterClockConsensusEngine) handleSelfTestReport(
141153
}
142154
}()
143155
}
156+
157+
proof := report.Proof
158+
timestamp := binary.BigEndian.Uint64(proof[:8])
159+
proof = proof[8:]
160+
161+
// Ignore outdated reports, give 3 minutes for propagation delay
162+
if int64(timestamp) < (time.Now().UnixMilli() - (480 * 1000)) {
163+
return nil
164+
}
165+
166+
challenge := binary.BigEndian.AppendUint64([]byte{}, report.MasterHeadFrame)
167+
challenge = append(challenge, peerID...)
168+
169+
proofs := make([][]byte, (len(report.Proof)-8)/516)
170+
for i := 0; i < len(proofs); i++ {
171+
proofs[i] = proof[i*516 : (i+1)*516]
172+
}
173+
if !e.frameProver.VerifyChallengeProof(
174+
challenge,
175+
int64(timestamp),
176+
report.DifficultyMetric,
177+
proofs,
178+
) {
179+
return errors.Wrap(
180+
errors.New("invalid report"),
181+
"handle self test report",
182+
)
183+
}
184+
185+
info.LastSeen = time.Now().UnixMilli()
144186
return nil
145187
}
146188

@@ -202,6 +244,8 @@ func (e *MasterClockConsensusEngine) handleSelfTestReport(
202244
}
203245
}()
204246

247+
info.LastSeen = time.Now().UnixMilli()
248+
205249
return nil
206250
}
207251

node/consensus/master/master_clock_consensus_engine.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"crypto/rand"
7+
"encoding/binary"
78
"encoding/hex"
89
"io"
910
"math/big"
@@ -233,11 +234,32 @@ func (e *MasterClockConsensusEngine) Start() <-chan error {
233234
}
234235

235236
e.report.MasterHeadFrame = head.FrameNumber
237+
parallelism := e.report.Cores - 1
238+
skew := (e.report.DifficultyMetric * 12) / 10
239+
challenge := binary.BigEndian.AppendUint64(
240+
[]byte{},
241+
e.report.MasterHeadFrame,
242+
)
243+
challenge = append(challenge, e.pubSub.GetPeerID()...)
244+
245+
ts, proofs, err := e.frameProver.CalculateChallengeProof(
246+
challenge,
247+
parallelism,
248+
skew,
249+
)
250+
if err != nil {
251+
panic(err)
252+
}
253+
254+
proof := binary.BigEndian.AppendUint64([]byte{}, uint64(ts))
255+
for i := 0; i < len(proofs); i++ {
256+
proof = append(proof, proofs[i]...)
257+
}
258+
e.report.Proof = proof
236259

237260
if err := e.publishMessage(e.filter, e.report); err != nil {
238261
e.logger.Debug("error publishing message", zap.Error(err))
239262
}
240-
time.Sleep(5 * time.Minute)
241263
}
242264
}()
243265

@@ -460,6 +482,7 @@ func (
460482
Memory: new(big.Int).SetBytes(peerManifest.Memory).Bytes(),
461483
Storage: new(big.Int).SetBytes(peerManifest.Storage).Bytes(),
462484
MasterHeadFrame: peerManifest.MasterHeadFrame,
485+
LastSeen: peerManifest.LastSeen,
463486
}
464487

465488
for _, capability := range peerManifest.Capabilities {

node/crypto/frame_prover.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,15 @@ type FrameProver interface {
5151
proof []byte,
5252
deepVerifier *protobufs.ClockFrame,
5353
) bool
54+
CalculateChallengeProof(
55+
challenge []byte,
56+
parallelism uint32,
57+
skew int64,
58+
) (int64, [][]byte, error)
59+
VerifyChallengeProof(
60+
challenge []byte,
61+
timestamp int64,
62+
assertedDifficulty int64,
63+
proof [][]byte,
64+
) bool
5465
}

node/crypto/wesolowski_frame_prover.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"crypto/rand"
77
"encoding/binary"
88
"math/big"
9+
"sync"
10+
"time"
911

1012
"github.com/cloudflare/circl/sign/ed448"
1113
"github.com/iden3/go-iden3-crypto/poseidon"
@@ -593,3 +595,75 @@ func (w *WesolowskiFrameProver) VerifyWeakRecursiveProof(
593595
return false
594596
}
595597
}
598+
599+
func (w *WesolowskiFrameProver) CalculateChallengeProof(
600+
challenge []byte,
601+
parallelism uint32,
602+
skew int64,
603+
) (int64, [][]byte, error) {
604+
now := time.Now().UnixMilli()
605+
input := binary.BigEndian.AppendUint64([]byte{}, uint64(now))
606+
input = append(input, challenge...)
607+
outputs := make([][]byte, parallelism)
608+
609+
wg := sync.WaitGroup{}
610+
wg.Add(int(parallelism))
611+
612+
for i := uint32(0); i < parallelism; i++ {
613+
i := i
614+
go func() {
615+
instanceInput := binary.BigEndian.AppendUint32([]byte{}, i)
616+
instanceInput = append(instanceInput, input...)
617+
b := sha3.Sum256(input)
618+
619+
// 4.5 minutes = 270 seconds, one increment should be ten seconds
620+
proofDuration := 270 * 1000
621+
calibratedDifficulty := (int64(proofDuration) / skew) * 10000
622+
623+
v := vdf.New(uint32(calibratedDifficulty), b)
624+
625+
v.Execute()
626+
o := v.GetOutput()
627+
628+
outputs[i] = make([]byte, 516)
629+
copy(outputs[i][:], o[:])
630+
wg.Done()
631+
}()
632+
}
633+
634+
wg.Wait()
635+
return now, outputs, nil
636+
}
637+
638+
func (w *WesolowskiFrameProver) VerifyChallengeProof(
639+
challenge []byte,
640+
timestamp int64,
641+
assertedDifficulty int64,
642+
proof [][]byte,
643+
) bool {
644+
input := binary.BigEndian.AppendUint64([]byte{}, uint64(timestamp))
645+
input = append(input, challenge...)
646+
647+
for i := uint32(0); i < uint32(len(proof)); i++ {
648+
if len(proof[i]) != 516 {
649+
return false
650+
}
651+
652+
instanceInput := binary.BigEndian.AppendUint32([]byte{}, i)
653+
instanceInput = append(instanceInput, input...)
654+
b := sha3.Sum256(input)
655+
656+
// 4.5 minutes = 270 seconds, one increment should be ten seconds
657+
proofDuration := 270 * 1000
658+
skew := (assertedDifficulty * 12) / 10
659+
calibratedDifficulty := (int64(proofDuration) / skew) * 10000
660+
661+
v := vdf.New(uint32(calibratedDifficulty), b)
662+
check := v.Verify([516]byte(proof[i]))
663+
if !check {
664+
return false
665+
}
666+
}
667+
668+
return true
669+
}

node/crypto/wesolowski_frame_prover_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,11 @@ func TestMasterProve(t *testing.T) {
2626
err = w.VerifyMasterClockFrame(next)
2727
assert.NoError(t, err)
2828
}
29+
30+
func TestChallengeProof(t *testing.T) {
31+
l, _ := zap.NewProduction()
32+
w := crypto.NewWesolowskiFrameProver(l)
33+
now, proofs, err := w.CalculateChallengeProof([]byte{0x01, 0x02, 0x03}, 3, 120000)
34+
assert.NoError(t, err)
35+
assert.True(t, w.VerifyChallengeProof([]byte{0x01, 0x02, 0x03}, now, 100000, proofs))
36+
}

node/p2p/peer_info_manager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type PeerManifest struct {
3939
Capabilities []Capability
4040
MasterHeadFrame uint64
4141
Bandwidth uint64
42+
LastSeen int64
4243
}
4344

4445
type InMemoryPeerInfoManager struct {

0 commit comments

Comments
 (0)