This commit is contained in:
Cassandra Heart 2024-02-13 01:04:56 -06:00 committed by GitHub
parent 1a1ef542d0
commit 0cd6b41f5d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 3821 additions and 2577 deletions

View File

@ -23,7 +23,7 @@ import (
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials/insecure"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/execution/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/tries"
@ -886,11 +886,11 @@ func logoVersion(width int) string {
out += " ####################################### ########\n"
out += " ############################# ##\n"
out += " \n"
out += " Quilibrium Node - v1.2.3 Dawn\n"
out += " Quilibrium Node - v1.2.4 Dawn\n"
out += " \n"
out += " DB Console\n"
} else {
out = "Quilibrium Node - v1.2.3 Dawn - DB Console\n"
out = "Quilibrium Node - v1.2.4 Dawn - DB Console\n"
}
return out
}

View File

@ -6,7 +6,7 @@ import (
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/consensus"
"source.quilibrium.com/quilibrium/monorepo/node/execution"
"source.quilibrium.com/quilibrium/monorepo/node/execution/ceremony"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/store"

View File

@ -9,7 +9,9 @@ import (
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/consensus"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/master"
"source.quilibrium.com/quilibrium/monorepo/node/execution/ceremony"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/store"
@ -54,6 +56,11 @@ var pubSubSet = wire.NewSet(
var engineSet = wire.NewSet(
wire.FieldsOf(new(*config.Config), "Engine"),
crypto.NewWesolowskiFrameProver,
wire.Bind(new(crypto.FrameProver), new(*crypto.WesolowskiFrameProver)),
crypto.NewKZGInclusionProver,
wire.Bind(new(crypto.InclusionProver), new(*crypto.KZGInclusionProver)),
time.NewMasterTimeReel,
ceremony.NewCeremonyExecutionEngine,
)

View File

@ -12,7 +12,9 @@ import (
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/consensus"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/master"
"source.quilibrium.com/quilibrium/monorepo/node/execution/ceremony"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/store"
@ -30,9 +32,12 @@ func NewNode(configConfig *config.Config) (*Node, error) {
p2PConfig := configConfig.P2P
blossomSub := p2p.NewBlossomSub(p2PConfig, zapLogger)
engineConfig := configConfig.Engine
wesolowskiFrameProver := crypto.NewWesolowskiFrameProver(zapLogger)
kzgInclusionProver := crypto.NewKZGInclusionProver(zapLogger)
masterTimeReel := time.NewMasterTimeReel(zapLogger, pebbleClockStore, engineConfig, wesolowskiFrameProver)
pebbleKeyStore := store.NewPebbleKeyStore(pebbleDB, zapLogger)
ceremonyExecutionEngine := ceremony.NewCeremonyExecutionEngine(zapLogger, engineConfig, fileKeyManager, blossomSub, pebbleClockStore, pebbleKeyStore)
masterClockConsensusEngine := master.NewMasterClockConsensusEngine(engineConfig, zapLogger, pebbleClockStore, fileKeyManager, blossomSub)
ceremonyExecutionEngine := ceremony.NewCeremonyExecutionEngine(zapLogger, engineConfig, fileKeyManager, blossomSub, wesolowskiFrameProver, kzgInclusionProver, pebbleClockStore, masterTimeReel, pebbleKeyStore)
masterClockConsensusEngine := master.NewMasterClockConsensusEngine(engineConfig, zapLogger, pebbleClockStore, fileKeyManager, blossomSub, wesolowskiFrameProver, masterTimeReel)
node, err := newNode(zapLogger, pebbleClockStore, fileKeyManager, blossomSub, ceremonyExecutionEngine, masterClockConsensusEngine)
if err != nil {
return nil, err
@ -77,7 +82,7 @@ var storeSet = wire.NewSet(wire.FieldsOf(new(*config.Config), "DB"), store.NewPe
var pubSubSet = wire.NewSet(wire.FieldsOf(new(*config.Config), "P2P"), p2p.NewBlossomSub, wire.Bind(new(p2p.PubSub), new(*p2p.BlossomSub)))
var engineSet = wire.NewSet(wire.FieldsOf(new(*config.Config), "Engine"), ceremony.NewCeremonyExecutionEngine)
var engineSet = wire.NewSet(wire.FieldsOf(new(*config.Config), "Engine"), crypto.NewWesolowskiFrameProver, wire.Bind(new(crypto.FrameProver), new(*crypto.WesolowskiFrameProver)), crypto.NewKZGInclusionProver, wire.Bind(new(crypto.InclusionProver), new(*crypto.KZGInclusionProver)), time.NewMasterTimeReel, ceremony.NewCeremonyExecutionEngine)
var consensusSet = wire.NewSet(master.NewMasterClockConsensusEngine, wire.Bind(
new(consensus.ConsensusEngine),

View File

@ -11,17 +11,13 @@ import (
"github.com/libp2p/go-libp2p/core/peer"
"github.com/pkg/errors"
"go.uber.org/zap"
"golang.org/x/crypto/sha3"
"golang.org/x/sync/errgroup"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/node/consensus"
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
)
func (e *CeremonyDataClockConsensusEngine) handleMessage(
@ -304,17 +300,8 @@ func (e *CeremonyDataClockConsensusEngine) handleClockFrameData(
return errors.Wrap(err, "handle clock frame data")
}
earliestFrame, _, count := e.frameProverTrie.Get(addr.Bytes())
_, latestFrame, _ := e.frameSeenProverTrie.Get(addr.Bytes())
if !isSync && frame.FrameNumber == latestFrame {
e.logger.Info(
"already received frame from address",
zap.Binary("address", address),
zap.Binary("filter", frame.Filter),
zap.Uint64("frame_number", frame.FrameNumber),
)
return nil
} else if frame.FrameNumber <= earliestFrame || count == 0 {
prover := e.frameProverTrie.FindNearest(addr.Bytes())
if !bytes.Equal(prover.External.Key, addr.Bytes()) {
e.logger.Info(
"prover not in trie at frame, address may be in fork",
zap.Binary("address", address),
@ -332,167 +319,14 @@ func (e *CeremonyDataClockConsensusEngine) handleClockFrameData(
zap.Int("proof_count", len(frame.AggregateProofs)),
)
if err := frame.VerifyDataClockFrame(); err != nil {
if err := e.frameProver.VerifyDataClockFrame(frame); err != nil {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
aggregateCommitments := []curves.PairingPoint{}
for i := 0; i < (len(frame.Input)-516)/74; i++ {
c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
frame.Input[516+(i*74) : 516+(i*74)+74],
)
if err != nil {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
aggregateCommitments = append(aggregateCommitments, c.(curves.PairingPoint))
}
for i, proof := range frame.AggregateProofs {
aggregatePoly := [][]curves.PairingScalar{}
commitments := []curves.PairingPoint{}
for _, commit := range proof.GetInclusionCommitments() {
switch commit.TypeUrl {
case protobufs.IntrinsicExecutionOutputType:
e.logger.Debug("confirming inclusion in aggregate")
digest := sha3.NewShake256()
_, err := digest.Write(commit.Data)
if err != nil {
e.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return errors.Wrap(err, "handle clock frame data")
}
expand := make([]byte, 1024)
_, err = digest.Read(expand)
if err != nil {
e.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return errors.Wrap(err, "handle clock frame data")
}
poly, err := e.prover.BytesToPolynomial(expand)
if err != nil {
e.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return errors.Wrap(err, "handle clock frame data")
}
evalPoly, err := qcrypto.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
),
16,
false,
)
if err != nil {
e.logger.Error(
"error performing fast fourier transform on key bundle",
zap.Error(err),
)
return errors.Wrap(err, "handle clock frame data")
}
e.logger.Debug(
"created fft of polynomial",
zap.Int("poly_size", len(evalPoly)),
)
aggregatePoly = append(aggregatePoly, evalPoly)
c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
commit.Commitment,
)
if err != nil {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
commitments = append(commitments, c.(curves.PairingPoint))
default:
e.logger.Debug("confirming inclusion in aggregate")
poly, err := e.prover.BytesToPolynomial(commit.Data)
if err != nil {
e.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return errors.Wrap(err, "handle clock frame data")
}
for i := 0; i < 128-len(poly); i++ {
poly = append(
poly,
curves.BLS48581G1().Scalar.Zero().(curves.PairingScalar),
)
}
evalPoly, err := qcrypto.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
),
128,
false,
)
if err != nil {
e.logger.Error(
"error performing fast fourier transform on key bundle",
zap.Error(err),
)
return errors.Wrap(err, "handle clock frame data")
}
e.logger.Debug(
"created fft of polynomial",
zap.Int("poly_size", len(evalPoly)),
)
aggregatePoly = append(aggregatePoly, evalPoly)
c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
commit.Commitment,
)
if err != nil {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
commitments = append(commitments, c.(curves.PairingPoint))
}
}
p, err := curves.BLS48581G1().Point.FromAffineCompressed(
proof.Proof,
)
if err != nil {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
result, err := e.prover.VerifyAggregateProof(
aggregatePoly,
commitments,
aggregateCommitments[i],
p.(curves.PairingPoint),
)
if err != nil {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
if !result {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(
errors.New("invalid proof"),
"handle clock frame data",
)
}
if err := e.inclusionProver.VerifyFrame(frame); err != nil {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
e.logger.Info(
@ -501,102 +335,23 @@ func (e *CeremonyDataClockConsensusEngine) handleClockFrameData(
zap.Binary("filter", frame.Filter),
zap.Uint64("frame_number", frame.FrameNumber),
)
masterFrame, err := e.clockStore.GetMasterClockFrame(
[]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
frame.FrameNumber-1,
)
if err != nil {
e.logger.Info("received frame with no known master, needs sync")
return nil
}
discriminator, err := masterFrame.GetSelector()
if err != nil {
return errors.Wrap(err, "handle clock frame data")
}
parentSelector, distance, selector, err :=
frame.GetParentSelectorAndDistance(discriminator)
if err != nil {
return errors.Wrap(err, "handle clock frame data")
}
if _, err := e.clockStore.GetParentDataClockFrame(
frame.Filter,
frame.FrameNumber-1,
frame.ParentSelector,
); errors.Is(err, store.ErrNotFound) {
// If this is a frame number higher than what we're already caught up to,
// push a request to fill the gap, unless we're syncing or it's in step,
// then just lazily seek.
from := e.frame.FrameNumber
if from >= frame.FrameNumber-1 {
from = frame.FrameNumber - 1
}
if err := e.publishMessage(e.filter, &protobufs.ClockFramesRequest{
Filter: e.filter,
FromFrameNumber: from,
ToFrameNumber: frame.FrameNumber,
}); err != nil {
e.logger.Error(
"could not publish clock frame parent request, skipping",
zap.Error(err),
)
}
}
txn, err := e.clockStore.NewTransaction()
if err != nil {
e.logger.Error("could not save candidate clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
if err := e.clockStore.PutCandidateDataClockFrame(
parentSelector.FillBytes(make([]byte, 32)),
distance.FillBytes(make([]byte, 32)),
selector.FillBytes(make([]byte, 32)),
frame,
txn,
); err != nil {
e.logger.Error("could not save candidate clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
if err := txn.Commit(); err != nil {
e.logger.Error("could not save candidate clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
if e.frame.FrameNumber < frame.FrameNumber {
e.latestFrameReceived = frame.FrameNumber
e.lastFrameReceivedAt = time.Now().UTC()
}
e.frameSeenProverTrie.Add(address, frame.FrameNumber)
e.dataTimeReel.Insert(frame)
return nil
}
func (e *CeremonyDataClockConsensusEngine) publishProof(
frame *protobufs.ClockFrame,
) error {
if e.state == consensus.EngineStatePublishing {
e.logger.Debug(
"publishing frame and aggregations",
zap.Uint64("frame_number", frame.FrameNumber),
e.logger.Debug(
"publishing frame and aggregations",
zap.Uint64("frame_number", frame.FrameNumber),
)
if err := e.publishMessage(e.filter, frame); err != nil {
return errors.Wrap(
err,
"publish proof",
)
if err := e.publishMessage(e.filter, frame); err != nil {
return errors.Wrap(
err,
"publish proof",
)
}
e.state = consensus.EngineStateCollecting
}
return nil

View File

@ -1,9 +1,9 @@
package ceremony
import (
"bytes"
"crypto"
"encoding/binary"
"math/big"
"sync"
"time"
@ -14,6 +14,7 @@ import (
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/consensus"
qtime "source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/execution"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
@ -53,7 +54,6 @@ type ChannelServer = protobufs.CeremonyService_GetPublicChannelServer
type CeremonyDataClockConsensusEngine struct {
protobufs.UnimplementedCeremonyServiceServer
frame *protobufs.ClockFrame
difficulty uint32
logger *zap.Logger
state consensus.EngineState
@ -61,6 +61,8 @@ type CeremonyDataClockConsensusEngine struct {
keyStore store.KeyStore
pubSub p2p.PubSub
keyManager keys.KeyManager
masterTimeReel *qtime.MasterTimeReel
dataTimeReel *qtime.DataTimeReel
provingKey crypto.Signer
provingKeyBytes []byte
provingKeyType keys.KeyType
@ -68,13 +70,11 @@ type CeremonyDataClockConsensusEngine struct {
lastFrameReceivedAt time.Time
latestFrameReceived uint64
frameProverTrie *tries.RollingFrecencyCritbitTrie
frameSeenProverTrie *tries.RollingFrecencyCritbitTrie
dependencyMap map[string]*anypb.Any
pendingCommits chan *anypb.Any
pendingCommitWorkers int64
prover *qcrypto.KZGProver
stagedKeyCommits InclusionMap
stagedKeyPolynomials PolynomialMap
inclusionProver qcrypto.InclusionProver
frameProver qcrypto.FrameProver
stagedLobbyStateTransitions *protobufs.CeremonyLobbyStateTransition
minimumPeersRequired int
@ -86,10 +86,8 @@ type CeremonyDataClockConsensusEngine struct {
syncingStatus SyncStatusType
syncingTarget []byte
previousHead *protobufs.ClockFrame
currentDistance *big.Int
engineMx sync.Mutex
dependencyMapMx sync.Mutex
stagedKeyCommitsMx sync.Mutex
stagedLobbyStateTransitionsMx sync.Mutex
peerMapMx sync.Mutex
peerAnnounceMapMx sync.Mutex
@ -112,6 +110,10 @@ func NewCeremonyDataClockConsensusEngine(
clockStore store.ClockStore,
keyStore store.KeyStore,
pubSub p2p.PubSub,
frameProver qcrypto.FrameProver,
inclusionProver qcrypto.InclusionProver,
masterTimeReel *qtime.MasterTimeReel,
dataTimeReel *qtime.DataTimeReel,
filter []byte,
seed []byte,
) *CeremonyDataClockConsensusEngine {
@ -139,6 +141,22 @@ func NewCeremonyDataClockConsensusEngine(
panic(errors.New("pubsub is nil"))
}
if frameProver == nil {
panic(errors.New("frame prover is nil"))
}
if inclusionProver == nil {
panic(errors.New("inclusion prover is nil"))
}
if masterTimeReel == nil {
panic(errors.New("master time reel is nil"))
}
if dataTimeReel == nil {
panic(errors.New("data time reel is nil"))
}
minimumPeersRequired := engineConfig.MinimumPeersRequired
if minimumPeersRequired == 0 {
minimumPeersRequired = 3
@ -150,7 +168,6 @@ func NewCeremonyDataClockConsensusEngine(
}
e := &CeremonyDataClockConsensusEngine{
frame: nil,
difficulty: difficulty,
logger: logger,
state: consensus.EngineStateStopped,
@ -169,17 +186,15 @@ func NewCeremonyDataClockConsensusEngine(
},
lastFrameReceivedAt: time.Time{},
frameProverTrie: &tries.RollingFrecencyCritbitTrie{},
frameSeenProverTrie: &tries.RollingFrecencyCritbitTrie{},
pendingCommits: make(chan *anypb.Any),
pendingCommitWorkers: engineConfig.PendingCommitWorkers,
prover: qcrypto.DefaultKZGProver(),
stagedKeyCommits: make(InclusionMap),
stagedKeyPolynomials: make(PolynomialMap),
inclusionProver: inclusionProver,
syncingStatus: SyncStatusNotSyncing,
peerAnnounceMap: map[string]*protobufs.CeremonyPeerListAnnounce{},
peerMap: map[string]*peerInfo{},
uncooperativePeersMap: map[string]*peerInfo{},
minimumPeersRequired: minimumPeersRequired,
frameProver: frameProver,
masterTimeReel: masterTimeReel,
dataTimeReel: dataTimeReel,
}
logger.Info("constructing consensus engine")
@ -205,30 +220,12 @@ func (e *CeremonyDataClockConsensusEngine) Start() <-chan error {
e.state = consensus.EngineStateLoading
e.logger.Info("loading last seen state")
latestFrame, err := e.clockStore.GetLatestDataClockFrame(
e.filter,
e.frameProverTrie,
)
if err != nil && !errors.Is(err, store.ErrNotFound) {
err := e.dataTimeReel.Start()
if err != nil {
panic(err)
}
candidateLatestFrame, err := e.clockStore.GetLatestCandidateDataClockFrame(
e.filter,
)
if err != nil && !errors.Is(err, store.ErrNotFound) {
panic(err)
}
if candidateLatestFrame != nil {
latestFrame = candidateLatestFrame
}
if latestFrame != nil {
e.setFrame(latestFrame)
} else {
latestFrame = e.CreateGenesisFrame(nil)
}
e.frameProverTrie = e.dataTimeReel.GetFrameProverTrie()
err = e.createCommunicationKeys()
if err != nil {
@ -255,10 +252,6 @@ func (e *CeremonyDataClockConsensusEngine) Start() <-chan error {
e.state = consensus.EngineStateCollecting
for i := int64(0); i < e.pendingCommitWorkers; i++ {
go e.handlePendingCommits(i)
}
go func() {
thresholdBeforeConfirming := 4
@ -269,8 +262,13 @@ func (e *CeremonyDataClockConsensusEngine) Start() <-chan error {
PeerList: []*protobufs.CeremonyPeer{},
}
frame, err := e.dataTimeReel.Head()
if err != nil {
panic(err)
}
timestamp := time.Now().UnixMilli()
msg := binary.BigEndian.AppendUint64([]byte{}, e.frame.FrameNumber)
msg := binary.BigEndian.AppendUint64([]byte{}, frame.FrameNumber)
msg = append(msg, consensus.GetVersion()...)
msg = binary.BigEndian.AppendUint64(msg, uint64(timestamp))
sig, err := e.pubSub.SignMessage(msg)
@ -282,7 +280,7 @@ func (e *CeremonyDataClockConsensusEngine) Start() <-chan error {
e.peerMap[string(e.pubSub.GetPeerID())] = &peerInfo{
peerId: e.pubSub.GetPeerID(),
multiaddr: "",
maxFrame: e.frame.FrameNumber,
maxFrame: frame.FrameNumber,
version: consensus.GetVersion(),
signature: sig,
publicKey: e.pubSub.GetPublicKey(),
@ -325,42 +323,7 @@ func (e *CeremonyDataClockConsensusEngine) Start() <-chan error {
}()
go func() {
e.logger.Info("waiting for peer list mappings")
// We need to re-tune this so that libp2p's peerstore activation threshold
// considers DHT peers to be correct:
time.Sleep(30 * time.Second)
for e.state < consensus.EngineStateStopping {
peerCount := e.pubSub.GetNetworkPeersCount()
if peerCount < e.minimumPeersRequired {
e.logger.Info(
"waiting for minimum peers",
zap.Int("peer_count", peerCount),
)
time.Sleep(1 * time.Second)
} else {
switch e.state {
case consensus.EngineStateCollecting:
currentFrame := latestFrame
if latestFrame, err = e.collect(latestFrame); err != nil {
e.logger.Error("could not collect", zap.Error(err))
e.state = consensus.EngineStateCollecting
latestFrame = currentFrame
}
case consensus.EngineStateProving:
currentFrame := latestFrame
if latestFrame, err = e.prove(latestFrame); err != nil {
e.logger.Error("could not prove", zap.Error(err))
e.state = consensus.EngineStateCollecting
latestFrame = currentFrame
}
case consensus.EngineStatePublishing:
if err = e.publishProof(latestFrame); err != nil {
e.logger.Error("could not publish", zap.Error(err))
e.state = consensus.EngineStateCollecting
}
}
}
}
e.runLoop()
}()
go func() {
@ -370,6 +333,86 @@ func (e *CeremonyDataClockConsensusEngine) Start() <-chan error {
return errChan
}
func (e *CeremonyDataClockConsensusEngine) runLoop() {
dataFrameCh := e.dataTimeReel.NewFrameCh()
e.logger.Info("waiting for peer list mappings")
// We need to re-tune this so that libp2p's peerstore activation threshold
// considers DHT peers to be correct:
time.Sleep(30 * time.Second)
for e.state < consensus.EngineStateStopping {
peerCount := e.pubSub.GetNetworkPeersCount()
if peerCount < e.minimumPeersRequired {
e.logger.Info(
"waiting for minimum peers",
zap.Int("peer_count", peerCount),
)
time.Sleep(1 * time.Second)
} else {
latestFrame, err := e.dataTimeReel.Head()
if err != nil {
panic(err)
}
select {
case dataFrame := <-dataFrameCh:
if latestFrame, err = e.collect(dataFrame); err != nil {
e.logger.Error("could not collect", zap.Error(err))
continue
}
go func() {
e.frameChan <- latestFrame
}()
var nextFrame *protobufs.ClockFrame
if nextFrame, err = e.prove(latestFrame); err != nil {
e.logger.Error("could not prove", zap.Error(err))
e.state = consensus.EngineStateCollecting
continue
}
if bytes.Equal(
e.frameProverTrie.FindNearest(e.provingKeyAddress).External.Key,
e.provingKeyAddress,
) {
if err = e.publishProof(nextFrame); err != nil {
e.logger.Error("could not publish", zap.Error(err))
e.state = consensus.EngineStateCollecting
}
}
case <-time.After(20 * time.Second):
e.logger.Info("no frames received, kicking off")
dataFrame, err := e.dataTimeReel.Head()
if err != nil {
panic(err)
}
if latestFrame, err = e.collect(dataFrame); err != nil {
e.logger.Error("could not collect", zap.Error(err))
continue
}
go func() {
e.frameChan <- latestFrame
}()
var nextFrame *protobufs.ClockFrame
if nextFrame, err = e.prove(latestFrame); err != nil {
e.logger.Error("could not prove", zap.Error(err))
e.state = consensus.EngineStateCollecting
continue
}
if e.frameProverTrie.Contains(e.provingKeyAddress) {
if err = e.publishProof(nextFrame); err != nil {
e.logger.Error("could not publish", zap.Error(err))
e.state = consensus.EngineStateCollecting
}
}
}
}
}
}
func (e *CeremonyDataClockConsensusEngine) Stop(force bool) <-chan error {
e.logger.Info("stopping ceremony consensus engine")
e.state = consensus.EngineStateStopping
@ -380,7 +423,12 @@ func (e *CeremonyDataClockConsensusEngine) Stop(force bool) <-chan error {
for name := range e.executionEngines {
name := name
go func(name string) {
err := <-e.UnregisterExecutor(name, e.frame.FrameNumber, force)
frame, err := e.dataTimeReel.Head()
if err != nil {
panic(err)
}
err = <-e.UnregisterExecutor(name, frame.FrameNumber, force)
if err != nil {
errChan <- err
}
@ -402,133 +450,17 @@ func (e *CeremonyDataClockConsensusEngine) Stop(force bool) <-chan error {
return errChan
}
func (e *CeremonyDataClockConsensusEngine) performSanityCheck(
frame *protobufs.ClockFrame,
) *protobufs.ClockFrame {
e.logger.Info("performing sanity check")
start := uint64(0)
idx := start
end := frame.FrameNumber + 1
var prior *protobufs.ClockFrame
for start < end {
tail := end
if start+16 < tail {
tail = start + 16
}
iter, err := e.clockStore.RangeDataClockFrames(
e.filter,
start,
tail,
)
if err != nil {
panic(err)
}
for iter.First(); iter.Valid(); iter.Next() {
v, err := iter.Value()
if err != nil {
panic(err)
}
if v.FrameNumber != idx {
e.logger.Warn(
"discontinuity found, attempting to fix",
zap.Uint64("expected_frame_number", idx),
zap.Uint64("found_frame_number", v.FrameNumber),
)
disc := v
for disc.FrameNumber-idx > 0 {
frames, err := e.clockStore.GetCandidateDataClockFrames(
e.filter,
disc.FrameNumber-1,
)
if err != nil {
panic(err)
}
found := false
for _, candidate := range frames {
selector, err := candidate.GetSelector()
if err != nil {
panic(err)
}
parentSelector, _, _, err := disc.GetParentSelectorAndDistance(nil)
if err != nil {
panic(err)
}
if selector.Cmp(parentSelector) == 0 {
found = true
_, priorTrie, err := e.clockStore.GetDataClockFrame(
e.filter,
prior.FrameNumber,
)
if err != nil {
panic(err)
}
txn, err := e.clockStore.NewTransaction()
if err != nil {
panic(err)
}
err = e.clockStore.PutDataClockFrame(
candidate,
priorTrie,
txn,
true,
)
if err != nil {
panic(err)
}
if err = txn.Commit(); err != nil {
panic(err)
}
disc = candidate
}
}
if !found {
e.logger.Error(
"could not resolve discontinuity, rewinding consensus head",
)
if err = iter.Close(); err != nil {
panic(err)
}
return prior
}
}
idx = v.FrameNumber
} else {
prior = v
}
idx++
}
if err = iter.Close(); err != nil {
panic(err)
}
start += 16
}
return frame
}
func (e *CeremonyDataClockConsensusEngine) GetDifficulty() uint32 {
return e.difficulty
}
func (e *CeremonyDataClockConsensusEngine) GetFrame() *protobufs.ClockFrame {
return e.frame
frame, err := e.dataTimeReel.Head()
if err != nil {
panic(err)
}
return frame
}
func (e *CeremonyDataClockConsensusEngine) GetState() consensus.EngineState {

File diff suppressed because it is too large Load Diff

View File

@ -15,13 +15,18 @@ func (e *CeremonyDataClockConsensusEngine) RegisterExecutor(
go func() {
for {
masterFrame, err := e.masterTimeReel.Head()
if err != nil {
panic(err)
}
logger.Info(
"awaiting frame",
zap.Uint64("current_frame", e.frame.FrameNumber),
zap.Uint64("current_frame", masterFrame.FrameNumber),
zap.Uint64("target_frame", frame),
)
newFrame := e.frame.FrameNumber
newFrame := masterFrame.FrameNumber
if newFrame >= frame {
logger.Info(
"injecting execution engine at frame",
@ -52,13 +57,18 @@ func (e *CeremonyDataClockConsensusEngine) UnregisterExecutor(
go func() {
for {
masterFrame, err := e.masterTimeReel.Head()
if err != nil {
panic(err)
}
logger.Info(
"awaiting frame",
zap.Uint64("current_frame", e.frame.FrameNumber),
zap.Uint64("current_frame", masterFrame.FrameNumber),
zap.Uint64("target_frame", frame),
)
newFrame := e.frame.FrameNumber
newFrame := masterFrame.FrameNumber
if newFrame >= frame {
logger.Info(
"removing execution engine at frame",

View File

@ -1,85 +0,0 @@
package ceremony
import (
"github.com/pkg/errors"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/anypb"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
func (e *CeremonyDataClockConsensusEngine) handlePendingCommits(
workerId int64,
) {
for {
msg := <-e.pendingCommits
switch msg.TypeUrl {
case protobufs.KeyBundleAnnouncementType:
if err := e.includeKeyBundle(msg); err != nil {
e.logger.Error(
"failed to include key bundle",
zap.Error(errors.Wrap(err, "handle pending commits")),
zap.Int64("worker_id", workerId),
)
}
}
}
}
func (e *CeremonyDataClockConsensusEngine) includeKeyBundle(
any *anypb.Any,
) error {
poly, err := e.prover.BytesToPolynomial(any.Value)
if err != nil {
e.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return errors.Wrap(err, "include key bundle")
}
for i := 0; i < 128-len(poly); i++ {
poly = append(
poly,
curves.BLS48581G1().Scalar.Zero().(curves.PairingScalar),
)
}
evalPoly, err := crypto.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
),
128,
false,
)
if err != nil {
e.logger.Error(
"error performing fast fourier transform on key bundle",
zap.Error(err),
)
return errors.Wrap(err, "include key bundle")
}
commitment, err := e.prover.Commit(evalPoly)
if err != nil {
e.logger.Error(
"error creating kzg commitment",
zap.Error(err),
)
return errors.Wrap(err, "include key bundle")
}
e.stagedKeyCommitsMx.Lock()
e.stagedKeyCommits[commitment] = &protobufs.InclusionCommitment{
Filter: e.filter,
TypeUrl: any.TypeUrl,
Data: any.Value,
Commitment: commitment.ToAffineCompressed(),
}
e.stagedKeyPolynomials[commitment] = evalPoly
e.stagedKeyCommitsMx.Unlock()
return nil
}

View File

@ -11,7 +11,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"source.quilibrium.com/quilibrium/monorepo/node/execution/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
@ -106,7 +106,12 @@ func (e *CeremonyDataClockConsensusEngine) GetCompressedSyncFrames(
}
}
max := e.frame.FrameNumber
head, err := e.dataTimeReel.Head()
if err != nil {
panic(err)
}
max := head.FrameNumber
to := request.ToFrameNumber
// We need to slightly rewind, to compensate for unconfirmed frame heads on a

View File

@ -51,13 +51,13 @@ type DataConsensusEngine interface {
}
func GetMinimumVersionCutoff() time.Time {
return time.Date(2024, time.January, 14, 7, 0, 0, 0, time.UTC)
return time.Date(2024, time.February, 13, 7, 0, 0, 0, time.UTC)
}
func GetMinimumVersion() []byte {
return []byte{0x01, 0x02, 0x03}
return []byte{0x01, 0x02, 0x04}
}
func GetVersion() []byte {
return []byte{0x01, 0x02, 0x03}
return []byte{0x01, 0x02, 0x04}
}

View File

@ -1,9 +1,7 @@
package master
import (
"bytes"
"strings"
"time"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/pkg/errors"
@ -92,17 +90,6 @@ func (e *MasterClockConsensusEngine) handleClockFrameData(
return errors.Wrap(err, "handle clock frame data")
}
if e.frame.FrameNumber > frame.FrameNumber {
e.logger.Debug(
"received anachronistic frame",
zap.Binary("sender", peerID),
zap.Binary("filter", frame.Filter),
zap.Uint64("frame_number", frame.FrameNumber),
zap.Int("proof_count", len(frame.AggregateProofs)),
)
return nil
}
if e.difficulty != frame.Difficulty {
e.logger.Debug(
"frame difficulty mismatched",
@ -122,65 +109,35 @@ func (e *MasterClockConsensusEngine) handleClockFrameData(
zap.Int("proof_count", len(frame.AggregateProofs)),
)
if err := frame.VerifyMasterClockFrame(); err != nil {
if err := e.frameProver.VerifyMasterClockFrame(frame); err != nil {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
if e.frame.FrameNumber < frame.FrameNumber {
if err := e.enqueueSeenFrame(frame); err != nil {
e.logger.Error("could not enqueue seen clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame data")
}
}
e.masterTimeReel.Insert(frame)
return nil
}
func (e *MasterClockConsensusEngine) enqueueSeenFrame(
frame *protobufs.ClockFrame,
) error {
e.seenFramesMx.Lock()
found := false
for _, f := range e.seenFrames {
if f.FrameNumber == frame.FrameNumber &&
bytes.Equal(f.Input, frame.Input) &&
f.Difficulty == frame.Difficulty &&
bytes.Equal(f.Output, frame.Output) {
found = true
}
}
if !found {
e.logger.Debug(
"enqueuing frame for consensus",
zap.Uint64("frame_number", frame.FrameNumber),
)
e.seenFrames = append(e.seenFrames, frame)
e.lastFrameReceivedAt = time.Now().UTC()
}
e.seenFramesMx.Unlock()
return nil
}
func (e *MasterClockConsensusEngine) publishProof(
frame *protobufs.ClockFrame,
) error {
if e.state == consensus.EngineStatePublishing {
e.logger.Debug(
"publishing frame",
zap.Uint64("frame_number", frame.FrameNumber),
)
e.enqueueSeenFrame(frame)
if err := e.publishMessage(e.filter, frame); err != nil {
return errors.Wrap(
err,
"publish proof",
)
}
e.logger.Debug(
"publishing frame",
zap.Uint64("frame_number", frame.FrameNumber),
)
e.state = consensus.EngineStateCollecting
e.masterTimeReel.Insert(frame)
if err := e.publishMessage(e.filter, frame); err != nil {
return errors.Wrap(
err,
"publish proof",
)
}
e.state = consensus.EngineStateCollecting
return nil
}

View File

@ -1,16 +1,11 @@
package master
import (
"bytes"
"encoding/binary"
"sort"
"time"
"github.com/pkg/errors"
"go.uber.org/zap"
"golang.org/x/crypto/sha3"
"source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/vdf"
"source.quilibrium.com/quilibrium/monorepo/node/consensus"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
@ -19,250 +14,92 @@ import (
func (e *MasterClockConsensusEngine) prove(
previousFrame *protobufs.ClockFrame,
) (*protobufs.ClockFrame, error) {
if e.state == consensus.EngineStateProving {
e.logger.Debug("proving new frame")
e.logger.Debug("proving new frame")
frame, err := protobufs.ProveMasterClockFrame(
previousFrame,
e.difficulty,
)
if err != nil {
return nil, errors.Wrap(err, "prove")
}
e.state = consensus.EngineStatePublishing
e.logger.Debug("returning new proven frame")
return frame, nil
frame, err := e.frameProver.ProveMasterClockFrame(
previousFrame,
time.Now().UnixMilli(),
e.difficulty,
)
if err != nil {
return nil, errors.Wrap(err, "prove")
}
return nil, nil
}
func (e *MasterClockConsensusEngine) setFrame(frame *protobufs.ClockFrame) {
previousSelectorBytes := [516]byte{}
copy(previousSelectorBytes[:], frame.Output[:516])
e.logger.Debug("set frame", zap.Uint64("frame_number", frame.FrameNumber))
e.frame = frame
go func() {
e.frameChan <- e.frame
}()
}
func (
e *MasterClockConsensusEngine,
) CreateGenesisFrame() *protobufs.ClockFrame {
e.logger.Debug("creating genesis frame")
b := sha3.Sum256(e.input)
v := vdf.New(e.difficulty, b)
v.Execute()
o := v.GetOutput()
inputMessage := o[:]
e.logger.Debug("proving genesis frame")
input := []byte{}
input = append(input, e.filter...)
input = binary.BigEndian.AppendUint64(input, 0)
input = binary.BigEndian.AppendUint32(input, e.difficulty)
if bytes.Equal(e.input, []byte{0x00}) {
value := [516]byte{}
input = append(input, value[:]...)
} else {
input = append(input, e.input...)
}
b = sha3.Sum256(input)
v = vdf.New(e.difficulty, b)
v.Execute()
o = v.GetOutput()
frame := &protobufs.ClockFrame{
Filter: e.filter,
FrameNumber: 0,
Timestamp: 0,
Difficulty: e.difficulty,
Input: inputMessage,
Output: o[:],
ParentSelector: []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
AggregateProofs: []*protobufs.InclusionAggregateProof{},
PublicKeySignature: nil,
}
e.setFrame(frame)
return frame
e.state = consensus.EngineStatePublishing
e.logger.Debug("returning new proven frame")
return frame, nil
}
func (e *MasterClockConsensusEngine) collect(
currentFramePublished *protobufs.ClockFrame,
) (*protobufs.ClockFrame, error) {
if e.state == consensus.EngineStateCollecting {
e.logger.Debug("collecting vdf proofs")
e.logger.Debug("collecting vdf proofs")
latest := e.frame
if e.syncingStatus == SyncStatusNotSyncing {
peer, err := e.pubSub.GetRandomPeer(e.filter)
if err != nil {
if errors.Is(err, p2p.ErrNoPeersAvailable) {
e.logger.Debug("no peers available, skipping sync")
} else {
e.logger.Error("error while fetching random peer", zap.Error(err))
}
} else {
e.syncingStatus = SyncStatusAwaitingResponse
e.logger.Debug("setting syncing target", zap.Binary("peer_id", peer))
e.syncingTarget = peer
channel := e.createPeerReceiveChannel(peer)
e.logger.Debug(
"listening on peer receive channel",
zap.Binary("channel", channel),
)
e.pubSub.Subscribe(channel, e.handleSync, true)
e.pubSub.Subscribe(
peer,
func(message *pb.Message) error { return nil },
true,
)
go func() {
time.Sleep(2 * time.Second)
if err := e.publishMessage(peer, &protobufs.ClockFramesRequest{
Filter: e.filter,
FromFrameNumber: latest.FrameNumber + 1,
}); err != nil {
e.logger.Error(
"could not publish clock frame request",
zap.Error(err),
)
}
}()
}
}
waitDecay := time.Duration(2000)
for e.syncingStatus != SyncStatusNotSyncing {
e.logger.Debug(
"waiting for sync to complete...",
zap.Duration("wait_decay", waitDecay),
)
time.Sleep(waitDecay * time.Millisecond)
waitDecay = waitDecay * 2
if waitDecay >= (100 * (2 << 6)) {
if e.syncingStatus == SyncStatusAwaitingResponse {
e.logger.Debug("maximum wait for sync response, skipping sync")
e.syncingStatus = SyncStatusNotSyncing
break
} else {
waitDecay = 100 * (2 << 6)
}
}
}
e.logger.Debug("selecting leader")
latestFrame, err := e.confirmLatestFrame()
if err != nil {
e.logger.Error("could not confirm latest frame", zap.Error(err))
return nil, errors.Wrap(err, "collect")
}
e.logger.Debug(
"returning leader frame",
zap.Uint64("frame_number", latestFrame.FrameNumber),
)
e.state = consensus.EngineStateProving
return latestFrame, nil
}
return nil, nil
}
func (
e *MasterClockConsensusEngine,
) confirmLatestFrame() (*protobufs.ClockFrame, error) {
e.seenFramesMx.Lock()
defer e.seenFramesMx.Unlock()
sort.Slice(e.seenFrames, func(i, j int) bool {
return e.seenFrames[i].FrameNumber < e.seenFrames[j].FrameNumber
})
if len(e.seenFrames) == 0 {
return e.frame, nil
}
prev := e.frame
committedSet := []*protobufs.ClockFrame{}
for len(e.seenFrames) > 0 {
curr := e.seenFrames[0]
e.seenFrames = e.seenFrames[1:]
e.logger.Debug(
"checking continuity for frame",
zap.Uint64("frame_number", curr.FrameNumber),
)
if prev.FrameNumber+1 < curr.FrameNumber ||
prev.FrameNumber > curr.FrameNumber {
e.logger.Debug(
"continuity break found",
zap.Uint64("prev_frame_number", prev.FrameNumber),
zap.Uint64("curr_frame_number", curr.FrameNumber),
)
break
}
if bytes.Equal(prev.Output, curr.Input[:516]) {
prev = curr
committedSet = append(committedSet, prev)
} else {
e.logger.Debug("frame mismatch on input/output")
}
}
txn, err := e.clockStore.NewTransaction()
latest, err := e.masterTimeReel.Head()
if err != nil {
e.logger.Error("error while creating transaction", zap.Error(err))
return nil, errors.Wrap(err, "confirm latest frame")
panic(err)
}
for _, frame := range committedSet {
frame := frame
if err = e.clockStore.PutMasterClockFrame(frame, txn); err != nil {
e.logger.Error("error while committing frame", zap.Error(err))
return nil, errors.Wrap(err, "confirm latest frame")
if e.syncingStatus == SyncStatusNotSyncing {
peer, err := e.pubSub.GetRandomPeer(e.filter)
if err != nil {
if errors.Is(err, p2p.ErrNoPeersAvailable) {
e.logger.Debug("no peers available, skipping sync")
} else {
e.logger.Error("error while fetching random peer", zap.Error(err))
}
} else {
e.syncingStatus = SyncStatusAwaitingResponse
e.logger.Debug("setting syncing target", zap.Binary("peer_id", peer))
e.syncingTarget = peer
channel := e.createPeerReceiveChannel(peer)
e.logger.Debug(
"listening on peer receive channel",
zap.Binary("channel", channel),
)
e.pubSub.Subscribe(channel, e.handleSync, true)
e.pubSub.Subscribe(
peer,
func(message *pb.Message) error { return nil },
true,
)
go func() {
time.Sleep(2 * time.Second)
if err := e.publishMessage(peer, &protobufs.ClockFramesRequest{
Filter: e.filter,
FromFrameNumber: latest.FrameNumber + 1,
}); err != nil {
e.logger.Error(
"could not publish clock frame request",
zap.Error(err),
)
}
}()
}
}
if err = txn.Commit(); err != nil {
e.logger.Error("error while committing transaction", zap.Error(err))
return nil, errors.Wrap(err, "confirm latest frame")
waitDecay := time.Duration(2000)
for e.syncingStatus != SyncStatusNotSyncing {
e.logger.Debug(
"waiting for sync to complete...",
zap.Duration("wait_decay", waitDecay),
)
time.Sleep(waitDecay * time.Millisecond)
waitDecay = waitDecay * 2
if waitDecay >= (100 * (2 << 6)) {
if e.syncingStatus == SyncStatusAwaitingResponse {
e.logger.Debug("maximum wait for sync response, skipping sync")
e.syncingStatus = SyncStatusNotSyncing
break
} else {
waitDecay = 100 * (2 << 6)
}
}
}
e.logger.Debug("stored frames", zap.Int("frame_count", len(committedSet)))
e.historicFramesMx.Lock()
e.historicFrames = append(e.historicFrames, committedSet...)
if len(e.historicFrames) > 256 {
e.historicFrames = e.historicFrames[len(e.historicFrames)-256:]
}
e.historicFramesMx.Unlock()
e.setFrame(prev)
return prev, nil
return latest, nil
}

View File

@ -15,11 +15,16 @@ func (e *MasterClockConsensusEngine) RegisterExecutor(
errChan := make(chan error)
go func() {
masterFrame, err := e.masterTimeReel.Head()
if err != nil {
panic(err)
}
logger.Info(
"starting execution engine at frame",
zap.Uint64("current_frame", e.frame.FrameNumber),
zap.Uint64("current_frame", masterFrame.FrameNumber),
)
err := <-exec.Start()
err = <-exec.Start()
if err != nil {
logger.Error("could not start execution engine", zap.Error(err))
errChan <- err
@ -27,13 +32,18 @@ func (e *MasterClockConsensusEngine) RegisterExecutor(
}
for {
masterFrame, err = e.masterTimeReel.Head()
if err != nil {
panic(err)
}
logger.Info(
"awaiting frame",
zap.Uint64("current_frame", e.frame.FrameNumber),
zap.Uint64("current_frame", masterFrame.FrameNumber),
zap.Uint64("target_frame", frame),
)
newFrame := e.frame.FrameNumber
newFrame := masterFrame.FrameNumber
if newFrame >= frame {
logger.Info(
"injecting execution engine at frame",
@ -74,13 +84,18 @@ func (e *MasterClockConsensusEngine) UnregisterExecutor(
go func() {
for {
masterFrame, err := e.masterTimeReel.Head()
if err != nil {
panic(err)
}
logger.Info(
"awaiting frame",
zap.Uint64("current_frame", e.frame.FrameNumber),
zap.Uint64("current_frame", masterFrame.FrameNumber),
zap.Uint64("target_frame", frame),
)
newFrame := e.frame.FrameNumber
newFrame := masterFrame.FrameNumber
if newFrame >= frame {
logger.Info(
"removing execution engine at frame",

View File

@ -9,6 +9,8 @@ import (
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/consensus"
qtime "source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/execution"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
@ -25,12 +27,12 @@ const (
)
type MasterClockConsensusEngine struct {
frame *protobufs.ClockFrame
difficulty uint32
logger *zap.Logger
state consensus.EngineState
pubSub p2p.PubSub
keyManager keys.KeyManager
frameProver crypto.FrameProver
lastFrameReceivedAt time.Time
frameChan chan *protobufs.ClockFrame
@ -45,6 +47,7 @@ type MasterClockConsensusEngine struct {
seenFrames []*protobufs.ClockFrame
historicFrames []*protobufs.ClockFrame
clockStore store.ClockStore
masterTimeReel *qtime.MasterTimeReel
}
var _ consensus.ConsensusEngine = (*MasterClockConsensusEngine)(nil)
@ -55,6 +58,8 @@ func NewMasterClockConsensusEngine(
clockStore store.ClockStore,
keyManager keys.KeyManager,
pubSub p2p.PubSub,
frameProver crypto.FrameProver,
masterTimeReel *qtime.MasterTimeReel,
) *MasterClockConsensusEngine {
if logger == nil {
panic(errors.New("logger is nil"))
@ -72,13 +77,20 @@ func NewMasterClockConsensusEngine(
panic(errors.New("pubsub is nil"))
}
if frameProver == nil {
panic(errors.New("frame prover is nil"))
}
if masterTimeReel == nil {
panic(errors.New("master time reel is nil"))
}
seed, err := hex.DecodeString(engineConfig.GenesisSeed)
if err != nil {
panic(errors.New("genesis seed is nil"))
}
e := &MasterClockConsensusEngine{
frame: nil,
difficulty: 10000,
logger: logger,
state: consensus.EngineStateStopped,
@ -90,15 +102,8 @@ func NewMasterClockConsensusEngine(
lastFrameReceivedAt: time.Time{},
syncingStatus: SyncStatusNotSyncing,
clockStore: clockStore,
}
latestFrame, err := e.clockStore.GetLatestMasterClockFrame(e.filter)
if err != nil && !errors.Is(err, store.ErrNotFound) {
panic(err)
}
if latestFrame != nil {
e.frame = latestFrame
frameProver: frameProver,
masterTimeReel: masterTimeReel,
}
if e.filter, err = hex.DecodeString(engineConfig.Filter); err != nil {
@ -118,28 +123,17 @@ func (e *MasterClockConsensusEngine) Start() <-chan error {
e.state = consensus.EngineStateLoading
e.logger.Info("syncing last seen state")
latestFrame, err := e.clockStore.GetLatestMasterClockFrame(e.filter)
if err != nil && errors.Is(err, store.ErrNotFound) {
latestFrame = e.CreateGenesisFrame()
txn, err := e.clockStore.NewTransaction()
if err != nil {
panic(err)
}
if err = e.clockStore.PutMasterClockFrame(latestFrame, txn); err != nil {
panic(err)
}
if err = txn.Commit(); err != nil {
panic(err)
}
} else if err != nil {
err := e.masterTimeReel.Start()
if err != nil {
panic(err)
} else {
e.setFrame(latestFrame)
}
e.buildHistoricFrameCache(latestFrame)
frame, err := e.masterTimeReel.Head()
if err != nil {
panic(err)
}
e.buildHistoricFrameCache(frame)
e.logger.Info("subscribing to pubsub messages")
e.pubSub.Subscribe(e.filter, e.handleMessage, true)
@ -159,25 +153,39 @@ func (e *MasterClockConsensusEngine) Start() <-chan error {
}()
go func() {
newFrameCh := e.masterTimeReel.NewFrameCh()
for e.state < consensus.EngineStateStopping {
var err error
switch e.state {
case consensus.EngineStateCollecting:
currentFrame := latestFrame
if latestFrame, err = e.collect(latestFrame); err != nil {
select {
case frame := <-newFrameCh:
currentFrame := frame
latestFrame := frame
if latestFrame, err = e.collect(currentFrame); err != nil {
e.logger.Error("could not collect", zap.Error(err))
latestFrame = currentFrame
continue
}
case consensus.EngineStateProving:
currentFrame := latestFrame
if latestFrame, err = e.prove(latestFrame); err != nil {
e.logger.Error("could not prove", zap.Error(err))
latestFrame = currentFrame
}
case consensus.EngineStatePublishing:
if err = e.publishProof(latestFrame); err != nil {
e.logger.Error("could not publish", zap.Error(err))
}
case <-time.After(20 * time.Second):
frame, err := e.masterTimeReel.Head()
if err != nil {
panic(err)
}
if frame, err = e.prove(frame); err != nil {
e.logger.Error("could not prove", zap.Error(err))
continue
}
if err = e.publishProof(frame); err != nil {
e.logger.Error("could not publish", zap.Error(err))
}
}
}
}()
@ -199,7 +207,13 @@ func (e *MasterClockConsensusEngine) Stop(force bool) <-chan error {
for name := range e.executionEngines {
name := name
go func(name string) {
err := <-e.UnregisterExecutor(name, e.frame.FrameNumber, force)
frame, err := e.masterTimeReel.Head()
if err != nil {
errChan <- err
return
}
err = <-e.UnregisterExecutor(name, frame.FrameNumber, force)
if err != nil {
errChan <- err
}
@ -210,6 +224,7 @@ func (e *MasterClockConsensusEngine) Stop(force bool) <-chan error {
e.logger.Info("waiting for execution engines to stop")
wg.Wait()
e.logger.Info("execution engines stopped")
e.masterTimeReel.Stop()
e.state = consensus.EngineStateStopped
go func() {
@ -223,7 +238,12 @@ func (e *MasterClockConsensusEngine) GetDifficulty() uint32 {
}
func (e *MasterClockConsensusEngine) GetFrame() *protobufs.ClockFrame {
return e.frame
frame, err := e.masterTimeReel.Head()
if err != nil {
panic(err)
}
return frame
}
func (e *MasterClockConsensusEngine) GetState() consensus.EngineState {

View File

@ -96,7 +96,7 @@ func (e *MasterClockConsensusEngine) handleClockFramesResponse(
zap.Uint64("frame_number", frame.FrameNumber),
)
if err := frame.VerifyMasterClockFrame(); err != nil {
if err := e.frameProver.VerifyMasterClockFrame(frame); err != nil {
e.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame response")
}
@ -108,12 +108,7 @@ func (e *MasterClockConsensusEngine) handleClockFramesResponse(
zap.Uint64("frame_number", frame.FrameNumber),
)
if e.frame.FrameNumber < frame.FrameNumber {
if err := e.enqueueSeenFrame(frame); err != nil {
e.logger.Error("could not enqueue seen clock frame", zap.Error(err))
return errors.Wrap(err, "handle clock frame response")
}
}
e.masterTimeReel.Insert(frame)
}
return nil
@ -145,7 +140,12 @@ func (e *MasterClockConsensusEngine) handleClockFramesRequest(
from := request.FromFrameNumber
if e.frame.FrameNumber < from || len(e.historicFrames) == 0 {
masterFrame, err := e.masterTimeReel.Head()
if err != nil {
panic(err)
}
if masterFrame.FrameNumber < from || len(e.historicFrames) == 0 {
e.logger.Debug(
"peer asked for undiscovered frame",
zap.Binary("peer_id", peerID),
@ -169,8 +169,8 @@ func (e *MasterClockConsensusEngine) handleClockFramesRequest(
to = request.FromFrameNumber + 127
}
if int(to) > int(e.frame.FrameNumber) {
to = e.frame.FrameNumber
if int(to) > int(masterFrame.FrameNumber) {
to = masterFrame.FrameNumber
}
e.logger.Debug(

View File

@ -0,0 +1,607 @@
package time
import (
"bytes"
"math/big"
"sync"
"github.com/pkg/errors"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
"source.quilibrium.com/quilibrium/monorepo/node/tries"
)
var allBitmaskFilter = []byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}
var unknownDistance = new(big.Int).SetBytes([]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
})
type pendingFrame struct {
parentSelector *big.Int
distance *big.Int
}
type DataTimeReel struct {
rwMutex sync.RWMutex
filter []byte
engineConfig *config.EngineConfig
logger *zap.Logger
clockStore store.ClockStore
frameProver crypto.FrameProver
parentTimeReel TimeReel
origin []byte
initialInclusionProof *crypto.InclusionAggregateProof
initialProverKeys [][]byte
head *protobufs.ClockFrame
totalDistance *big.Int
headDistance *big.Int
proverTrie *tries.RollingFrecencyCritbitTrie
pending map[uint64][]*pendingFrame
incompleteForks map[uint64][]*pendingFrame
frames chan *protobufs.ClockFrame
newFrameCh chan *protobufs.ClockFrame
badFrameCh chan *protobufs.ClockFrame
done chan bool
}
func NewDataTimeReel(
filter []byte,
logger *zap.Logger,
clockStore store.ClockStore,
engineConfig *config.EngineConfig,
frameProver crypto.FrameProver,
origin []byte,
initialInclusionProof *crypto.InclusionAggregateProof,
initialProverKeys [][]byte,
) *DataTimeReel {
if filter == nil {
panic("filter is nil")
}
if logger == nil {
panic("logger is nil")
}
if clockStore == nil {
panic("clock store is nil")
}
if engineConfig == nil {
panic("engine config is nil")
}
if frameProver == nil {
panic("frame prover is nil")
}
return &DataTimeReel{
logger: logger,
filter: filter,
engineConfig: engineConfig,
clockStore: clockStore,
frameProver: frameProver,
origin: origin,
initialInclusionProof: initialInclusionProof,
initialProverKeys: initialProverKeys,
pending: make(map[uint64][]*pendingFrame),
incompleteForks: make(map[uint64][]*pendingFrame),
frames: make(chan *protobufs.ClockFrame),
newFrameCh: make(chan *protobufs.ClockFrame),
badFrameCh: make(chan *protobufs.ClockFrame),
done: make(chan bool),
}
}
func (d *DataTimeReel) Start() error {
trie := &tries.RollingFrecencyCritbitTrie{}
frame, err := d.clockStore.GetLatestDataClockFrame(d.filter, trie)
if err != nil && !errors.Is(err, store.ErrNotFound) {
panic(err)
}
if frame == nil {
d.head, d.proverTrie = d.createGenesisFrame()
d.totalDistance = big.NewInt(0)
} else {
d.head = frame
d.proverTrie = trie
d.totalDistance = d.getTotalDistance(frame)
}
go d.runLoop()
return nil
}
func (d *DataTimeReel) Head() (*protobufs.ClockFrame, error) {
return d.head, nil
}
// Insert enqueues a structurally valid frame into the time reel. If the frame
// is the next one in sequence, it advances the reel head forward and emits a
// new frame on the new frame channel.
func (d *DataTimeReel) Insert(frame *protobufs.ClockFrame) error {
go func() {
d.frames <- frame
}()
return nil
}
func (d *DataTimeReel) GetFrameProverTrie() *tries.RollingFrecencyCritbitTrie {
return d.proverTrie
}
func (d *DataTimeReel) NewFrameCh() <-chan *protobufs.ClockFrame {
return d.newFrameCh
}
func (d *DataTimeReel) BadFrameCh() <-chan *protobufs.ClockFrame {
return d.badFrameCh
}
func (d *DataTimeReel) Stop() {
d.done <- true
}
func (d *DataTimeReel) createGenesisFrame() (
*protobufs.ClockFrame,
*tries.RollingFrecencyCritbitTrie,
) {
if d.origin == nil {
panic("origin is nil")
}
if d.initialInclusionProof == nil {
panic("initial inclusion proof is nil")
}
if d.initialProverKeys == nil {
panic("initial prover keys is nil")
}
frame, trie, err := d.frameProver.CreateDataGenesisFrame(
d.filter,
d.origin,
d.engineConfig.Difficulty,
d.initialInclusionProof,
d.initialProverKeys,
true,
)
if err != nil {
panic(err)
}
txn, err := d.clockStore.NewTransaction()
if err != nil {
panic(err)
}
if err := d.clockStore.PutDataClockFrame(
frame,
trie,
txn,
false,
); err != nil {
panic(err)
}
if err := txn.Commit(); err != nil {
panic(err)
}
return frame, trie
}
// Main data consensus loop
func (d *DataTimeReel) runLoop() {
for {
select {
case frame := <-d.frames:
// Most common scenario: in order new frame is higher number
if d.head.FrameNumber < frame.FrameNumber {
parent := new(big.Int).SetBytes(frame.ParentSelector)
selector, err := frame.GetSelector()
if err != nil {
panic(err)
}
distance, err := d.GetDistance(frame)
if err != nil {
// If the frame arrived ahead of a master, e.g. the master data is not
// synced, we'll go ahead and mark it as pending and process it when
// we can, but if we had a general fault, panic:
if !errors.Is(err, store.ErrNotFound) {
panic(err)
}
d.addPending(selector, parent, unknownDistance, frame)
continue
}
// If the frame has a gap from the head, mark it as pending:
if frame.FrameNumber-d.head.FrameNumber != 1 {
d.addPending(selector, parent, distance, frame)
continue
}
// Otherwise set it as the next and process all pending
d.setHead(frame, distance)
d.processPending(frame)
} else if d.head.FrameNumber == frame.FrameNumber {
// frames are equivalent, no need to act
if bytes.Equal(d.head.Output, frame.Output) {
continue
}
distance, err := d.GetDistance(frame)
if err != nil {
panic(err)
}
// Optimization: if competing frames share a parent we can short-circuit
// fork choice
if bytes.Equal(d.head.ParentSelector, frame.ParentSelector) &&
distance.Cmp(d.headDistance) < 0 {
d.totalDistance.Sub(d.totalDistance, d.headDistance)
d.setHead(frame, distance)
}
// Choose fork
d.forkChoice(frame, distance)
} else {
// tag: dusk we should have some kind of check here to avoid brutal
// thrashing
existing, _, err := d.clockStore.GetDataClockFrame(
d.filter,
frame.FrameNumber,
)
if err != nil {
// if this returns an error it's either not found (which shouldn't
// happen without corruption) or pebble is borked, either way, panic
panic(err)
}
// It's a fork, but it's behind. We need to stash it until it catches
// up (or dies off)
if !bytes.Equal(existing.Output, frame.Output) {
distance, err := d.GetDistance(frame)
if err != nil {
panic(err)
}
parent, selector, err := frame.GetParentAndSelector()
if err != nil {
panic(err)
}
d.addPending(selector, parent, distance, frame)
d.processPending(d.head)
}
}
case <-d.done:
return
}
}
}
func (d *DataTimeReel) addPending(
selector *big.Int,
parent *big.Int,
distance *big.Int,
frame *protobufs.ClockFrame,
) {
if _, ok := d.pending[frame.FrameNumber]; !ok {
d.pending[frame.FrameNumber] = []*pendingFrame{}
}
txn, err := d.clockStore.NewTransaction()
if err != nil {
panic(err)
}
if distance.Cmp(unknownDistance) == 0 {
distance = new(big.Int).Set(unknownDistance)
distance.Sub(distance, big.NewInt(int64(len(d.pending[frame.FrameNumber]))))
}
err = d.clockStore.PutCandidateDataClockFrame(
parent.FillBytes(make([]byte, 32)),
distance.FillBytes(make([]byte, 32)),
selector.FillBytes(make([]byte, 32)),
frame,
txn,
)
if err != nil {
txn.Abort()
panic(err)
}
if err = txn.Commit(); err != nil {
panic(err)
}
d.pending[frame.FrameNumber] = append(
d.pending[frame.FrameNumber],
&pendingFrame{
parentSelector: parent,
distance: distance,
},
)
}
func (d *DataTimeReel) processPending(frame *protobufs.ClockFrame) {
neighbors := false
// Flush the current pending frames
neighborPending, ok := d.pending[frame.FrameNumber]
for ok && neighborPending != nil {
next := neighborPending[0]
d.pending[frame.FrameNumber] =
d.pending[frame.FrameNumber][1:]
if len(d.pending[frame.FrameNumber]) == 0 {
delete(d.pending, frame.FrameNumber)
break
}
nextFrame, err := d.clockStore.GetCandidateDataClockFrame(
d.filter,
frame.FrameNumber,
next.parentSelector.FillBytes(make([]byte, 32)),
next.distance.FillBytes(make([]byte, 32)),
)
if err != nil && !errors.Is(err, store.ErrNotFound) {
panic(err)
}
if nextFrame != nil {
neighbors = true
go func() {
d.frames <- nextFrame
}()
}
neighborPending, ok = d.pending[frame.FrameNumber]
}
if !neighbors {
// Pull the next
nextPending, ok := d.pending[frame.FrameNumber+1]
if ok {
next := nextPending[0]
d.pending[frame.FrameNumber+1] =
d.pending[frame.FrameNumber+1][1:]
if len(d.pending[frame.FrameNumber+1]) == 0 {
delete(d.pending, frame.FrameNumber+1)
}
nextFrame, err := d.clockStore.GetCandidateDataClockFrame(
d.filter,
frame.FrameNumber+1,
next.parentSelector.FillBytes(make([]byte, 32)),
next.distance.FillBytes(make([]byte, 32)),
)
if err != nil && !errors.Is(err, store.ErrNotFound) {
panic(err)
}
if nextFrame != nil {
go func() {
d.frames <- nextFrame
}()
}
}
}
}
func (d *DataTimeReel) setHead(frame *protobufs.ClockFrame, distance *big.Int) {
txn, err := d.clockStore.NewTransaction()
if err != nil {
panic(err)
}
if err := d.clockStore.PutDataClockFrame(
frame,
d.proverTrie,
txn,
false,
); err != nil {
panic(err)
}
if err = txn.Commit(); err != nil {
panic(err)
}
d.head = frame
d.totalDistance.Add(d.totalDistance, distance)
d.headDistance = distance
go func() {
d.newFrameCh <- frame
}()
}
// tag: dusk store the distance with the frame
func (d *DataTimeReel) getTotalDistance(frame *protobufs.ClockFrame) *big.Int {
total, err := d.GetDistance(frame)
if err != nil {
panic(err)
}
for index := frame; err == nil &&
index.FrameNumber > 0; index, err = d.clockStore.GetParentDataClockFrame(
d.filter,
index.FrameNumber-1,
index.ParentSelector,
) {
distance, err := d.GetDistance(index)
if err != nil {
panic(err)
}
total.Add(total, distance)
}
return total
}
func (d *DataTimeReel) GetDistance(frame *protobufs.ClockFrame) (
*big.Int,
error,
) {
// tag: equinox master filter changes
master, err := d.clockStore.GetMasterClockFrame(
allBitmaskFilter,
frame.FrameNumber)
if err != nil {
return nil, errors.Wrap(err, "get distance")
}
masterSelector, err := master.GetSelector()
if err != nil {
return nil, errors.Wrap(err, "get distance")
}
discriminatorNode :=
d.proverTrie.FindNearest(masterSelector.FillBytes(make([]byte, 32)))
discriminator := discriminatorNode.External.Key
addr, err := frame.GetAddress()
if err != nil {
return nil, errors.Wrap(err, "get distance")
}
distance := new(big.Int).Sub(
new(big.Int).SetBytes(discriminator),
new(big.Int).SetBytes(addr),
)
distance.Abs(distance)
return distance, nil
}
func (d *DataTimeReel) forkChoice(
frame *protobufs.ClockFrame,
distance *big.Int,
) {
parentSelector, selector, err := frame.GetParentAndSelector()
if err != nil {
panic(err)
}
leftIndex := d.head
rightIndex := frame
leftTotal := new(big.Int).Set(d.headDistance)
rightTotal := new(big.Int).Set(distance)
left := d.head.ParentSelector
right := frame.ParentSelector
rightReplaySelectors := [][]byte{selector.FillBytes(make([]byte, 32))}
// Walk backwards through the parents, until we find a matching parent
// selector:
for !bytes.Equal(left, right) {
rightReplaySelectors = append(
append(
[][]byte{},
right,
),
rightReplaySelectors...,
)
leftIndex, err = d.clockStore.GetParentDataClockFrame(
d.filter,
leftIndex.FrameNumber-1,
leftIndex.ParentSelector,
)
if err != nil {
panic(err)
}
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)
}
}
left = leftIndex.ParentSelector
right = rightIndex.ParentSelector
leftIndexDistance, err := d.GetDistance(leftIndex)
if err != nil {
panic(err)
}
rightIndexDistance, err := d.GetDistance(rightIndex)
if err != nil {
panic(err)
}
leftTotal.Add(leftTotal, leftIndexDistance)
rightTotal.Add(rightTotal, rightIndexDistance)
}
// Choose new fork based on lightest distance sub-tree
if rightTotal.Cmp(leftTotal) < 0 {
for {
if len(rightReplaySelectors) == 0 {
break
}
next := rightReplaySelectors[0]
rightReplaySelectors[frame.FrameNumber] =
rightReplaySelectors[frame.FrameNumber][1:]
rightIndex, err = d.clockStore.GetParentDataClockFrame(
d.filter,
rightIndex.FrameNumber+1,
next,
)
if err != nil {
panic(err)
}
txn, err := d.clockStore.NewTransaction()
if err != nil {
panic(err)
}
if err := d.clockStore.PutDataClockFrame(
rightIndex,
d.proverTrie,
txn,
false,
); err != nil {
panic(err)
}
if err = txn.Commit(); err != nil {
panic(err)
}
}
d.head = frame
d.totalDistance.Sub(d.totalDistance, leftTotal)
d.totalDistance.Add(d.totalDistance, rightTotal)
d.headDistance = distance
go func() {
d.newFrameCh <- frame
}()
}
}
var _ TimeReel = (*DataTimeReel)(nil)

View File

@ -0,0 +1,407 @@
package time_test
import (
"encoding/hex"
"fmt"
"strings"
"sync"
"testing"
"github.com/cloudflare/circl/sign/ed448"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
"source.quilibrium.com/quilibrium/monorepo/node/tries"
)
func generateTestProvers() (
keys.KeyManager,
[]peer.ID,
[][]byte,
[][]byte,
map[string]string,
*tries.RollingFrecencyCritbitTrie,
) {
keyManager := keys.NewInMemoryKeyManager()
peers := []peer.ID{}
pubKeys := [][]byte{}
privKeys := [][]byte{}
addrMap := map[string]string{}
for i := 0; i < 1000; i++ {
keyManager.CreateSigningKey(
fmt.Sprintf("test-key-%d", i),
keys.KeyTypeEd448,
)
k, err := keyManager.GetRawKey(fmt.Sprintf("test-key-%d", i))
if err != nil {
panic(err)
}
privKey, err := crypto.UnmarshalEd448PrivateKey([]byte(k.PrivateKey))
if err != nil {
panic(err)
}
privKeys = append(privKeys, []byte(k.PrivateKey))
pub := privKey.GetPublic()
id, err := peer.IDFromPublicKey(pub)
if err != nil {
panic(err)
}
peers = append(peers, id)
keyManager.CreateSigningKey(
fmt.Sprintf("proving-key-%d", i),
keys.KeyTypeEd448,
)
pk, err := keyManager.GetRawKey(fmt.Sprintf("proving-key-%d", i))
if err != nil {
panic(err)
}
pprivKey, err := crypto.UnmarshalEd448PrivateKey([]byte(pk.PrivateKey))
if err != nil {
panic(err)
}
ppub := pprivKey.GetPublic()
ppubKey, err := ppub.Raw()
if err != nil {
panic(err)
}
pubKeys = append(pubKeys, ppubKey)
}
proverTrie := &tries.RollingFrecencyCritbitTrie{}
for i, s := range pubKeys {
addr, err := poseidon.HashBytes(s)
if err != nil {
panic(err)
}
addrBytes := addr.Bytes()
addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...)
proverTrie.Add(addrBytes, 0)
addrMap[string(addrBytes)] = fmt.Sprintf("proving-key-%d", i)
}
return keyManager,
peers,
pubKeys,
privKeys,
addrMap,
proverTrie
}
func TestDataTimeReel(t *testing.T) {
logger, _ := zap.NewProduction()
db := store.NewInMemKVDB()
clockStore := store.NewPebbleClockStore(db, logger)
prover := qcrypto.NewWesolowskiFrameProver(logger)
filter := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
keyManager,
_,
pubKeys,
_,
addrMap,
proverTrie := generateTestProvers()
// We're going to set this up by churning 100 master frames so we don't
// have to zig zag on master and data frames to confirm data time reel
// behaviors
m := time.NewMasterTimeReel(
logger,
clockStore,
&config.EngineConfig{
Filter: filter,
GenesisSeed: strings.Repeat("00", 516),
Difficulty: 10,
},
prover,
)
err := m.Start()
assert.NoError(t, err)
frame, err := m.Head()
assert.NoError(t, err)
frames := []*protobufs.ClockFrame{}
wg := sync.WaitGroup{}
wg.Add(1)
frameCh := m.NewFrameCh()
go func() {
for i := 0; i < 100; i++ {
frames = append(frames, <-frameCh)
}
wg.Done()
}()
// in order
for i := int64(0); i < 100; i++ {
frame, err = prover.ProveMasterClockFrame(frame, i+1, 10)
assert.NoError(t, err)
err := m.Insert(frame)
assert.NoError(t, err)
}
wg.Wait()
for i := 0; i < 100; i++ {
assert.NotNil(t, frames[i])
assert.Equal(t, frames[i].FrameNumber, uint64(i+1))
}
filterBytes, _ := hex.DecodeString(filter)
// Ok, now onto the data time reel. We're going to test the following
// scenarios:
// 1. Z-dist optimal, in order
// 2. Z-dist optimal, out of order
// 3. 90% optimal, out of order
// 4. Malicious majority, out of order
d := time.NewDataTimeReel(
filterBytes,
logger,
clockStore,
&config.EngineConfig{
Filter: filter,
GenesisSeed: strings.Repeat("00", 516),
Difficulty: 10,
},
prover,
frames[0].Output,
&qcrypto.InclusionAggregateProof{
InclusionCommitments: []*qcrypto.InclusionCommitment{},
AggregateCommitment: []byte{},
Proof: []byte{},
},
pubKeys,
)
err = d.Start()
assert.NoError(t, err)
frame, err = d.Head()
assert.NoError(t, err)
dataFrames := []*protobufs.ClockFrame{}
datawg := sync.WaitGroup{}
datawg.Add(1)
dataFrameCh := d.NewFrameCh()
go func() {
for i := 0; i < 40; i++ {
dataFrames = append(dataFrames, <-dataFrameCh)
}
datawg.Done()
}()
// 1. z-dist optimal proof submission is strictly master-frame evoked leader
for i := int64(0); i < 10; i++ {
masterSelector, err := frames[i+1].GetSelector()
assert.NoError(t, err)
proverSelection := proverTrie.FindNearest(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelection.External.Key)],
)
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
d.Insert(frame)
}
// 2. z-dist optimal, out of order proof submission is strictly master-frame
// evoked leader, but arrived completely backwards
insertFrames := []*protobufs.ClockFrame{}
for i := int64(10); i < 20; i++ {
masterSelector, err := frames[i+1].GetSelector()
assert.NoError(t, err)
proverSelection := proverTrie.FindNearest(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelection.External.Key)],
)
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
insertFrames = append(insertFrames, frame)
}
for i := 9; i >= 0; i-- {
err := d.Insert(insertFrames[i])
assert.NoError(t, err)
}
// 3. 90% optimal, out of order
insertFrames = []*protobufs.ClockFrame{}
for i := int64(20); i < 25; i++ {
masterSelector, err := frames[i+1].GetSelector()
assert.NoError(t, err)
proverSelection := proverTrie.FindNearest(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelection.External.Key)],
)
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
d.Insert(frame)
}
masterSelector, err := frames[26].GetSelector()
assert.NoError(t, err)
proverSelections := proverTrie.FindNearestAndApproximateNeighbors(
masterSelector.FillBytes(make([]byte, 32)),
)
suboptimalSigner2, _ := keyManager.GetSigningKey(
addrMap[string(proverSelections[2].External.Key)],
)
// What we're trying to simulate: consensus heads progressed on a slightly
// less optimal prover.
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
suboptimalSigner2,
26,
10,
)
insertFrames = append(insertFrames, frame)
for i := int64(26); i < 30; i++ {
masterSelector, err := frames[i+1].GetSelector()
assert.NoError(t, err)
proverSelection := proverTrie.FindNearest(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelection.External.Key)],
)
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
insertFrames = append(insertFrames, frame)
}
for i := 4; i >= 0; i-- {
err := d.Insert(insertFrames[i])
assert.NoError(t, err)
}
// 4. Malicious majority, out of order handle a suppressive majority and
// force consensus on the lowest distance sub-tree:
insertFrames = []*protobufs.ClockFrame{}
conflictFrames := []*protobufs.ClockFrame{}
optimalKeySet := [][]byte{}
suppressedFrame := frame
for i := int64(30); i < 40; i++ {
masterSelector, err := frames[i+1].GetSelector()
assert.NoError(t, err)
proverSelections := proverTrie.FindNearestAndApproximateNeighbors(
masterSelector.FillBytes(make([]byte, 32)),
)
optimalSigner, _ := keyManager.GetSigningKey(
addrMap[string(proverSelections[0].External.Key)],
)
suboptimalSigner2, _ := keyManager.GetSigningKey(
addrMap[string(proverSelections[2].External.Key)],
)
optimalKeySet = append(optimalKeySet, []byte(
(optimalSigner.Public()).(ed448.PublicKey),
))
// What we're trying to simulate: the majority is intentionally ignoring
// the most optimal signer
suppressedFrame, err := prover.ProveDataClockFrame(
suppressedFrame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
optimalSigner,
i+1,
10,
)
insertFrames = append(insertFrames, suppressedFrame)
frame, err = prover.ProveDataClockFrame(
frame,
[][]byte{},
[]*protobufs.InclusionAggregateProof{},
suboptimalSigner2,
i+1,
10,
)
conflictFrames = append(conflictFrames, frame)
}
for i := 9; i >= 0; i-- {
err := d.Insert(conflictFrames[i])
assert.NoError(t, err)
}
// Someone is honest, but clumsy:
for i := 9; i >= 0; i-- {
err := d.Insert(insertFrames[i])
assert.NoError(t, err)
}
datawg.Wait()
for i := 0; i < 40; i++ {
assert.NotNil(t, dataFrames[i])
if i >= 40 {
assert.Equal(t, uint64(i), dataFrames[i].FrameNumber)
assert.Equal(
t,
dataFrames[i].GetPublicKeySignatureEd448().PublicKey.KeyValue,
optimalKeySet[i-30],
)
} else {
assert.Equal(t, uint64(i+1), dataFrames[i].FrameNumber)
}
}
}

View File

@ -0,0 +1,221 @@
package time
import (
"encoding/hex"
"errors"
"math/big"
"sync"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
)
type MasterTimeReel struct {
rwMutex sync.RWMutex
filter []byte
engineConfig *config.EngineConfig
logger *zap.Logger
clockStore store.ClockStore
frameProver crypto.FrameProver
head *protobufs.ClockFrame
pending map[uint64][]*big.Int
frames chan *protobufs.ClockFrame
newFrameCh chan *protobufs.ClockFrame
badFrameCh chan *protobufs.ClockFrame
done chan bool
}
func NewMasterTimeReel(
logger *zap.Logger,
clockStore store.ClockStore,
engineConfig *config.EngineConfig,
frameProver crypto.FrameProver,
) *MasterTimeReel {
if logger == nil {
panic("logger is nil")
}
if clockStore == nil {
panic("clock store is nil")
}
if engineConfig == nil {
panic("engine config is nil")
}
if frameProver == nil {
panic("frame prover is nil")
}
filter, err := hex.DecodeString(engineConfig.Filter)
if err != nil {
panic(err)
}
return &MasterTimeReel{
logger: logger,
filter: filter,
engineConfig: engineConfig,
clockStore: clockStore,
frameProver: frameProver,
pending: make(map[uint64][]*big.Int),
frames: make(chan *protobufs.ClockFrame),
newFrameCh: make(chan *protobufs.ClockFrame),
badFrameCh: make(chan *protobufs.ClockFrame),
done: make(chan bool),
}
}
// Start implements TimeReel.
func (m *MasterTimeReel) Start() error {
frame, err := m.clockStore.GetLatestMasterClockFrame(m.filter)
if err != nil && !errors.Is(err, store.ErrNotFound) {
panic(err)
}
if frame == nil {
m.head = m.createGenesisFrame()
} else {
m.head = frame
}
go m.runLoop()
return nil
}
// Head implements TimeReel.
func (m *MasterTimeReel) Head() (*protobufs.ClockFrame, error) {
return m.head, nil
}
// Insert enqueues a structurally valid frame into the time reel. If the frame
// is the next one in sequence, it advances the reel head forward and emits a
// new frame on the new frame channel.
func (m *MasterTimeReel) Insert(frame *protobufs.ClockFrame) error {
go func() {
m.frames <- frame
}()
return nil
}
// NewFrameCh implements TimeReel.
func (m *MasterTimeReel) NewFrameCh() <-chan *protobufs.ClockFrame {
return m.newFrameCh
}
func (m *MasterTimeReel) BadFrameCh() <-chan *protobufs.ClockFrame {
return m.badFrameCh
}
// Stop implements TimeReel.
func (m *MasterTimeReel) Stop() {
m.done <- true
}
func (m *MasterTimeReel) createGenesisFrame() *protobufs.ClockFrame {
seed, err := hex.DecodeString(m.engineConfig.GenesisSeed)
if err != nil {
panic(errors.New("genesis seed is nil"))
}
frame, err := m.frameProver.CreateMasterGenesisFrame(
m.filter,
seed,
m.engineConfig.Difficulty,
)
if err != nil {
panic(err)
}
txn, err := m.clockStore.NewTransaction()
if err != nil {
panic(err)
}
if err = m.clockStore.PutMasterClockFrame(frame, txn); err != nil {
panic(err)
}
if err = txn.Commit(); err != nil {
panic(err)
}
return frame
}
func (m *MasterTimeReel) runLoop() {
for {
select {
case frame := <-m.frames:
if m.head.FrameNumber < frame.FrameNumber {
m.logger.Debug(
"new frame has higher number",
zap.Uint32("new_frame_number", uint32(frame.FrameNumber)),
zap.Uint32("frame_number", uint32(m.head.FrameNumber)),
)
if frame.FrameNumber-m.head.FrameNumber == 1 {
parent := new(big.Int).SetBytes(frame.ParentSelector)
selector, err := m.head.GetSelector()
if err != nil {
panic(err)
}
// master frames cannot fork, this is invalid
if parent.Cmp(selector) != 0 {
m.logger.Debug(
"invalid parent selector for frame",
zap.Binary("frame_parent_selector", frame.ParentSelector),
zap.Binary("actual_parent_selector", selector.FillBytes(
make([]byte, 32),
)),
)
go func() {
m.badFrameCh <- frame
}()
continue
}
txn, err := m.clockStore.NewTransaction()
if err != nil {
panic(err)
}
if err := m.clockStore.PutMasterClockFrame(frame, txn); err != nil {
panic(err)
}
if err = txn.Commit(); err != nil {
panic(err)
}
m.head = frame
go func() {
m.newFrameCh <- frame
}()
} else {
go func() {
m.frames <- frame
}()
}
} else {
m.logger.Debug(
"new frame has same or lower frame number",
zap.Uint32("new_frame_number", uint32(frame.FrameNumber)),
zap.Uint32("frame_number", uint32(m.head.FrameNumber)),
)
continue
}
case <-m.done:
return
}
}
}
var _ TimeReel = (*MasterTimeReel)(nil)

View File

@ -0,0 +1,82 @@
package time_test
import (
"strings"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
)
func TestMasterTimeReel(t *testing.T) {
logger, _ := zap.NewProduction()
db := store.NewInMemKVDB()
clockStore := store.NewPebbleClockStore(db, logger)
prover := crypto.NewWesolowskiFrameProver(logger)
filter := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
m := time.NewMasterTimeReel(
logger,
clockStore,
&config.EngineConfig{
Filter: filter,
GenesisSeed: strings.Repeat("00", 516),
Difficulty: 10,
},
prover,
)
err := m.Start()
assert.NoError(t, err)
frame, err := m.Head()
assert.NoError(t, err)
frames := []*protobufs.ClockFrame{}
wg := sync.WaitGroup{}
wg.Add(1)
frameCh := m.NewFrameCh()
go func() {
for i := 0; i < 200; i++ {
frames = append(frames, <-frameCh)
}
wg.Done()
}()
// in order
for i := int64(0); i < 100; i++ {
frame, err = prover.ProveMasterClockFrame(frame, i+1, 10)
assert.NoError(t, err)
err := m.Insert(frame)
assert.NoError(t, err)
}
insertFrames := []*protobufs.ClockFrame{}
// reverse order
for i := int64(100); i < 200; i++ {
frame, err = prover.ProveMasterClockFrame(frame, i+1, 10)
assert.NoError(t, err)
insertFrames = append(insertFrames, frame)
}
for i := 99; i >= 0; i-- {
err := m.Insert(insertFrames[i])
assert.NoError(t, err)
}
wg.Wait()
for i := 0; i < 200; i++ {
assert.NotNil(t, frames[i])
assert.Equal(t, frames[i].FrameNumber, uint64(i+1))
}
}

View File

@ -0,0 +1,14 @@
package time
import (
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
type TimeReel interface {
Start() error
Stop()
Insert(frame *protobufs.ClockFrame) error
Head() (*protobufs.ClockFrame, error)
NewFrameCh() <-chan *protobufs.ClockFrame
BadFrameCh() <-chan *protobufs.ClockFrame
}

View File

@ -1,4 +1,4 @@
package crypto
package channel
import (
"crypto/aes"

View File

@ -1,4 +1,4 @@
package crypto_test
package channel_test
import (
"crypto/rand"
@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/require"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/channel"
)
func TestRatchetEncrypt(t *testing.T) {
@ -20,7 +20,7 @@ func TestRatchetEncrypt(t *testing.T) {
x448ReceivingIdentityKey := curves.ED448().NewGeneratorPoint().Mul(x448ReceivingIdentityPrivateKey)
x448ReceivingSignedPreKey := curves.ED448().NewGeneratorPoint().Mul(x448ReceivingSignedPrePrivateKey)
senderResult := crypto.SenderX3DH(
senderResult := channel.SenderX3DH(
x448SendingIdentityPrivateKey,
x448SendingEphemeralPrivateKey,
x448ReceivingIdentityKey,
@ -28,7 +28,7 @@ func TestRatchetEncrypt(t *testing.T) {
96,
)
receiverResult := crypto.ReceiverX3DH(
receiverResult := channel.ReceiverX3DH(
x448ReceivingIdentityPrivateKey,
x448ReceivingSignedPrePrivateKey,
x448SendingIdentityKey,
@ -36,7 +36,7 @@ func TestRatchetEncrypt(t *testing.T) {
96,
)
sender, err := crypto.NewDoubleRatchetParticipant(
sender, err := channel.NewDoubleRatchetParticipant(
senderResult[:32],
senderResult[32:64],
senderResult[64:],
@ -48,7 +48,7 @@ func TestRatchetEncrypt(t *testing.T) {
)
require.NoError(t, err)
receiver, err := crypto.NewDoubleRatchetParticipant(
receiver, err := channel.NewDoubleRatchetParticipant(
receiverResult[:32],
receiverResult[32:64],
receiverResult[64:],

View File

@ -1,4 +1,4 @@
package crypto
package channel
import (
"crypto/sha512"

View File

@ -1,4 +1,4 @@
package crypto_test
package channel_test
import (
"crypto/rand"
@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/require"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/channel"
)
func TestX3DHMatches(t *testing.T) {
@ -20,7 +20,7 @@ func TestX3DHMatches(t *testing.T) {
x448ReceivingIdentityKey := curves.ED448().NewGeneratorPoint().Mul(x448ReceivingIdentityPrivateKey)
x448ReceivingSignedPreKey := curves.ED448().NewGeneratorPoint().Mul(x448ReceivingSignedPrePrivateKey)
result := crypto.SenderX3DH(
result := channel.SenderX3DH(
x448SendingIdentityPrivateKey,
x448SendingEphemeralPrivateKey,
x448ReceivingIdentityKey,
@ -28,7 +28,7 @@ func TestX3DHMatches(t *testing.T) {
32,
)
compare := crypto.ReceiverX3DH(
compare := channel.ReceiverX3DH(
x448ReceivingIdentityPrivateKey,
x448ReceivingSignedPrePrivateKey,
x448SendingIdentityKey,

View File

@ -0,0 +1,43 @@
package crypto
import (
"crypto"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/tries"
)
type FrameProver interface {
ProveMasterClockFrame(
previousFrame *protobufs.ClockFrame,
timestamp int64,
difficulty uint32,
) (*protobufs.ClockFrame, error)
ProveDataClockFrame(
previousFrame *protobufs.ClockFrame,
commitments [][]byte,
aggregateProofs []*protobufs.InclusionAggregateProof,
provingKey crypto.Signer,
timestamp int64,
difficulty uint32,
) (*protobufs.ClockFrame, error)
CreateMasterGenesisFrame(
filter []byte,
seed []byte,
difficulty uint32,
) (*protobufs.ClockFrame, error)
CreateDataGenesisFrame(
filter []byte,
origin []byte,
difficulty uint32,
inclusionProof *InclusionAggregateProof,
proverKeys [][]byte,
preDusk bool,
) (*protobufs.ClockFrame, *tries.RollingFrecencyCritbitTrie, error)
VerifyMasterClockFrame(
frame *protobufs.ClockFrame,
) error
VerifyDataClockFrame(
frame *protobufs.ClockFrame,
) error
}

View File

@ -0,0 +1,28 @@
package crypto
import "source.quilibrium.com/quilibrium/monorepo/node/protobufs"
type InclusionCommitment struct {
TypeUrl string
Data []byte
Commitment []byte
}
type InclusionAggregateProof struct {
InclusionCommitments []*InclusionCommitment
AggregateCommitment []byte
Proof []byte
}
type InclusionProver interface {
Commit(
data []byte,
typeUrl string,
) (*InclusionCommitment, error)
ProveAggregate(commits []*InclusionCommitment) (
*InclusionAggregateProof,
error,
)
VerifyAggregate(proof *InclusionAggregateProof) (bool, error)
VerifyFrame(frame *protobufs.ClockFrame) error
}

View File

@ -1,4 +1,4 @@
package crypto
package kzg
import (
"math/big"

View File

@ -1,4 +1,4 @@
package crypto
package kzg
import (
"encoding/binary"

View File

@ -1,4 +1,4 @@
package crypto_test
package kzg_test
import (
"encoding/hex"
@ -13,16 +13,16 @@ import (
"golang.org/x/crypto/sha3"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves/native/bls48581"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/kzg"
)
func TestMain(m *testing.M) {
csBytes, err := os.ReadFile("../ceremony.json")
csBytes, err := os.ReadFile("../../ceremony.json")
if err != nil {
panic(err)
}
cs := &crypto.CeremonyState{}
cs := &kzg.CeremonyState{}
if err := json.Unmarshal(csBytes, cs); err != nil {
panic(err)
}
@ -75,8 +75,8 @@ func TestMain(m *testing.M) {
wg.Wait()
crypto.CeremonyBLS48581G1 = g1s
crypto.CeremonyBLS48581G2 = g2s
kzg.CeremonyBLS48581G1 = g1s
kzg.CeremonyBLS48581G2 = g2s
// Post-ceremony, precompute everything and put it in the finalized ceremony
// state
@ -135,16 +135,16 @@ func TestMain(m *testing.M) {
wg.Add(len(sizes))
for i := range root {
i := i
crypto.RootOfUnityBLS48581[uint64(sizes[i])] = root[i]
crypto.RootsOfUnityBLS48581[uint64(sizes[i])] = roots[i]
crypto.ReverseRootsOfUnityBLS48581[uint64(sizes[i])] = reverseRoots[i]
kzg.RootOfUnityBLS48581[uint64(sizes[i])] = root[i]
kzg.RootsOfUnityBLS48581[uint64(sizes[i])] = roots[i]
kzg.ReverseRootsOfUnityBLS48581[uint64(sizes[i])] = reverseRoots[i]
go func() {
// We precomputed 65536, others are cheap and will be fully precomputed
// post-ceremony
if sizes[i] < 65536 {
fftG1, err := crypto.FFTG1(
crypto.CeremonyBLS48581G1[:sizes[i]],
fftG1, err := kzg.FFTG1(
kzg.CeremonyBLS48581G1[:sizes[i]],
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
),
@ -165,7 +165,7 @@ func TestMain(m *testing.M) {
wg.Wait()
for i := range root {
crypto.FFTBLS48581[uint64(sizes[i])] = ffts[i]
kzg.FFTBLS48581[uint64(sizes[i])] = ffts[i]
}
code := m.Run()
os.Exit(code)
@ -175,7 +175,7 @@ func TestKzgBytesToPoly(t *testing.T) {
modulus := make([]byte, 73)
bls48581.NewBIGints(bls48581.CURVE_Order, nil).ToBytes(modulus)
q := new(big.Int).SetBytes(modulus)
p := crypto.NewKZGProver(curves.BLS48581(curves.BLS48581G1().Point), sha3.New256, q)
p := kzg.NewKZGProver(curves.BLS48581(curves.BLS48581G1().Point), sha3.New256, q)
poly, err := p.BytesToPolynomial([]byte(
"Did you ever hear the tragedy of Darth Plagueis The Wise? I thought not." +
@ -217,7 +217,7 @@ func TestPolynomialCommitment(t *testing.T) {
modulus := make([]byte, 73)
bls48581.NewBIGints(bls48581.CURVE_Order, nil).ToBytes(modulus)
q := new(big.Int).SetBytes(modulus)
p := crypto.NewKZGProver(curves.BLS48581(curves.BLS48581G1().Point), sha3.New256, q)
p := kzg.NewKZGProver(curves.BLS48581(curves.BLS48581G1().Point), sha3.New256, q)
poly, err := p.BytesToPolynomial([]byte(
"Did you ever hear the tragedy of Darth Plagueis The Wise? I thought not." +
@ -235,7 +235,7 @@ func TestPolynomialCommitment(t *testing.T) {
poly = append(poly, curves.BLS48581G1().NewScalar().(curves.PairingScalar))
}
require.NoError(t, err)
evalPoly, err := crypto.FFT(
evalPoly, err := kzg.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
@ -247,12 +247,12 @@ func TestPolynomialCommitment(t *testing.T) {
require.NoError(t, err)
commitByCoeffs, err := p.PointLinearCombination(
crypto.CeremonyBLS48581G1[:16],
kzg.CeremonyBLS48581G1[:16],
poly,
)
require.NoError(t, err)
commitByEval, err := p.PointLinearCombination(
crypto.FFTBLS48581[16],
kzg.FFTBLS48581[16],
evalPoly,
)
require.NoError(t, err)
@ -265,7 +265,7 @@ func TestKZGProof(t *testing.T) {
modulus := make([]byte, 73)
bls48581.NewBIGints(bls48581.CURVE_Order, nil).ToBytes(modulus)
q := new(big.Int).SetBytes(modulus)
p := crypto.NewKZGProver(curves.BLS48581(curves.BLS48581G1().Point), sha3.New256, q)
p := kzg.NewKZGProver(curves.BLS48581(curves.BLS48581G1().Point), sha3.New256, q)
poly, err := p.BytesToPolynomial([]byte(
"Did you ever hear the tragedy of Darth Plagueis The Wise? I thought not." +
@ -284,7 +284,7 @@ func TestKZGProof(t *testing.T) {
poly = append(poly, curves.BLS48581G1().NewScalar().(curves.PairingScalar))
}
evalPoly, err := crypto.FFT(
evalPoly, err := kzg.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
@ -297,7 +297,7 @@ func TestKZGProof(t *testing.T) {
commit, err := p.Commit(poly)
require.NoError(t, err)
z := crypto.RootsOfUnityBLS48581[16][2]
z := kzg.RootsOfUnityBLS48581[16][2]
require.NoError(t, err)
checky := evalPoly[len(poly)-1]
@ -331,7 +331,7 @@ func TestKZGProof(t *testing.T) {
diff -= 1
}
proof, err := p.PointLinearCombination(crypto.CeremonyBLS48581G1[:15], out)
proof, err := p.PointLinearCombination(kzg.CeremonyBLS48581G1[:15], out)
// proof, err := p.Prove(evalPoly, commit, z.(curves.PairingScalar))
require.NoError(t, err)
require.True(t, p.Verify(commit, z, checky, proof))

View File

@ -0,0 +1,464 @@
package crypto
import (
"github.com/pkg/errors"
"go.uber.org/zap"
"golang.org/x/crypto/sha3"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/kzg"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
type KZGInclusionProver struct {
prover *kzg.KZGProver
logger *zap.Logger
}
func NewKZGInclusionProver(logger *zap.Logger) *KZGInclusionProver {
return &KZGInclusionProver{
prover: kzg.DefaultKZGProver(),
logger: logger,
}
}
// Commit implements InclusionProver.
func (k *KZGInclusionProver) Commit(
data []byte,
typeUrl string,
) (*InclusionCommitment, error) {
if typeUrl == protobufs.IntrinsicExecutionOutputType {
digest := sha3.NewShake256()
_, err := digest.Write(data)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return nil, errors.Wrap(err, "prove aggregate")
}
expand := make([]byte, 1024)
_, err = digest.Read(expand)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return nil, errors.Wrap(err, "prove aggregate")
}
poly, err := k.prover.BytesToPolynomial(expand)
if err != nil {
return nil, errors.Wrap(err, "commit")
}
k.logger.Debug("proving execution output for inclusion")
polys, err := kzg.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
),
16,
false,
)
if err != nil {
return nil, errors.Wrap(err, "prove")
}
k.logger.Debug("converted execution output chunk to evaluation form")
k.logger.Debug("creating kzg commitment")
points, err := k.prover.Commit(polys)
if err != nil {
return nil, errors.Wrap(err, "prove")
}
return &InclusionCommitment{
TypeUrl: typeUrl,
Data: data,
Commitment: points.ToAffineCompressed(),
}, nil
}
poly, err := k.prover.BytesToPolynomial(data)
if err != nil {
return nil, errors.Wrap(err, "commit")
}
points, err := k.prover.Commit(poly)
if err != nil {
return nil, errors.Wrap(err, "commit")
}
return &InclusionCommitment{
TypeUrl: typeUrl,
Data: data,
Commitment: points.ToAffineCompressed(),
}, nil
}
// ProveAggregate implements InclusionProver.
func (k *KZGInclusionProver) ProveAggregate(
commits []*InclusionCommitment,
) (*InclusionAggregateProof, error) {
polys := [][]curves.PairingScalar{}
commitPoints := []curves.PairingPoint{}
for _, commit := range commits {
switch commit.TypeUrl {
case protobufs.IntrinsicExecutionOutputType:
k.logger.Debug("confirming inclusion in aggregate")
digest := sha3.NewShake256()
_, err := digest.Write(commit.Data)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return nil, errors.Wrap(err, "prove aggregate")
}
expand := make([]byte, 1024)
_, err = digest.Read(expand)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return nil, errors.Wrap(err, "prove aggregate")
}
poly, err := k.prover.BytesToPolynomial(expand)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return nil, errors.Wrap(err, "prove aggregate")
}
evalPoly, err := kzg.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
),
16,
false,
)
if err != nil {
k.logger.Error(
"error performing fast fourier transform on key bundle",
zap.Error(err),
)
return nil, errors.Wrap(err, "prove aggregate")
}
k.logger.Debug(
"created fft of polynomial",
zap.Int("poly_size", len(evalPoly)),
)
polys = append(polys, evalPoly)
c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
commit.Commitment,
)
if err != nil {
return nil, errors.Wrap(err, "prove aggregate")
}
commitPoints = append(commitPoints, c.(curves.PairingPoint))
default:
k.logger.Debug("confirming inclusion in aggregate")
poly, err := k.prover.BytesToPolynomial(commit.Data)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return nil, errors.Wrap(err, "prove aggregate")
}
for i := 0; i < 128-len(poly); i++ {
poly = append(
poly,
curves.BLS48581G1().Scalar.Zero().(curves.PairingScalar),
)
}
evalPoly, err := kzg.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
),
128,
false,
)
if err != nil {
k.logger.Error(
"error performing fast fourier transform on key bundle",
zap.Error(err),
)
return nil, errors.Wrap(err, "prove aggregate")
}
k.logger.Debug(
"created fft of polynomial",
zap.Int("poly_size", len(evalPoly)),
)
polys = append(polys, evalPoly)
c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
commit.Commitment,
)
if err != nil {
k.logger.Error("could not verify clock frame", zap.Error(err))
return nil, errors.Wrap(err, "prove aggregate")
}
commitPoints = append(commitPoints, c.(curves.PairingPoint))
}
}
proof, commitment, err := k.prover.ProveAggregate(
polys,
commitPoints,
)
if err != nil {
return nil, errors.Wrap(err, "prove aggregate")
}
if proof.IsIdentity() {
return nil, errors.Wrap(errors.New("invalid proof"), "prove aggregate")
}
return &InclusionAggregateProof{
InclusionCommitments: commits,
AggregateCommitment: commitment.ToAffineCompressed(),
Proof: proof.ToAffineCompressed(),
}, nil
}
// VerifyAggregate implements InclusionProver.
func (k *KZGInclusionProver) VerifyAggregate(
proof *InclusionAggregateProof,
) (bool, error) {
polys := [][]curves.PairingScalar{}
commitPoints := []curves.PairingPoint{}
for _, commit := range proof.InclusionCommitments {
poly, err := k.prover.BytesToPolynomial(commit.Data)
if err != nil {
return false, errors.Wrap(err, "verify aggregate")
}
polys = append(polys, poly)
point, err := curves.BLS48581G1().Point.FromAffineCompressed(
commit.Commitment,
)
if err != nil {
return false, errors.Wrap(err, "verify aggregate")
}
commitPoints = append(commitPoints, point.(curves.PairingPoint))
}
aggregate, err := curves.BLS48581G1().Point.FromAffineCompressed(
proof.AggregateCommitment,
)
if err != nil {
return false, errors.Wrap(err, "verify aggregate")
}
proofPoint, err := curves.BLS48581G1().Point.FromAffineCompressed(
proof.Proof,
)
if err != nil {
return false, errors.Wrap(err, "verify aggregate")
}
verify, err := k.prover.VerifyAggregateProof(
polys,
commitPoints,
aggregate.(curves.PairingPoint),
proofPoint.(curves.PairingPoint),
)
return verify, errors.Wrap(err, "verify aggregate")
}
func (k *KZGInclusionProver) VerifyFrame(
frame *protobufs.ClockFrame,
) error {
aggregateCommitments := []curves.PairingPoint{}
for i := 0; i < (len(frame.Input)-516)/74; i++ {
c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
frame.Input[516+(i*74) : 516+(i*74)+74],
)
if err != nil {
k.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "verify frame")
}
aggregateCommitments = append(aggregateCommitments, c.(curves.PairingPoint))
}
if len(aggregateCommitments) != len(frame.AggregateProofs) {
k.logger.Error(
"commit length mismatched proof for frame",
zap.Int("commit_length", len(aggregateCommitments)),
zap.Int("proof_length", len(frame.AggregateProofs)),
)
return errors.Wrap(
errors.New("commit length mismatched proof for frame"),
"verify frame",
)
}
for i, proof := range frame.AggregateProofs {
aggregatePoly := [][]curves.PairingScalar{}
commitments := []curves.PairingPoint{}
for _, commit := range proof.GetInclusionCommitments() {
switch commit.TypeUrl {
case protobufs.IntrinsicExecutionOutputType:
k.logger.Debug("confirming inclusion in aggregate")
digest := sha3.NewShake256()
_, err := digest.Write(commit.Data)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return errors.Wrap(err, "verify frame")
}
expand := make([]byte, 1024)
_, err = digest.Read(expand)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return errors.Wrap(err, "verify frame")
}
poly, err := k.prover.BytesToPolynomial(expand)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return errors.Wrap(err, "verify frame")
}
evalPoly, err := kzg.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
),
16,
false,
)
if err != nil {
k.logger.Error(
"error performing fast fourier transform on key bundle",
zap.Error(err),
)
return errors.Wrap(err, "verify frame")
}
k.logger.Debug(
"created fft of polynomial",
zap.Int("poly_size", len(evalPoly)),
)
aggregatePoly = append(aggregatePoly, evalPoly)
c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
commit.Commitment,
)
if err != nil {
k.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "verify frame")
}
commitments = append(commitments, c.(curves.PairingPoint))
default:
k.logger.Debug("confirming inclusion in aggregate")
poly, err := k.prover.BytesToPolynomial(commit.Data)
if err != nil {
k.logger.Error(
"error converting key bundle to polynomial",
zap.Error(err),
)
return errors.Wrap(err, "verify frame")
}
for i := 0; i < 128-len(poly); i++ {
poly = append(
poly,
curves.BLS48581G1().Scalar.Zero().(curves.PairingScalar),
)
}
evalPoly, err := kzg.FFT(
poly,
*curves.BLS48581(
curves.BLS48581G1().NewGeneratorPoint(),
),
128,
false,
)
if err != nil {
k.logger.Error(
"error performing fast fourier transform on key bundle",
zap.Error(err),
)
return errors.Wrap(err, "verify frame")
}
k.logger.Debug(
"created fft of polynomial",
zap.Int("poly_size", len(evalPoly)),
)
aggregatePoly = append(aggregatePoly, evalPoly)
c, err := curves.BLS48581G1().NewGeneratorPoint().FromAffineCompressed(
commit.Commitment,
)
if err != nil {
k.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "verify frame")
}
commitments = append(commitments, c.(curves.PairingPoint))
}
}
p, err := curves.BLS48581G1().Point.FromAffineCompressed(
proof.Proof,
)
if err != nil {
k.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(err, "verify frame")
}
result, err := k.prover.VerifyAggregateProof(
aggregatePoly,
commitments,
aggregateCommitments[i],
p.(curves.PairingPoint),
)
if err != nil {
k.logger.Error(
"could not verify clock frame",
zap.Error(err),
)
return errors.Wrap(err, "verify frame")
}
if !result {
k.logger.Error("could not verify clock frame", zap.Error(err))
return errors.Wrap(
errors.New("invalid proof"),
"verify frame",
)
}
}
return nil
}
var _ InclusionProver = (*KZGInclusionProver)(nil)

View File

@ -0,0 +1,72 @@
package crypto_test
import (
"bytes"
"crypto/rand"
"testing"
"time"
"github.com/cloudflare/circl/sign/ed448"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/kzg"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
func TestKZGVerifyFrame(t *testing.T) {
kzg.TestInit("../ceremony.json")
data := make([]byte, 1024)
rand.Read(data)
l, _ := zap.NewProduction()
inclusionProver := crypto.NewKZGInclusionProver(l)
commitment, err := inclusionProver.Commit(
data,
protobufs.IntrinsicExecutionOutputType,
)
assert.NoError(t, err)
proof, err := inclusionProver.ProveAggregate(
[]*crypto.InclusionCommitment{commitment},
)
assert.NoError(t, err)
frame := &protobufs.ClockFrame{
Filter: []byte{0x00},
FrameNumber: 1,
Input: bytes.Repeat([]byte{0x00}, 516),
Output: bytes.Repeat([]byte{0x00}, 516),
}
_, priv, _ := ed448.GenerateKey(rand.Reader)
w := crypto.NewWesolowskiFrameProver(l)
frame, err = w.ProveDataClockFrame(
frame,
[][]byte{proof.AggregateCommitment},
[]*protobufs.InclusionAggregateProof{
{
Filter: []byte{0x00},
FrameNumber: 1,
InclusionCommitments: []*protobufs.InclusionCommitment{
{
Filter: []byte{0x00},
FrameNumber: 1,
TypeUrl: proof.InclusionCommitments[0].TypeUrl,
Commitment: proof.InclusionCommitments[0].Commitment,
Data: data,
Position: 0,
},
},
Proof: proof.Proof,
},
},
priv,
time.Now().UnixMilli(),
100,
)
err = inclusionProver.VerifyFrame(frame)
assert.NoError(t, err)
}

View File

@ -1,4 +1,4 @@
package crypto
package shuffle
import (
"crypto/rand"
@ -7,10 +7,16 @@ import (
"filippo.io/edwards25519"
)
var lBE = []byte{16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 222, 249, 222, 162, 247, 156, 214, 88, 18, 99, 26, 92, 245, 211, 236}
var lBE = []byte{
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 222, 249, 222, 162, 247,
156, 214, 88, 18, 99, 26, 92, 245, 211, 236,
}
var lBigInt = big.NewInt(0).SetBytes(lBE)
func genPolyFrags(secret *edwards25519.Scalar, total, threshold int) []*edwards25519.Scalar {
func genPolyFrags(
secret *edwards25519.Scalar,
total, threshold int,
) []*edwards25519.Scalar {
coeffs := []*edwards25519.Scalar{}
coeffs = append(coeffs, secret)
@ -48,7 +54,10 @@ func genPolyFrags(secret *edwards25519.Scalar, total, threshold int) []*edwards2
return frags
}
func ShamirSplitMatrix(matrix [][]*edwards25519.Scalar, total, threshold int) [][][]*edwards25519.Scalar {
func ShamirSplitMatrix(
matrix [][]*edwards25519.Scalar,
total, threshold int,
) [][][]*edwards25519.Scalar {
shamirMatrix := make([][][]*edwards25519.Scalar, len(matrix))
for x := 0; x < len(matrix); x++ {
@ -79,7 +88,9 @@ func AddMatrices(matrices ...[][]*edwards25519.Scalar) [][]*edwards25519.Scalar
return result
}
func GenerateRandomVectorShares(length, total, threshold int) [][]*edwards25519.Scalar {
func GenerateRandomVectorShares(
length, total, threshold int,
) [][]*edwards25519.Scalar {
result := make([][]*edwards25519.Scalar, length)
for i := 0; i < length; i++ {
@ -93,7 +104,10 @@ func GenerateRandomVectorShares(length, total, threshold int) [][]*edwards25519.
return result
}
func InterpolatePolynomialShares(shares []*edwards25519.Scalar, ids []int) *edwards25519.Scalar {
func InterpolatePolynomialShares(
shares []*edwards25519.Scalar,
ids []int,
) *edwards25519.Scalar {
var reconstructedSum *edwards25519.Scalar
for j := 0; j < len(ids); j++ {
@ -116,7 +130,10 @@ func InterpolatePolynomialShares(shares []*edwards25519.Scalar, ids []int) *edwa
coeffDenom.Invert(coeffDenom)
coeffNum.Multiply(coeffNum, coeffDenom)
reconstructedFrag := edwards25519.NewScalar().Multiply(coeffNum, shares[ids[j]-1])
reconstructedFrag := edwards25519.NewScalar().Multiply(
coeffNum,
shares[ids[j]-1],
)
if reconstructedSum == nil {
reconstructedSum = reconstructedFrag
@ -128,7 +145,9 @@ func InterpolatePolynomialShares(shares []*edwards25519.Scalar, ids []int) *edwa
return reconstructedSum
}
func LUDecompose(matrix [][]*edwards25519.Scalar) ([][]*edwards25519.Scalar, [][]*edwards25519.Scalar) {
func LUDecompose(
matrix [][]*edwards25519.Scalar,
) ([][]*edwards25519.Scalar, [][]*edwards25519.Scalar) {
imax := 0
maxA := edwards25519.NewScalar()
N := len(matrix)
@ -141,18 +160,24 @@ func LUDecompose(matrix [][]*edwards25519.Scalar) ([][]*edwards25519.Scalar, [][
pm[i] = make([]*edwards25519.Scalar, N)
p[i] = i
for j := 0; j < N; j++ {
newA[i][j], _ = edwards25519.NewScalar().SetCanonicalBytes(matrix[i][j].Bytes())
newA[i][j], _ = edwards25519.NewScalar().SetCanonicalBytes(
matrix[i][j].Bytes(),
)
}
}
scalarOne, _ := edwards25519.NewScalar().SetCanonicalBytes(BigIntToLEBytes(big.NewInt(int64(1))))
scalarOne, _ := edwards25519.NewScalar().SetCanonicalBytes(
BigIntToLEBytes(big.NewInt(int64(1))),
)
for i := 0; i < N; i++ {
maxA = edwards25519.NewScalar()
imax = i
for k := i; k < N; k++ {
if LEBytesToBigInt(newA[k][i].Bytes()).Cmp(LEBytesToBigInt(maxA.Bytes())) > 0 {
if LEBytesToBigInt(newA[k][i].Bytes()).Cmp(
LEBytesToBigInt(maxA.Bytes()),
) > 0 {
maxA = newA[k][i]
imax = k
}
@ -171,10 +196,16 @@ func LUDecompose(matrix [][]*edwards25519.Scalar) ([][]*edwards25519.Scalar, [][
}
for j := i + 1; j < N; j++ {
newA[j][i].Multiply(newA[j][i], edwards25519.NewScalar().Invert(newA[i][i]))
newA[j][i].Multiply(
newA[j][i],
edwards25519.NewScalar().Invert(newA[i][i]),
)
for k := i + 1; k < N; k++ {
newA[j][k].Subtract(newA[j][k], edwards25519.NewScalar().Multiply(newA[j][i], newA[i][k]))
newA[j][k].Subtract(newA[j][k], edwards25519.NewScalar().Multiply(
newA[j][i],
newA[i][k],
))
}
}
}
@ -205,13 +236,19 @@ func Invert(matrix [][]*edwards25519.Scalar) [][]*edwards25519.Scalar {
ia[i][j] = edwards25519.NewScalar().Set(p[i][j])
for k := 0; k < i; k++ {
ia[i][j].Subtract(ia[i][j], edwards25519.NewScalar().Multiply(a[i][k], ia[k][j]))
ia[i][j].Subtract(ia[i][j], edwards25519.NewScalar().Multiply(
a[i][k],
ia[k][j],
))
}
}
for i := len(matrix) - 1; i >= 0; i-- {
for k := i + 1; k < len(matrix); k++ {
ia[i][j].Subtract(ia[i][j], edwards25519.NewScalar().Multiply(a[i][k], ia[k][j]))
ia[i][j].Subtract(ia[i][j], edwards25519.NewScalar().Multiply(
a[i][k],
ia[k][j],
))
}
ia[i][j].Multiply(ia[i][j], edwards25519.NewScalar().Invert(a[i][i]))
@ -221,7 +258,10 @@ func Invert(matrix [][]*edwards25519.Scalar) [][]*edwards25519.Scalar {
return ia
}
func InterpolateMatrixShares(matrixShares [][][]*edwards25519.Scalar, ids []int) [][]*edwards25519.Scalar {
func InterpolateMatrixShares(
matrixShares [][][]*edwards25519.Scalar,
ids []int,
) [][]*edwards25519.Scalar {
matrix := make([][]*edwards25519.Scalar, len(matrixShares))
for x := 0; x < len(matrix); x++ {
@ -241,10 +281,17 @@ func ScalarMult(a int, b [][]*edwards25519.Scalar) [][]*edwards25519.Scalar {
for y := 0; y < len(b[0]); y++ {
if a >= 0 {
prod[x][y], _ = edwards25519.NewScalar().SetCanonicalBytes(BigIntToLEBytes(big.NewInt(int64(a))))
prod[x][y], _ = edwards25519.NewScalar().SetCanonicalBytes(
BigIntToLEBytes(big.NewInt(int64(a))),
)
} else {
negA, _ := edwards25519.NewScalar().SetCanonicalBytes(BigIntToLEBytes(big.NewInt(int64(-a))))
prod[x][y] = edwards25519.NewScalar().Subtract(edwards25519.NewScalar(), negA)
negA, _ := edwards25519.NewScalar().SetCanonicalBytes(
BigIntToLEBytes(big.NewInt(int64(-a))),
)
prod[x][y] = edwards25519.NewScalar().Subtract(
edwards25519.NewScalar(),
negA,
)
}
prod[x][y] = prod[x][y].Multiply(prod[x][y], b[x][y])
@ -254,7 +301,9 @@ func ScalarMult(a int, b [][]*edwards25519.Scalar) [][]*edwards25519.Scalar {
return prod
}
func GenerateDotProduct(a, b [][]*edwards25519.Scalar) [][]*edwards25519.Scalar {
func GenerateDotProduct(
a, b [][]*edwards25519.Scalar,
) [][]*edwards25519.Scalar {
if len(a[0]) != len(b) {
panic("cannot generate dot product of a and b - mismatched length")
}
@ -276,7 +325,9 @@ func GenerateDotProduct(a, b [][]*edwards25519.Scalar) [][]*edwards25519.Scalar
return abMatrix
}
func GenerateRandomMatrixAndInverseShares(size, total, threshold int) [2][][][]*edwards25519.Scalar {
func GenerateRandomMatrixAndInverseShares(
size, total, threshold int,
) [2][][][]*edwards25519.Scalar {
output := make([][]*edwards25519.Scalar, size)
for x := 0; x < size; x++ {
output[x] = make([]*edwards25519.Scalar, size)
@ -294,7 +345,9 @@ func GenerateRandomMatrixAndInverseShares(size, total, threshold int) [2][][][]*
return [2][][][]*edwards25519.Scalar{splitOutput, splitInverse}
}
func GenerateRandomBeaverTripleMatrixShares(size, total, threshold int) [3][][][]*edwards25519.Scalar {
func GenerateRandomBeaverTripleMatrixShares(
size, total, threshold int,
) [3][][][]*edwards25519.Scalar {
uMatrix := make([][]*edwards25519.Scalar, size)
vMatrix := make([][]*edwards25519.Scalar, size)
@ -321,7 +374,9 @@ func GenerateRandomBeaverTripleMatrixShares(size, total, threshold int) [3][][][
vMatrixShares := ShamirSplitMatrix(vMatrix, total, threshold)
uvMatrixShares := ShamirSplitMatrix(uvMatrix, total, threshold)
return [3][][][]*edwards25519.Scalar{uMatrixShares, vMatrixShares, uvMatrixShares}
return [3][][][]*edwards25519.Scalar{
uMatrixShares, vMatrixShares, uvMatrixShares,
}
}
func GeneratePermutationMatrix(size int) [][]*edwards25519.Scalar {

View File

@ -1,4 +1,4 @@
package crypto_test
package shuffle_test
import (
"fmt"
@ -7,11 +7,11 @@ import (
"filippo.io/edwards25519"
"github.com/stretchr/testify/assert"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/shuffle"
)
func TestGeneratePermutationMatrix(t *testing.T) {
m := crypto.GeneratePermutationMatrix(6)
m := shuffle.GeneratePermutationMatrix(6)
for _, x := range m {
ySum := byte(0x00)
for _, y := range x {
@ -39,14 +39,14 @@ func verifyLagrange(t *testing.T, shares []*edwards25519.Scalar, expected *edwar
var reconstructedSum *edwards25519.Scalar
for j := 0; j < threshold; j++ {
oneLENumBytes := crypto.BigIntToLEBytes(big.NewInt(1))
oneLENumBytes := shuffle.BigIntToLEBytes(big.NewInt(1))
coeffNum, _ := edwards25519.NewScalar().SetCanonicalBytes(oneLENumBytes)
coeffDenom, _ := edwards25519.NewScalar().SetCanonicalBytes(oneLENumBytes)
for k := 0; k < threshold; k++ {
if j != k {
ikBytes := crypto.BigIntToLEBytes(big.NewInt(int64(i + k)))
ijBytes := crypto.BigIntToLEBytes(big.NewInt(int64(i + j)))
ikBytes := shuffle.BigIntToLEBytes(big.NewInt(int64(i + k)))
ijBytes := shuffle.BigIntToLEBytes(big.NewInt(int64(i + j)))
ikScalar, _ := edwards25519.NewScalar().SetCanonicalBytes(ikBytes)
ijScalar, _ := edwards25519.NewScalar().SetCanonicalBytes(ijBytes)
@ -78,8 +78,8 @@ func verifyLagrange(t *testing.T, shares []*edwards25519.Scalar, expected *edwar
}
func TestGenerateShamirMatrix(t *testing.T) {
m := crypto.GeneratePermutationMatrix(6)
sm := crypto.ShamirSplitMatrix(m, 10, 3)
m := shuffle.GeneratePermutationMatrix(6)
sm := shuffle.ShamirSplitMatrix(m, 10, 3)
for xi, x := range sm {
for yi, y := range x {
verifyLagrange(t, y, m[xi][yi], 10, 3)
@ -88,11 +88,11 @@ func TestGenerateShamirMatrix(t *testing.T) {
}
func TestMatrixDotProduct(t *testing.T) {
zeroBytes := crypto.BigIntToLEBytes(big.NewInt(0))
oneBytes := crypto.BigIntToLEBytes(big.NewInt(1))
twoBytes := crypto.BigIntToLEBytes(big.NewInt(2))
threeBytes := crypto.BigIntToLEBytes(big.NewInt(3))
fourBytes := crypto.BigIntToLEBytes(big.NewInt(4))
zeroBytes := shuffle.BigIntToLEBytes(big.NewInt(0))
oneBytes := shuffle.BigIntToLEBytes(big.NewInt(1))
twoBytes := shuffle.BigIntToLEBytes(big.NewInt(2))
threeBytes := shuffle.BigIntToLEBytes(big.NewInt(3))
fourBytes := shuffle.BigIntToLEBytes(big.NewInt(4))
zero, _ := edwards25519.NewScalar().SetCanonicalBytes(zeroBytes)
one, _ := edwards25519.NewScalar().SetCanonicalBytes(oneBytes)
@ -110,7 +110,7 @@ func TestMatrixDotProduct(t *testing.T) {
{three, two, four},
}
abMatrix := crypto.GenerateDotProduct(aMatrix, bMatrix)
abMatrix := shuffle.GenerateDotProduct(aMatrix, bMatrix)
assert.Equal(t, byte(0x0a), abMatrix[0][0].Bytes()[0])
assert.Equal(t, byte(0x06), abMatrix[0][1].Bytes()[0])
assert.Equal(t, byte(0x0c), abMatrix[0][2].Bytes()[0])
@ -123,15 +123,15 @@ func TestMatrixDotProduct(t *testing.T) {
}
func TestGenerateRandomBeaverTripleMatrixShares(t *testing.T) {
beaverTripleShares := crypto.GenerateRandomBeaverTripleMatrixShares(6, 10, 3)
beaverTripleShares := shuffle.GenerateRandomBeaverTripleMatrixShares(6, 10, 3)
uMatrixShares := beaverTripleShares[0]
vMatrixShares := beaverTripleShares[1]
uvMatrixShares := beaverTripleShares[2]
uMatrix := crypto.InterpolateMatrixShares(uMatrixShares, []int{1, 2, 3})
vMatrix := crypto.InterpolateMatrixShares(vMatrixShares, []int{1, 2, 3})
uvMatrix := crypto.InterpolateMatrixShares(uvMatrixShares, []int{1, 2, 3})
uMatrix := shuffle.InterpolateMatrixShares(uMatrixShares, []int{1, 2, 3})
vMatrix := shuffle.InterpolateMatrixShares(vMatrixShares, []int{1, 2, 3})
uvMatrix := shuffle.InterpolateMatrixShares(uvMatrixShares, []int{1, 2, 3})
for x := 0; x < len(uMatrixShares); x++ {
for y := 0; y < len(uMatrixShares[0]); y++ {
@ -141,21 +141,21 @@ func TestGenerateRandomBeaverTripleMatrixShares(t *testing.T) {
}
}
uvCheck := crypto.GenerateDotProduct(uMatrix, vMatrix)
uvCheck := shuffle.GenerateDotProduct(uMatrix, vMatrix)
assert.Equal(t, uvMatrix, uvCheck)
}
func TestPermutationMatrix(t *testing.T) {
permutationMatrix1 := crypto.GeneratePermutationMatrix(6)
permutationMatrix2 := crypto.GeneratePermutationMatrix(6)
permutationMatrix3 := crypto.GeneratePermutationMatrix(6)
permutationMatrix4 := crypto.GeneratePermutationMatrix(6)
permutationMatrix1 := shuffle.GeneratePermutationMatrix(6)
permutationMatrix2 := shuffle.GeneratePermutationMatrix(6)
permutationMatrix3 := shuffle.GeneratePermutationMatrix(6)
permutationMatrix4 := shuffle.GeneratePermutationMatrix(6)
permutationMatrix := crypto.GenerateDotProduct(permutationMatrix1, permutationMatrix2)
permutationMatrix = crypto.GenerateDotProduct(permutationMatrix, permutationMatrix3)
permutationMatrix = crypto.GenerateDotProduct(permutationMatrix, permutationMatrix4)
permutationMatrix := shuffle.GenerateDotProduct(permutationMatrix1, permutationMatrix2)
permutationMatrix = shuffle.GenerateDotProduct(permutationMatrix, permutationMatrix3)
permutationMatrix = shuffle.GenerateDotProduct(permutationMatrix, permutationMatrix4)
one, _ := edwards25519.NewScalar().SetCanonicalBytes(crypto.BigIntToLEBytes(big.NewInt(1)))
one, _ := edwards25519.NewScalar().SetCanonicalBytes(shuffle.BigIntToLEBytes(big.NewInt(1)))
for x := 0; x < 6; x++ {
sumX := edwards25519.NewScalar()
@ -178,14 +178,14 @@ func TestPermutationMatrix(t *testing.T) {
}
func TestPermutationSharing(t *testing.T) {
permutationMatrix1 := crypto.GeneratePermutationMatrix(6)
permutationMatrix2 := crypto.GeneratePermutationMatrix(6)
permutationMatrix3 := crypto.GeneratePermutationMatrix(6)
permutationMatrix4 := crypto.GeneratePermutationMatrix(6)
permutationMatrixShares1 := crypto.ShamirSplitMatrix(permutationMatrix1, 4, 3)
permutationMatrixShares2 := crypto.ShamirSplitMatrix(permutationMatrix2, 4, 3)
permutationMatrixShares3 := crypto.ShamirSplitMatrix(permutationMatrix3, 4, 3)
permutationMatrixShares4 := crypto.ShamirSplitMatrix(permutationMatrix4, 4, 3)
permutationMatrix1 := shuffle.GeneratePermutationMatrix(6)
permutationMatrix2 := shuffle.GeneratePermutationMatrix(6)
permutationMatrix3 := shuffle.GeneratePermutationMatrix(6)
permutationMatrix4 := shuffle.GeneratePermutationMatrix(6)
permutationMatrixShares1 := shuffle.ShamirSplitMatrix(permutationMatrix1, 4, 3)
permutationMatrixShares2 := shuffle.ShamirSplitMatrix(permutationMatrix2, 4, 3)
permutationMatrixShares3 := shuffle.ShamirSplitMatrix(permutationMatrix3, 4, 3)
permutationMatrixShares4 := shuffle.ShamirSplitMatrix(permutationMatrix4, 4, 3)
inverseShareMatrix1 := make([][][]*edwards25519.Scalar, 4)
inverseShareMatrix2 := make([][][]*edwards25519.Scalar, 4)
@ -213,9 +213,9 @@ func TestPermutationSharing(t *testing.T) {
}
}
beaverTripleShares1 := crypto.GenerateRandomBeaverTripleMatrixShares(6, 4, 3)
beaverTripleShares2 := crypto.GenerateRandomBeaverTripleMatrixShares(6, 4, 3)
beaverTripleShares3 := crypto.GenerateRandomBeaverTripleMatrixShares(6, 4, 3)
beaverTripleShares1 := shuffle.GenerateRandomBeaverTripleMatrixShares(6, 4, 3)
beaverTripleShares2 := shuffle.GenerateRandomBeaverTripleMatrixShares(6, 4, 3)
beaverTripleShares3 := shuffle.GenerateRandomBeaverTripleMatrixShares(6, 4, 3)
beaverTriplesAShares1 := beaverTripleShares1[0]
beaverTriplesBShares1 := beaverTripleShares1[1]
@ -310,18 +310,18 @@ func TestPermutationSharing(t *testing.T) {
// f = b - v
// (a - u)(b - v) = -ab + ub + av - uv + (ab-av) + (ab - ub) + uv
e1 := crypto.InterpolateMatrixShares(es1, []int{1, 2, 3, 4})
f1 := crypto.InterpolateMatrixShares(fs1, []int{1, 2, 3, 4})
e2 := crypto.InterpolateMatrixShares(es2, []int{1, 2, 3, 4})
f2 := crypto.InterpolateMatrixShares(fs2, []int{1, 2, 3, 4})
e3 := crypto.InterpolateMatrixShares(es3, []int{1, 2, 3, 4})
f3 := crypto.InterpolateMatrixShares(fs3, []int{1, 2, 3, 4})
e1 := shuffle.InterpolateMatrixShares(es1, []int{1, 2, 3, 4})
f1 := shuffle.InterpolateMatrixShares(fs1, []int{1, 2, 3, 4})
e2 := shuffle.InterpolateMatrixShares(es2, []int{1, 2, 3, 4})
f2 := shuffle.InterpolateMatrixShares(fs2, []int{1, 2, 3, 4})
e3 := shuffle.InterpolateMatrixShares(es3, []int{1, 2, 3, 4})
f3 := shuffle.InterpolateMatrixShares(fs3, []int{1, 2, 3, 4})
// mul(a, b) => <e> = <a> - <u>, <f> = <b> - <v>, <c> = -i * e * f + f * <a> + e * <b> + <z>
ef1 := crypto.GenerateDotProduct(e1, f1)
ef2 := crypto.GenerateDotProduct(e2, f2)
ef3 := crypto.GenerateDotProduct(e3, f3)
ef1 := shuffle.GenerateDotProduct(e1, f1)
ef2 := shuffle.GenerateDotProduct(e2, f2)
ef3 := shuffle.GenerateDotProduct(e3, f3)
fa1 := make([][][]*edwards25519.Scalar, 4)
fa2 := make([][][]*edwards25519.Scalar, 4)
fa3 := make([][][]*edwards25519.Scalar, 4)
@ -336,15 +336,15 @@ func TestPermutationSharing(t *testing.T) {
inverseCS3 := make([][][]*edwards25519.Scalar, 6)
for i := 0; i < 4; i++ {
fa1[i] = crypto.GenerateDotProduct(inverseShareMatrix1[i], f1)
eb1[i] = crypto.GenerateDotProduct(e1, inverseShareMatrix2[i])
fa2[i] = crypto.GenerateDotProduct(inverseShareMatrix2[i], f2)
eb2[i] = crypto.GenerateDotProduct(e2, inverseShareMatrix3[i])
fa3[i] = crypto.GenerateDotProduct(inverseShareMatrix3[i], f3)
eb3[i] = crypto.GenerateDotProduct(e3, inverseShareMatrix4[i])
cs1[i] = crypto.AddMatrices(crypto.ScalarMult(-1, ef1), fa1[i], eb1[i], inverseBeaverTriplesABShares1[i])
cs2[i] = crypto.AddMatrices(crypto.ScalarMult(-1, ef2), fa2[i], eb2[i], inverseBeaverTriplesABShares2[i])
cs3[i] = crypto.AddMatrices(crypto.ScalarMult(-1, ef3), fa3[i], eb3[i], inverseBeaverTriplesABShares3[i])
fa1[i] = shuffle.GenerateDotProduct(inverseShareMatrix1[i], f1)
eb1[i] = shuffle.GenerateDotProduct(e1, inverseShareMatrix2[i])
fa2[i] = shuffle.GenerateDotProduct(inverseShareMatrix2[i], f2)
eb2[i] = shuffle.GenerateDotProduct(e2, inverseShareMatrix3[i])
fa3[i] = shuffle.GenerateDotProduct(inverseShareMatrix3[i], f3)
eb3[i] = shuffle.GenerateDotProduct(e3, inverseShareMatrix4[i])
cs1[i] = shuffle.AddMatrices(shuffle.ScalarMult(-1, ef1), fa1[i], eb1[i], inverseBeaverTriplesABShares1[i])
cs2[i] = shuffle.AddMatrices(shuffle.ScalarMult(-1, ef2), fa2[i], eb2[i], inverseBeaverTriplesABShares2[i])
cs3[i] = shuffle.AddMatrices(shuffle.ScalarMult(-1, ef3), fa3[i], eb3[i], inverseBeaverTriplesABShares3[i])
}
for x := 0; x < 6; x++ {
@ -360,12 +360,12 @@ func TestPermutationSharing(t *testing.T) {
}
}
c1 := crypto.InterpolateMatrixShares(inverseCS1, []int{1, 2, 3, 4})
c3 := crypto.InterpolateMatrixShares(inverseCS3, []int{1, 2, 3, 4})
c := crypto.GenerateDotProduct(c1, c3)
ab := crypto.GenerateDotProduct(permutationMatrix1, permutationMatrix2)
abc := crypto.GenerateDotProduct(ab, permutationMatrix3)
abcd := crypto.GenerateDotProduct(abc, permutationMatrix4)
c1 := shuffle.InterpolateMatrixShares(inverseCS1, []int{1, 2, 3, 4})
c3 := shuffle.InterpolateMatrixShares(inverseCS3, []int{1, 2, 3, 4})
c := shuffle.GenerateDotProduct(c1, c3)
ab := shuffle.GenerateDotProduct(permutationMatrix1, permutationMatrix2)
abc := shuffle.GenerateDotProduct(ab, permutationMatrix3)
abcd := shuffle.GenerateDotProduct(abc, permutationMatrix4)
for x := 0; x < 6; x++ {
for y := 0; y < 6; y++ {

View File

@ -0,0 +1,469 @@
package crypto
import (
"bytes"
"crypto"
"crypto/rand"
"encoding/binary"
"math/big"
"github.com/cloudflare/circl/sign/ed448"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/pkg/errors"
"go.uber.org/zap"
"golang.org/x/crypto/sha3"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/vdf"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/tries"
)
type WesolowskiFrameProver struct {
logger *zap.Logger
}
func NewWesolowskiFrameProver(logger *zap.Logger) *WesolowskiFrameProver {
return &WesolowskiFrameProver{
logger,
}
}
func (w *WesolowskiFrameProver) ProveMasterClockFrame(
previousFrame *protobufs.ClockFrame,
timestamp int64,
difficulty uint32,
) (*protobufs.ClockFrame, error) {
input := []byte{}
input = append(input, previousFrame.Filter...)
input = binary.BigEndian.AppendUint64(input, previousFrame.FrameNumber+1)
input = binary.BigEndian.AppendUint32(input, difficulty)
input = append(input, previousFrame.Output[:]...)
b := sha3.Sum256(input)
v := vdf.New(difficulty, b)
v.Execute()
o := v.GetOutput()
previousSelectorBytes := [516]byte{}
copy(previousSelectorBytes[:], previousFrame.Output[:516])
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
if err != nil {
return nil, errors.Wrap(err, "prove clock frame")
}
frame := &protobufs.ClockFrame{
Filter: previousFrame.Filter,
FrameNumber: previousFrame.FrameNumber + 1,
Timestamp: timestamp,
Difficulty: difficulty,
ParentSelector: parent.FillBytes(make([]byte, 32)),
Input: previousFrame.Output,
AggregateProofs: []*protobufs.InclusionAggregateProof{},
Output: o[:],
}
return frame, nil
}
func (w *WesolowskiFrameProver) VerifyMasterClockFrame(
frame *protobufs.ClockFrame,
) error {
input := []byte{}
input = append(input, frame.Filter...)
input = binary.BigEndian.AppendUint64(input, frame.FrameNumber)
input = binary.BigEndian.AppendUint32(input, frame.Difficulty)
input = append(input, frame.Input...)
if len(frame.Input) < 516 {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
if len(frame.AggregateProofs) > 0 {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
if frame.PublicKeySignature != nil {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
if len(frame.Input) != 516 {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
b := sha3.Sum256(input)
v := vdf.New(frame.Difficulty, b)
proof := [516]byte{}
copy(proof[:], frame.Output)
if !v.Verify(proof) {
w.logger.Error("invalid proof",
zap.Binary("filter", frame.Filter),
zap.Uint64("frame_number", frame.FrameNumber),
zap.Uint32("difficulty", frame.Difficulty),
zap.Binary("frame_input", frame.Input),
zap.Binary("frame_output", frame.Output),
)
return errors.Wrap(
errors.New("invalid proof"),
"verify clock frame",
)
}
previousSelectorBytes := [516]byte{}
copy(previousSelectorBytes[:], frame.Input[:516])
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
if err != nil {
return errors.Wrap(err, "verify clock frame")
}
selector := new(big.Int).SetBytes(frame.ParentSelector)
if parent.Cmp(selector) != 0 {
return errors.Wrap(
errors.New("selector did not match input"),
"verify clock frame",
)
}
return nil
}
func (w *WesolowskiFrameProver) CreateMasterGenesisFrame(
filter []byte,
seed []byte,
difficulty uint32,
) (
*protobufs.ClockFrame,
error,
) {
b := sha3.Sum256(seed)
v := vdf.New(difficulty, b)
v.Execute()
o := v.GetOutput()
inputMessage := o[:]
w.logger.Debug("proving genesis frame")
input := []byte{}
input = append(input, filter...)
input = binary.BigEndian.AppendUint64(input, 0)
input = binary.BigEndian.AppendUint32(input, difficulty)
if bytes.Equal(seed, []byte{0x00}) {
value := [516]byte{}
input = append(input, value[:]...)
} else {
input = append(input, seed...)
}
b = sha3.Sum256(input)
v = vdf.New(difficulty, b)
v.Execute()
o = v.GetOutput()
frame := &protobufs.ClockFrame{
Filter: filter,
FrameNumber: 0,
Timestamp: 0,
Difficulty: difficulty,
Input: inputMessage,
Output: o[:],
ParentSelector: []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
AggregateProofs: []*protobufs.InclusionAggregateProof{},
PublicKeySignature: nil,
}
return frame, nil
}
func (w *WesolowskiFrameProver) ProveDataClockFrame(
previousFrame *protobufs.ClockFrame,
commitments [][]byte,
aggregateProofs []*protobufs.InclusionAggregateProof,
provingKey crypto.Signer,
timestamp int64,
difficulty uint32,
) (*protobufs.ClockFrame, error) {
var pubkey []byte
pubkeyType := keys.KeyTypeEd448
ed448PublicKey, ok := provingKey.Public().(ed448.PublicKey)
if ok {
pubkey = []byte(ed448PublicKey)
} else {
return nil, errors.Wrap(
errors.New("no valid signature provided"),
"prove clock frame",
)
}
h, err := poseidon.HashBytes(pubkey)
if err != nil {
return nil, errors.Wrap(
errors.New("could not hash proving key"),
"prove clock frame",
)
}
address := h.Bytes()
input := []byte{}
input = append(input, previousFrame.Filter...)
input = binary.BigEndian.AppendUint64(input, previousFrame.FrameNumber+1)
input = binary.BigEndian.AppendUint64(input, uint64(timestamp))
input = binary.BigEndian.AppendUint32(input, difficulty)
input = append(input, address...)
input = append(input, previousFrame.Output[:]...)
commitmentInput := []byte{}
for _, commitment := range commitments {
commitmentInput = append(commitmentInput, commitment...)
}
input = append(input, commitmentInput...)
b := sha3.Sum256(input)
v := vdf.New(difficulty, b)
v.Execute()
o := v.GetOutput()
// TODO: make this configurable for signing algorithms that allow
// user-supplied hash functions
signature, err := provingKey.Sign(
rand.Reader,
append(append([]byte{}, b[:]...), o[:]...),
crypto.Hash(0),
)
if err != nil {
return nil, errors.Wrap(
err,
"prove",
)
}
previousSelectorBytes := [516]byte{}
copy(previousSelectorBytes[:], previousFrame.Output[:516])
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
if err != nil {
return nil, errors.Wrap(err, "prove clock frame")
}
frame := &protobufs.ClockFrame{
Filter: previousFrame.Filter,
FrameNumber: previousFrame.FrameNumber + 1,
Timestamp: timestamp,
Difficulty: difficulty,
ParentSelector: parent.Bytes(),
Input: append(
append([]byte{}, previousFrame.Output...),
commitmentInput...,
),
AggregateProofs: aggregateProofs,
Output: o[:],
}
switch pubkeyType {
case keys.KeyTypeEd448:
frame.PublicKeySignature = &protobufs.ClockFrame_PublicKeySignatureEd448{
PublicKeySignatureEd448: &protobufs.Ed448Signature{
Signature: signature,
PublicKey: &protobufs.Ed448PublicKey{
KeyValue: pubkey,
},
},
}
default:
return nil, errors.Wrap(
errors.New("unsupported proving key"),
"prove clock frame",
)
}
return frame, nil
}
func (w *WesolowskiFrameProver) CreateDataGenesisFrame(
filter []byte,
origin []byte,
difficulty uint32,
inclusionProof *InclusionAggregateProof,
proverKeys [][]byte,
preDusk bool,
) (*protobufs.ClockFrame, *tries.RollingFrecencyCritbitTrie, error) {
frameProverTrie := &tries.RollingFrecencyCritbitTrie{}
for _, s := range proverKeys {
addr, err := poseidon.HashBytes(s)
if err != nil {
panic(err)
}
addrBytes := addr.Bytes()
addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...)
frameProverTrie.Add(addrBytes, 0)
}
w.logger.Info("proving genesis frame")
input := []byte{}
input = append(input, filter...)
input = binary.BigEndian.AppendUint64(input, 0)
input = binary.BigEndian.AppendUint64(input, 0)
input = binary.BigEndian.AppendUint32(input, difficulty)
input = append(input, origin...)
if !preDusk {
input = append(input, inclusionProof.AggregateCommitment...)
}
b := sha3.Sum256(input)
v := vdf.New(difficulty, b)
v.Execute()
o := v.GetOutput()
commitments := []*protobufs.InclusionCommitment{}
for i, commit := range inclusionProof.InclusionCommitments {
commitments = append(commitments, &protobufs.InclusionCommitment{
Filter: filter,
FrameNumber: 0,
Position: uint32(i),
TypeUrl: commit.TypeUrl,
Data: commit.Data,
Commitment: commit.Commitment,
})
}
frame := &protobufs.ClockFrame{
Filter: filter,
FrameNumber: 0,
Timestamp: 0,
Difficulty: difficulty,
Input: append(
append([]byte{}, origin...),
inclusionProof.AggregateCommitment...,
),
Output: o[:],
ParentSelector: []byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
AggregateProofs: []*protobufs.InclusionAggregateProof{
{
Filter: filter,
FrameNumber: 0,
InclusionCommitments: commitments,
Proof: inclusionProof.Proof,
},
},
PublicKeySignature: nil,
}
return frame, frameProverTrie, nil
}
func (w *WesolowskiFrameProver) VerifyDataClockFrame(
frame *protobufs.ClockFrame,
) error {
var pubkey []byte
var signature []byte
pubkeyType := keys.KeyTypeEd448
ed448PublicKey := frame.GetPublicKeySignatureEd448()
if ed448PublicKey != nil {
pubkey = ed448PublicKey.PublicKey.KeyValue
signature = ed448PublicKey.Signature
} else {
return errors.Wrap(
errors.New("no valid signature provided"),
"verify clock frame",
)
}
h, err := poseidon.HashBytes(pubkey)
if err != nil {
return errors.Wrap(
errors.New("could not hash proving key"),
"verify clock frame",
)
}
address := h.Bytes()
input := []byte{}
input = append(input, frame.Filter...)
input = binary.BigEndian.AppendUint64(input, frame.FrameNumber)
input = binary.BigEndian.AppendUint64(input, uint64(frame.Timestamp))
input = binary.BigEndian.AppendUint32(input, frame.Difficulty)
input = append(input, address...)
input = append(input, frame.Input...)
if len(frame.Input) < 516 {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
b := sha3.Sum256(input)
v := vdf.New(frame.Difficulty, b)
proof := [516]byte{}
copy(proof[:], frame.Output)
// TODO: make this configurable for signing algorithms that allow
// user-supplied hash functions
switch pubkeyType {
case keys.KeyTypeEd448:
if len(pubkey) != 57 || len(signature) != 114 || !ed448.VerifyAny(
pubkey,
append(append([]byte{}, b[:]...), frame.Output...),
signature,
crypto.Hash(0),
) {
return errors.Wrap(
errors.New("invalid signature for issuer"),
"verify clock frame",
)
}
}
if !v.Verify(proof) {
return errors.Wrap(
errors.New("invalid proof"),
"verify clock frame",
)
}
previousSelectorBytes := [516]byte{}
copy(previousSelectorBytes[:], frame.Input[:516])
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
if err != nil {
return errors.Wrap(err, "verify clock frame")
}
selector := new(big.Int).SetBytes(frame.ParentSelector)
if parent.Cmp(selector) != 0 {
return errors.Wrap(
errors.New("selector did not match input"),
"verify clock frame",
)
}
return nil
}

View File

@ -0,0 +1,28 @@
package crypto_test
import (
"bytes"
"testing"
"time"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
)
func TestMasterProve(t *testing.T) {
l, _ := zap.NewProduction()
w := crypto.NewWesolowskiFrameProver(l)
m, err := w.CreateMasterGenesisFrame([]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}, bytes.Repeat([]byte{0x00}, 516), 10000)
assert.NoError(t, err)
next, err := w.ProveMasterClockFrame(m, time.Now().UnixMilli(), 10000)
assert.NoError(t, err)
err = w.VerifyMasterClockFrame(next)
assert.NoError(t, err)
}

View File

@ -667,7 +667,10 @@ func MaterializeApplicationFromFrame(
lastStateCount := frame.FrameNumber -
inProgress.ActiveParticipants[len(inProgress.ActiveParticipants)-1].FrameNumber
if !setCount || (lastStateCount < stateCount && lastStateCount > 20) {
stateCount = lastStateCount - 20
stateCount = lastStateCount
if stateCount >= 10 {
stateCount -= 10
}
}
}
@ -760,9 +763,26 @@ func MaterializeApplicationFromFrame(
func (a *CeremonyApplication) ApplyTransition(
currentFrameNumber uint64,
transition *protobufs.CeremonyLobbyStateTransition,
) (*CeremonyApplication, error) {
skipFailures bool,
) (
*CeremonyApplication,
*protobufs.CeremonyLobbyStateTransition,
*protobufs.CeremonyLobbyStateTransition,
error,
) {
finalizedTransition := &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{},
TransitionInputs: [][]byte{},
}
skippedTransition := &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{},
TransitionInputs: [][]byte{},
}
switch a.LobbyState {
case CEREMONY_APPLICATION_STATE_OPEN:
if a.StateCount > currentFrameNumber {
a.StateCount = 0
}
a.StateCount++
for i, url := range transition.TypeUrls {
@ -771,34 +791,75 @@ func (a *CeremonyApplication) ApplyTransition(
join := &protobufs.CeremonyLobbyJoin{}
err := proto.Unmarshal(transition.TransitionInputs[i], join)
if err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
if currentFrameNumber < join.FrameNumber {
return nil, errors.Wrap(
errors.New("too recent join"), "apply transition",
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("too recent join"), "apply transition",
)
}
skippedTransition.TypeUrls = append(
skippedTransition.TypeUrls,
url,
)
skippedTransition.TransitionInputs = append(
skippedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
continue
}
if currentFrameNumber-join.FrameNumber > 10 {
return nil, errors.Wrap(
errors.New("outdated join"), "apply transition",
)
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("outdated join"), "apply transition",
)
}
continue
}
if err = a.applyLobbyJoin(join); err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
finalizedTransition.TypeUrls = append(
finalizedTransition.TypeUrls,
url,
)
finalizedTransition.TransitionInputs = append(
finalizedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
a.StateCount = 0
default:
return a, nil
if !skipFailures {
return a, nil, nil, nil
}
skippedTransition.TypeUrls = append(
skippedTransition.TypeUrls,
url,
)
skippedTransition.TransitionInputs = append(
skippedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
continue
}
}
if a.StateCount > 10 {
if len(a.LobbyJoins) == 0 {
return a, nil
return a, finalizedTransition, skippedTransition, nil
}
a.LobbyState = CEREMONY_APPLICATION_STATE_IN_PROGRESS
@ -806,14 +867,17 @@ func (a *CeremonyApplication) ApplyTransition(
a.RoundCount = 1
if err := a.finalizeParticipantSet(); err != nil {
return nil, errors.Wrap(err, "apply transition")
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
a.LobbyJoins = []*protobufs.CeremonyLobbyJoin{}
}
return a, nil
return a, finalizedTransition, skippedTransition, nil
case CEREMONY_APPLICATION_STATE_IN_PROGRESS:
if a.StateCount > currentFrameNumber {
a.StateCount = 0
}
a.StateCount++
for i, url := range transition.TypeUrls {
switch url {
@ -821,61 +885,149 @@ func (a *CeremonyApplication) ApplyTransition(
seenProverAtt := &protobufs.CeremonySeenProverAttestation{}
err := proto.Unmarshal(transition.TransitionInputs[i], seenProverAtt)
if err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
if currentFrameNumber < seenProverAtt.LastSeenFrame {
return nil, errors.Wrap(
errors.New("too recent attestation"), "apply transition",
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("too recent attestation"), "apply transition",
)
}
skippedTransition.TypeUrls = append(
skippedTransition.TypeUrls,
url,
)
skippedTransition.TransitionInputs = append(
skippedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
continue
}
if currentFrameNumber-seenProverAtt.LastSeenFrame > 10 {
return nil, errors.Wrap(
errors.New("outdated attestation"), "apply transition",
)
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("outdated attestation"), "apply transition",
)
}
continue
}
if err = a.applySeenProverAttestation(seenProverAtt); err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
finalizedTransition.TypeUrls = append(
finalizedTransition.TypeUrls,
url,
)
finalizedTransition.TransitionInputs = append(
finalizedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
a.StateCount = 0
case protobufs.CeremonyDroppedProverAttestationType:
droppedProverAtt := &protobufs.CeremonyDroppedProverAttestation{}
err := proto.Unmarshal(transition.TransitionInputs[i], droppedProverAtt)
if err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
if currentFrameNumber < droppedProverAtt.LastSeenFrame {
return nil, errors.Wrap(
errors.New("too recent attestation"), "apply transition",
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("too recent attestation"), "apply transition",
)
}
skippedTransition.TypeUrls = append(
skippedTransition.TypeUrls,
url,
)
skippedTransition.TransitionInputs = append(
skippedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
continue
}
if currentFrameNumber-droppedProverAtt.LastSeenFrame > 10 {
return nil, errors.Wrap(
errors.New("outdated attestation"), "apply transition",
)
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("outdated attestation"), "apply transition",
)
}
continue
}
if err = a.applyDroppedProverAttestation(droppedProverAtt); err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
finalizedTransition.TypeUrls = append(
finalizedTransition.TypeUrls,
url,
)
finalizedTransition.TransitionInputs = append(
finalizedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
a.StateCount = 0
case protobufs.CeremonyTranscriptCommitType:
transcriptCommit := &protobufs.CeremonyTranscriptCommit{}
err := proto.Unmarshal(transition.TransitionInputs[i], transcriptCommit)
if err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
if err = a.applyTranscriptCommit(transcriptCommit); err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
finalizedTransition.TypeUrls = append(
finalizedTransition.TypeUrls,
url,
)
finalizedTransition.TransitionInputs = append(
finalizedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
a.StateCount = 0
default:
return a, nil
if !skipFailures {
return a, nil, nil, nil
}
skippedTransition.TypeUrls = append(
skippedTransition.TypeUrls,
url,
)
skippedTransition.TransitionInputs = append(
skippedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
continue
}
}
@ -898,7 +1050,7 @@ func (a *CeremonyApplication) ApplyTransition(
[]*protobufs.CeremonySeenProverAttestation{}
a.TranscriptRoundAdvanceCommits =
[]*protobufs.CeremonyAdvanceRound{}
return a, nil
return a, finalizedTransition, skippedTransition, nil
}
attLimit := 1<<a.RoundCount - 1
@ -949,8 +1101,11 @@ func (a *CeremonyApplication) ApplyTransition(
[]*protobufs.CeremonyAdvanceRound{}
}
return a, nil
return a, finalizedTransition, skippedTransition, nil
case CEREMONY_APPLICATION_STATE_FINALIZING:
if a.StateCount > currentFrameNumber {
a.StateCount = 0
}
a.StateCount++
for i, url := range transition.TypeUrls {
switch url {
@ -958,67 +1113,155 @@ func (a *CeremonyApplication) ApplyTransition(
seenProverAtt := &protobufs.CeremonySeenProverAttestation{}
err := proto.Unmarshal(transition.TransitionInputs[i], seenProverAtt)
if err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
if currentFrameNumber < seenProverAtt.LastSeenFrame {
return nil, errors.Wrap(
errors.New("too recent attestation"), "apply transition",
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("too recent attestation"), "apply transition",
)
}
skippedTransition.TypeUrls = append(
skippedTransition.TypeUrls,
url,
)
skippedTransition.TransitionInputs = append(
skippedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
continue
}
if currentFrameNumber-seenProverAtt.LastSeenFrame > 10 {
return nil, errors.Wrap(
errors.New("outdated attestation"), "apply transition",
)
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("outdated attestation"), "apply transition",
)
}
continue
}
if err = a.applySeenProverAttestation(seenProverAtt); err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
finalizedTransition.TypeUrls = append(
finalizedTransition.TypeUrls,
url,
)
finalizedTransition.TransitionInputs = append(
finalizedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
a.StateCount = 0
case protobufs.CeremonyDroppedProverAttestationType:
droppedProverAtt := &protobufs.CeremonyDroppedProverAttestation{}
err := proto.Unmarshal(transition.TransitionInputs[i], droppedProverAtt)
if err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
if currentFrameNumber < droppedProverAtt.LastSeenFrame {
return nil, errors.Wrap(
errors.New("too recent attestation"), "apply transition",
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("too recent attestation"), "apply transition",
)
}
skippedTransition.TypeUrls = append(
skippedTransition.TypeUrls,
url,
)
skippedTransition.TransitionInputs = append(
skippedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
continue
}
if currentFrameNumber-droppedProverAtt.LastSeenFrame > 10 {
return nil, errors.Wrap(
errors.New("outdated attestation"), "apply transition",
)
if !skipFailures {
return nil, nil, nil, errors.Wrap(
errors.New("outdated attestation"), "apply transition",
)
}
continue
}
if err = a.applyDroppedProverAttestation(droppedProverAtt); err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
finalizedTransition.TypeUrls = append(
finalizedTransition.TypeUrls,
url,
)
finalizedTransition.TransitionInputs = append(
finalizedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
a.StateCount = 0
case protobufs.CeremonyTranscriptShareType:
transcriptShare := &protobufs.CeremonyTranscriptShare{}
err := proto.Unmarshal(transition.TransitionInputs[i], transcriptShare)
if err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
if err = a.applyTranscriptShare(transcriptShare); err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
finalizedTransition.TypeUrls = append(
finalizedTransition.TypeUrls,
url,
)
finalizedTransition.TransitionInputs = append(
finalizedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
a.StateCount = 0
default:
return a, nil
if !skipFailures {
return a, nil, nil, nil
}
skippedTransition.TypeUrls = append(
skippedTransition.TypeUrls,
url,
)
skippedTransition.TransitionInputs = append(
skippedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
continue
}
}
if len(a.TranscriptShares) == len(a.ActiveParticipants) {
if err := a.finalizeTranscript(); err != nil {
return nil, errors.Wrap(err, "apply transition")
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
a.LobbyState = CEREMONY_APPLICATION_STATE_VALIDATING
@ -1027,7 +1270,7 @@ func (a *CeremonyApplication) ApplyTransition(
[]*protobufs.CeremonyDroppedProverAttestation{}
a.LatestSeenProverAttestations =
[]*protobufs.CeremonySeenProverAttestation{}
return a, finalizedTransition, skippedTransition, nil
}
shouldReset := false
@ -1048,7 +1291,7 @@ func (a *CeremonyApplication) ApplyTransition(
}
}
if a.StateCount > 10 {
if a.StateCount > 100 {
shouldReset = true
}
@ -1081,8 +1324,11 @@ func (a *CeremonyApplication) ApplyTransition(
[]*protobufs.CeremonyTranscriptShare{}
}
return a, nil
return a, finalizedTransition, skippedTransition, nil
case CEREMONY_APPLICATION_STATE_VALIDATING:
if a.StateCount > currentFrameNumber {
a.StateCount = 0
}
a.StateCount++
for i, url := range transition.TypeUrls {
switch url {
@ -1090,20 +1336,47 @@ func (a *CeremonyApplication) ApplyTransition(
transcript := &protobufs.CeremonyTranscript{}
err := proto.Unmarshal(transition.TransitionInputs[i], transcript)
if err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
if err = a.applyTranscript(transcript); err != nil {
return nil, errors.Wrap(err, "apply transition")
if !skipFailures {
return nil, nil, nil, errors.Wrap(err, "apply transition")
}
continue
}
finalizedTransition.TypeUrls = append(
finalizedTransition.TypeUrls,
url,
)
finalizedTransition.TransitionInputs = append(
finalizedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
a.StateCount = 0
default:
return a, nil
if !skipFailures {
return a, nil, nil, nil
}
skippedTransition.TypeUrls = append(
skippedTransition.TypeUrls,
url,
)
skippedTransition.TransitionInputs = append(
skippedTransition.TransitionInputs,
transition.TransitionInputs[i],
)
continue
}
}
shouldReset := false
if a.StateCount > 100 {
if a.StateCount > 300 {
shouldReset = true
}
@ -1160,9 +1433,9 @@ func (a *CeremonyApplication) ApplyTransition(
a.FinalCommits = []*protobufs.CeremonyTranscriptCommit{}
}
return a, nil
return a, finalizedTransition, skippedTransition, nil
default:
return nil, errors.Wrap(ErrInvalidStateTransition, "apply transition")
return nil, nil, nil, errors.Wrap(ErrInvalidStateTransition, "apply transition")
}
}

View File

@ -5,7 +5,6 @@ import (
"fmt"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
@ -79,43 +78,30 @@ func (a *CeremonyApplication) applyTranscriptShare(
return errors.Wrap(err, "apply transcript share")
}
eg := errgroup.Group{}
eg.SetLimit(100)
for i, g1 := range a.LatestTranscript.G1Powers {
i := i
g1 := g1
eg.Go(func() error {
if _, err := curves.BLS48581G1().Point.FromAffineCompressed(
g1.KeyValue,
); err != nil {
return errors.Wrap(
errors.Wrap(err, fmt.Sprintf("invalid g1 at position %d", i)),
"apply transcript share",
)
}
return nil
})
if _, err := curves.BLS48581G1().Point.FromAffineCompressed(
g1.KeyValue,
); err != nil {
return errors.Wrap(
errors.Wrap(err, fmt.Sprintf("invalid g1 at position %d", i)),
"apply transcript share",
)
}
}
for i, g2 := range a.LatestTranscript.G2Powers {
i := i
g2 := g2
eg.Go(func() error {
if _, err := curves.BLS48581G2().Point.FromAffineCompressed(
g2.KeyValue,
); err != nil {
return errors.Wrap(
errors.Wrap(err, fmt.Sprintf("invalid g2 at position %d", i)),
"apply transcript share",
)
}
return nil
})
}
if err := eg.Wait(); err != nil {
return err
if _, err := curves.BLS48581G2().Point.FromAffineCompressed(
g2.KeyValue,
); err != nil {
return errors.Wrap(
errors.Wrap(err, fmt.Sprintf("invalid g2 at position %d", i)),
"apply transcript share",
)
}
}
exists := false

View File

@ -100,26 +100,26 @@ func TestCeremonyTransitions(t *testing.T) {
joinBytes, err := proto.Marshal(join)
require.NoError(t, err)
a, err = a.ApplyTransition(0, &protobufs.CeremonyLobbyStateTransition{
a, _, _, err = a.ApplyTransition(0, &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{protobufs.CeremonyLobbyJoinType},
TransitionInputs: [][]byte{joinBytes},
})
}, false)
require.NoError(t, err)
require.Equal(t, a.LobbyState, CEREMONY_APPLICATION_STATE_OPEN)
for i := uint64(0); i < 10; i++ {
a, err = a.ApplyTransition(i+1, &protobufs.CeremonyLobbyStateTransition{
a, _, _, err = a.ApplyTransition(i+1, &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{},
TransitionInputs: [][]byte{},
})
}, false)
require.NoError(t, err)
require.Equal(t, a.LobbyState, CEREMONY_APPLICATION_STATE_OPEN)
}
a, err = a.ApplyTransition(12, &protobufs.CeremonyLobbyStateTransition{
a, _, _, err = a.ApplyTransition(12, &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{},
TransitionInputs: [][]byte{},
})
}, false)
require.NoError(t, err)
require.Equal(t, a.LobbyState, CEREMONY_APPLICATION_STATE_IN_PROGRESS)
require.True(t, bytes.Equal(
@ -165,10 +165,10 @@ func TestCeremonyTransitions(t *testing.T) {
advanceRoundBytes, err := proto.Marshal(advanceRound)
require.NoError(t, err)
a, err = a.ApplyTransition(13, &protobufs.CeremonyLobbyStateTransition{
a, _, _, err = a.ApplyTransition(13, &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{protobufs.CeremonyTranscriptCommitType},
TransitionInputs: [][]byte{advanceRoundBytes},
})
}, false)
require.NoError(t, err)
require.Equal(t, a.LobbyState, CEREMONY_APPLICATION_STATE_FINALIZING)
@ -209,10 +209,10 @@ func TestCeremonyTransitions(t *testing.T) {
shareBytes, err := proto.Marshal(transcriptShare)
require.NoError(t, err)
a, err = a.ApplyTransition(14, &protobufs.CeremonyLobbyStateTransition{
a, _, _, err = a.ApplyTransition(14, &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{protobufs.CeremonyTranscriptShareType},
TransitionInputs: [][]byte{shareBytes},
})
}, false)
require.NoError(t, err)
require.Equal(t, a.LobbyState, CEREMONY_APPLICATION_STATE_VALIDATING)
@ -272,10 +272,10 @@ func TestCeremonyTransitions(t *testing.T) {
}
transcriptBytes, err := proto.Marshal(updatedTranscript)
require.NoError(t, err)
a, err = a.ApplyTransition(15, &protobufs.CeremonyLobbyStateTransition{
a, _, _, err = a.ApplyTransition(15, &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{protobufs.CeremonyTranscriptType},
TransitionInputs: [][]byte{transcriptBytes},
})
}, false)
require.NoError(t, err)
require.Equal(t, a.LobbyState, CEREMONY_APPLICATION_STATE_OPEN)
bi, err := poseidon.HashBytes(proverPubKey)

View File

@ -12,8 +12,8 @@ import (
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
bls48581 "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves/native/bls48581"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/ot/base/simplest"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/execution/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/channel"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony/application"
)
func TestPairings(t *testing.T) {
@ -329,7 +329,7 @@ func TestCompositeConstructionOfBLS(t *testing.T) {
x448ReceivingIdentityKey := curves.ED448().NewGeneratorPoint().Mul(x448ReceivingIdentityPrivateKey)
x448ReceivingSignedPreKey := curves.ED448().NewGeneratorPoint().Mul(x448ReceivingSignedPrePrivateKey)
senderResult := crypto.SenderX3DH(
senderResult := channel.SenderX3DH(
x448SendingIdentityPrivateKey,
x448SendingEphemeralPrivateKey,
x448ReceivingIdentityKey,
@ -337,7 +337,7 @@ func TestCompositeConstructionOfBLS(t *testing.T) {
96,
)
receiverResult := crypto.ReceiverX3DH(
receiverResult := channel.ReceiverX3DH(
x448ReceivingIdentityPrivateKey,
x448ReceivingSignedPrePrivateKey,
x448SendingIdentityKey,
@ -345,7 +345,7 @@ func TestCompositeConstructionOfBLS(t *testing.T) {
96,
)
drSender, err := crypto.NewDoubleRatchetParticipant(
drSender, err := channel.NewDoubleRatchetParticipant(
senderResult[:32],
senderResult[32:64],
senderResult[64:],
@ -357,7 +357,7 @@ func TestCompositeConstructionOfBLS(t *testing.T) {
)
require.NoError(t, err)
drReceiver, err := crypto.NewDoubleRatchetParticipant(
drReceiver, err := channel.NewDoubleRatchetParticipant(
receiverResult[:32],
receiverResult[32:64],
receiverResult[64:],

View File

@ -4,8 +4,12 @@ import (
"bytes"
"crypto"
"crypto/rand"
"encoding/base64"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"strings"
"sync"
@ -18,15 +22,19 @@ import (
"google.golang.org/protobuf/types/known/anypb"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/vdf"
"source.quilibrium.com/quilibrium/monorepo/node/config"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/ceremony"
"source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/kzg"
"source.quilibrium.com/quilibrium/monorepo/node/execution"
"source.quilibrium.com/quilibrium/monorepo/node/execution/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
"source.quilibrium.com/quilibrium/monorepo/node/store"
"source.quilibrium.com/quilibrium/monorepo/node/tries"
)
type CeremonyExecutionEngine struct {
@ -41,6 +49,7 @@ type CeremonyExecutionEngine struct {
provingKey crypto.Signer
proverPublicKey []byte
provingKeyAddress []byte
inclusionProver qcrypto.InclusionProver
participantMx sync.Mutex
peerChannels map[string]*p2p.PublicP2PChannel
activeSecrets []curves.Scalar
@ -50,6 +59,7 @@ type CeremonyExecutionEngine struct {
seenMessageMap map[string]bool
seenMessageMx sync.Mutex
intrinsicFilter []byte
frameProver qcrypto.FrameProver
}
func NewCeremonyExecutionEngine(
@ -57,7 +67,10 @@ func NewCeremonyExecutionEngine(
engineConfig *config.EngineConfig,
keyManager keys.KeyManager,
pubSub p2p.PubSub,
frameProver qcrypto.FrameProver,
inclusionProver qcrypto.InclusionProver,
clockStore store.ClockStore,
masterTimeReel *time.MasterTimeReel,
keyStore store.KeyStore,
) *CeremonyExecutionEngine {
if logger == nil {
@ -74,6 +87,31 @@ func NewCeremonyExecutionEngine(
p2p.GetBloomFilterIndices(application.CEREMONY_ADDRESS, 65536, 24)...,
)
_, _, err = clockStore.GetDataClockFrame(intrinsicFilter, 0)
var origin []byte
var inclusionProof *qcrypto.InclusionAggregateProof
var proverKeys [][]byte
if err != nil && errors.Is(err, store.ErrNotFound) {
origin, inclusionProof, proverKeys = CreateGenesisState(
logger,
engineConfig,
nil,
inclusionProver,
)
}
dataTimeReel := time.NewDataTimeReel(
intrinsicFilter,
logger,
clockStore,
engineConfig,
frameProver,
origin,
inclusionProof,
proverKeys,
)
clock := ceremony.NewCeremonyDataClockConsensusEngine(
engineConfig,
logger,
@ -81,6 +119,10 @@ func NewCeremonyExecutionEngine(
clockStore,
keyStore,
pubSub,
frameProver,
inclusionProver,
masterTimeReel,
dataTimeReel,
intrinsicFilter,
seed,
)
@ -93,6 +135,8 @@ func NewCeremonyExecutionEngine(
clockStore: clockStore,
keyStore: keyStore,
pubSub: pubSub,
inclusionProver: inclusionProver,
frameProver: frameProver,
participantMx: sync.Mutex{},
peerChannels: map[string]*p2p.PublicP2PChannel{},
alreadyPublishedShare: false,
@ -139,13 +183,234 @@ func (
}
}
// Creates a genesis state for the intrinsic
func CreateGenesisState(
logger *zap.Logger,
engineConfig *config.EngineConfig,
testProverKeys [][]byte,
inclusionProver qcrypto.InclusionProver,
) (
[]byte,
*qcrypto.InclusionAggregateProof,
[][]byte,
) {
seed, err := hex.DecodeString(engineConfig.GenesisSeed)
if err != nil {
panic(errors.New("genesis seed is nil"))
}
logger.Info("creating genesis frame")
for _, l := range strings.Split(string(seed), "\n") {
logger.Info(l)
}
b := sha3.Sum256(seed)
v := vdf.New(engineConfig.Difficulty, b)
v.Execute()
o := v.GetOutput()
inputMessage := o[:]
// Signatories are special, they don't have an inclusion proof because they
// have not broadcasted communication keys, but they still get contribution
// rights prior to PoMW, because they did produce meaningful work in the
// first phase:
logger.Info("encoding signatories to prover trie")
proverKeys := [][]byte{}
if len(testProverKeys) != 0 {
logger.Warn(
"TEST PROVER ENTRIES BEING ADDED, YOUR NODE WILL BE KICKED IF IN" +
" PRODUCTION",
)
proverKeys = testProverKeys
} else {
for _, s := range kzg.CeremonySignatories {
pubkey := s.ToAffineCompressed()
logger.Info("0x" + hex.EncodeToString(pubkey))
proverKeys = append(proverKeys, pubkey)
}
}
logger.Info("encoding ceremony and phase one signatories")
transcript := &protobufs.CeremonyTranscript{}
for p, s := range kzg.CeremonyBLS48581G1 {
transcript.G1Powers = append(
transcript.G1Powers,
&protobufs.BLS48581G1PublicKey{
KeyValue: s.ToAffineCompressed(),
},
)
logger.Info(fmt.Sprintf("encoded G1 power %d", p))
}
for p, s := range kzg.CeremonyBLS48581G2 {
transcript.G2Powers = append(
transcript.G2Powers,
&protobufs.BLS48581G2PublicKey{
KeyValue: s.ToAffineCompressed(),
},
)
logger.Info(fmt.Sprintf("encoded G2 power %d", p))
}
transcript.RunningG1_256Witnesses = append(
transcript.RunningG1_256Witnesses,
&protobufs.BLS48581G1PublicKey{
KeyValue: kzg.CeremonyRunningProducts[0].ToAffineCompressed(),
},
)
transcript.RunningG2_256Powers = append(
transcript.RunningG2_256Powers,
&protobufs.BLS48581G2PublicKey{
KeyValue: kzg.CeremonyBLS48581G2[len(kzg.CeremonyBLS48581G2)-1].
ToAffineCompressed(),
},
)
outputProof := &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{},
TransitionInputs: [][]byte{},
}
proofBytes, err := proto.Marshal(outputProof)
if err != nil {
panic(err)
}
logger.Info("encoded transcript")
logger.Info("encoding ceremony signatories into application state")
rewardTrie := &tries.RewardCritbitTrie{}
for _, s := range kzg.CeremonySignatories {
pubkey := s.ToAffineCompressed()
addr, err := poseidon.HashBytes(pubkey)
if err != nil {
panic(err)
}
addrBytes := addr.Bytes()
addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...)
rewardTrie.Add(addrBytes, 0, 50)
}
// 2024-01-03: 1.2.0
d, err := os.ReadFile("./retroactive_peers.json")
if err != nil {
panic(err)
}
type peerData struct {
PeerId string `json:"peer_id"`
TokenBalance uint64 `json:"token_balance"`
}
type rewards struct {
Rewards []peerData `json:"rewards"`
}
retroEntries := &rewards{}
err = json.Unmarshal(d, retroEntries)
if err != nil {
panic(err)
}
logger.Info("adding retroactive peer reward info")
for _, s := range retroEntries.Rewards {
peerId := s.PeerId
peerBytes, err := base64.StdEncoding.DecodeString(peerId)
if err != nil {
panic(err)
}
addr, err := poseidon.HashBytes(peerBytes)
if err != nil {
panic(err)
}
addrBytes := addr.Bytes()
addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...)
rewardTrie.Add(addrBytes, 0, s.TokenBalance)
}
trieBytes, err := rewardTrie.Serialize()
if err != nil {
panic(err)
}
ceremonyLobbyState := &protobufs.CeremonyLobbyState{
LobbyState: 0,
CeremonyState: &protobufs.CeremonyLobbyState_CeremonyOpenState{
CeremonyOpenState: &protobufs.CeremonyOpenState{
JoinedParticipants: []*protobufs.CeremonyLobbyJoin{},
PreferredParticipants: []*protobufs.Ed448PublicKey{},
},
},
LatestTranscript: transcript,
RewardTrie: trieBytes,
}
outputBytes, err := proto.Marshal(ceremonyLobbyState)
if err != nil {
panic(err)
}
executionOutput := &protobufs.IntrinsicExecutionOutput{
Address: application.CEREMONY_ADDRESS,
Output: outputBytes,
Proof: proofBytes,
}
data, err := proto.Marshal(executionOutput)
if err != nil {
panic(err)
}
logger.Info("encoded execution output")
digest := sha3.NewShake256()
_, err = digest.Write(data)
if err != nil {
panic(err)
}
expand := make([]byte, 1024)
_, err = digest.Read(expand)
if err != nil {
panic(err)
}
logger.Info("proving execution output for inclusion")
commitment, err := inclusionProver.Commit(
expand,
protobufs.IntrinsicExecutionOutputType,
)
if err != nil {
panic(err)
}
logger.Info("creating kzg proof")
proof, err := inclusionProver.ProveAggregate(
[]*qcrypto.InclusionCommitment{
commitment,
},
)
if err != nil {
panic(err)
}
logger.Info("finalizing execution proof")
return inputMessage, proof, proverKeys
}
// Start implements ExecutionEngine
func (e *CeremonyExecutionEngine) Start() <-chan error {
errChan := make(chan error)
e.logger.Info("ceremony data loaded", zap.Binary(
"g2_power",
qcrypto.CeremonyBLS48581G2[1].ToAffineCompressed(),
kzg.CeremonyBLS48581G2[1].ToAffineCompressed(),
))
go func() {
@ -203,7 +468,7 @@ func (e *CeremonyExecutionEngine) ProcessMessage(
return nil, nil
}
if err := frame.VerifyDataClockFrame(); err != nil {
if err := e.frameProver.VerifyDataClockFrame(frame); err != nil {
return nil, errors.Wrap(err, "process message")
}
@ -221,7 +486,6 @@ func (e *CeremonyExecutionEngine) ProcessMessage(
case protobufs.CeremonyTranscriptShareType:
fallthrough
case protobufs.CeremonyTranscriptType:
frame := e.activeClockFrame
hash := sha3.Sum256(any.Value)
if any.TypeUrl == protobufs.CeremonyTranscriptType {
e.seenMessageMx.Lock()
@ -237,10 +501,6 @@ func (e *CeremonyExecutionEngine) ProcessMessage(
e.seenMessageMx.Unlock()
}
if e.clock.IsInProverTrie(e.proverPublicKey) {
app, err := application.MaterializeApplicationFromFrame(frame)
if err != nil {
return nil, errors.Wrap(err, "process message")
}
proposedTransition := &protobufs.CeremonyLobbyStateTransition{
TypeUrls: []string{any.TypeUrl},
TransitionInputs: [][]byte{
@ -248,11 +508,6 @@ func (e *CeremonyExecutionEngine) ProcessMessage(
},
}
_, err = app.ApplyTransition(frame.FrameNumber, proposedTransition)
if err != nil {
return nil, errors.Wrap(err, "process message")
}
any := &anypb.Any{}
if err := any.MarshalFrom(proposedTransition); err != nil {
return nil, errors.Wrap(err, "process message")
@ -293,14 +548,13 @@ func (e *CeremonyExecutionEngine) ProcessMessage(
func (e *CeremonyExecutionEngine) RunWorker() {
frameChan := e.clock.GetFrameChannel()
for {
frameFromBuffer := <-frameChan
frame := e.clock.GetFrame()
frame := <-frameChan
e.activeClockFrame = frame
e.logger.Info(
"evaluating next frame",
zap.Int(
"last_run_took_frames",
int(frame.FrameNumber)-int(frameFromBuffer.FrameNumber),
zap.Uint64(
"frame_number",
frame.FrameNumber,
),
)
app, err := application.MaterializeApplicationFromFrame(frame)
@ -519,9 +773,7 @@ func (e *CeremonyExecutionEngine) RunWorker() {
}
case application.CEREMONY_APPLICATION_STATE_VALIDATING:
e.logger.Info("round contribution validating")
// Do a best effort to clear Go's GC is noisy and unenforceable, but
// this should at least mark it as dead space
e.activeSecrets = []curves.Scalar{}
e.alreadyPublishedShare = false
for _, c := range e.peerChannels {
c.Close()
}
@ -1134,7 +1386,7 @@ func (e *CeremonyExecutionEngine) VerifyExecution(
return errors.Wrap(err, "verify execution")
}
a, err = a.ApplyTransition(frame.FrameNumber, transition)
a, _, _, err = a.ApplyTransition(frame.FrameNumber, transition, false)
if err != nil {
return errors.Wrap(err, "verify execution")
}

View File

@ -25,6 +25,11 @@ require (
source.quilibrium.com/quilibrium/monorepo/nekryptology v0.0.0-00010101000000-000000000000
)
require (
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
)
require (
github.com/DataDog/zstd v1.4.5 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
@ -59,7 +64,7 @@ require (
github.com/quic-go/qtls-go1-19 v0.3.3 // indirect
github.com/quic-go/qtls-go1-20 v0.2.3 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
golang.org/x/term v0.14.0
golang.org/x/term v0.16.0
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
@ -155,19 +160,17 @@ require (
github.com/stretchr/testify v1.8.4
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.14.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.uber.org/dig v1.17.0 // indirect
go.uber.org/fx v1.20.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.25.0
golang.org/x/crypto v0.13.0
go.uber.org/zap v1.26.0
golang.org/x/crypto v0.18.0
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sync v0.3.0
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sync v0.6.0
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect
gonum.org/v1/gonum v0.11.0 // indirect
google.golang.org/grpc v1.58.2

View File

@ -494,8 +494,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -510,8 +510,8 @@ golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ=
golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8=
@ -546,8 +546,8 @@ golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -561,8 +561,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -583,16 +583,16 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -18,8 +18,8 @@ import (
"github.com/pkg/errors"
"source.quilibrium.com/quilibrium/monorepo/node/app"
"source.quilibrium.com/quilibrium/monorepo/node/config"
qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/execution/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/kzg"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/rpc"
)
@ -131,7 +131,7 @@ func main() {
}
fmt.Println("Loading ceremony state and starting node...")
qcrypto.Init()
kzg.Init()
node, err := app.NewNode(nodeConfig)
if err != nil {
@ -283,5 +283,5 @@ func printLogo() {
func printVersion() {
fmt.Println(" ")
fmt.Println(" Quilibrium Node - v1.2.3 Dawn")
fmt.Println(" Quilibrium Node - v1.2.4 Dawn")
}

View File

@ -10,7 +10,7 @@ import (
"google.golang.org/protobuf/proto"
"source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
"source.quilibrium.com/quilibrium/monorepo/node/crypto"
"source.quilibrium.com/quilibrium/monorepo/node/crypto/channel"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
)
@ -18,7 +18,7 @@ import (
// A simplified P2P channel the pair of actors communicating is public
// knowledge, even though the data itself is encrypted.
type PublicP2PChannel struct {
participant *crypto.DoubleRatchetParticipant
participant *channel.DoubleRatchetParticipant
sendMap map[uint64][]byte
receiveMap map[uint64][]byte
pubSub PubSub
@ -58,7 +58,7 @@ func NewPublicP2PChannel(
senderIdentifier...,
)
channel := &PublicP2PChannel{
ch := &PublicP2PChannel{
publicChannelClient: publicChannelClient,
sendMap: map[uint64][]byte{},
receiveMap: map[uint64][]byte{},
@ -72,19 +72,19 @@ func NewPublicP2PChannel(
}
var err error
var participant *crypto.DoubleRatchetParticipant
var participant *channel.DoubleRatchetParticipant
if initiator {
sendingEphemeralPrivateKey := curve.Scalar.Random(
rand.Reader,
)
x3dh := crypto.SenderX3DH(
x3dh := channel.SenderX3DH(
sendingIdentityPrivateKey,
sendingSignedPrePrivateKey,
receivingIdentityKey,
receivingSignedPreKey,
96,
)
participant, err = crypto.NewDoubleRatchetParticipant(
participant, err = channel.NewDoubleRatchetParticipant(
x3dh[:32],
x3dh[32:64],
x3dh[64:],
@ -98,14 +98,14 @@ func NewPublicP2PChannel(
return nil, errors.Wrap(err, "new public p2p channel")
}
} else {
x3dh := crypto.SenderX3DH(
x3dh := channel.SenderX3DH(
sendingIdentityPrivateKey,
sendingSignedPrePrivateKey,
receivingIdentityKey,
receivingSignedPreKey,
96,
)
participant, err = crypto.NewDoubleRatchetParticipant(
participant, err = channel.NewDoubleRatchetParticipant(
x3dh[:32],
x3dh[32:64],
x3dh[64:],
@ -120,7 +120,7 @@ func NewPublicP2PChannel(
}
}
channel.participant = participant
ch.participant = participant
if publicChannelClient == nil {
pubSub.Subscribe(
sendFilter,
@ -130,12 +130,12 @@ func NewPublicP2PChannel(
pubSub.Subscribe(
receiveFilter,
channel.handleReceive,
ch.handleReceive,
true,
)
}
return channel, nil
return ch, nil
}
func (c *PublicP2PChannel) handleReceive(message *pb.Message) error {

View File

@ -1,130 +1,13 @@
package protobufs
import (
"crypto"
"crypto/rand"
"encoding/binary"
"math/big"
"time"
"github.com/cloudflare/circl/sign/ed448"
"github.com/iden3/go-iden3-crypto/ff"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/pkg/errors"
"golang.org/x/crypto/sha3"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/vdf"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
)
func ProveMasterClockFrame(
previousFrame *ClockFrame,
difficulty uint32,
) (*ClockFrame, error) {
input := []byte{}
input = append(input, previousFrame.Filter...)
input = binary.BigEndian.AppendUint64(input, previousFrame.FrameNumber+1)
input = binary.BigEndian.AppendUint32(input, difficulty)
input = append(input, previousFrame.Output[:]...)
b := sha3.Sum256(input)
v := vdf.New(difficulty, b)
v.Execute()
o := v.GetOutput()
timestamp := time.Now().UnixMilli()
previousSelectorBytes := [516]byte{}
copy(previousSelectorBytes[:], previousFrame.Output[:516])
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
if err != nil {
return nil, errors.Wrap(err, "prove clock frame")
}
frame := &ClockFrame{
Filter: previousFrame.Filter,
FrameNumber: previousFrame.FrameNumber + 1,
Timestamp: timestamp,
Difficulty: difficulty,
ParentSelector: parent.Bytes(),
Input: previousFrame.Output,
AggregateProofs: []*InclusionAggregateProof{},
Output: o[:],
}
return frame, nil
}
func (frame *ClockFrame) VerifyMasterClockFrame() error {
input := []byte{}
input = append(input, frame.Filter...)
input = binary.BigEndian.AppendUint64(input, frame.FrameNumber)
input = binary.BigEndian.AppendUint32(input, frame.Difficulty)
input = append(input, frame.Input...)
if len(frame.Input) < 516 {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
if len(frame.AggregateProofs) > 0 {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
if frame.PublicKeySignature != nil {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
if len(frame.Input) != 516 {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
b := sha3.Sum256(input)
v := vdf.New(frame.Difficulty, b)
proof := [516]byte{}
copy(proof[:], frame.Output)
if !v.Verify(proof) {
return errors.Wrap(
errors.New("invalid proof"),
"verify clock frame",
)
}
previousSelectorBytes := [516]byte{}
copy(previousSelectorBytes[:], frame.Input[:516])
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
if err != nil {
return errors.Wrap(err, "verify clock frame")
}
selector := new(big.Int).SetBytes(frame.ParentSelector)
if parent.Cmp(selector) != 0 {
return errors.Wrap(
errors.New("selector did not match input"),
"verify clock frame",
)
}
return nil
}
func (frame *ClockFrame) GetParentSelectorAndDistance(
discriminator *big.Int,
) (
*big.Int,
func (frame *ClockFrame) GetParentAndSelector() (
*big.Int,
*big.Int,
error,
@ -134,32 +17,16 @@ func (frame *ClockFrame) GetParentSelectorAndDistance(
selector, err := poseidon.HashBytes(outputBytes[:])
if err != nil {
return nil, nil, nil, errors.Wrap(err, "get parent selector and distance")
return nil, nil, errors.Wrap(err, "get parent selector and distance")
}
if frame.FrameNumber == 0 {
return big.NewInt(0), big.NewInt(0), selector, nil
return big.NewInt(0), selector, nil
}
parentSelector := new(big.Int).SetBytes(frame.ParentSelector)
var distance *big.Int
if discriminator != nil {
l := new(big.Int).Mod(
new(big.Int).Sub(selector, discriminator),
ff.Modulus(),
)
r := new(big.Int).Mod(
new(big.Int).Sub(discriminator, selector),
ff.Modulus(),
)
distance = r
if l.Cmp(r) == 1 {
distance = l
}
}
return parentSelector, distance, selector, nil
return parentSelector, selector, nil
}
func (frame *ClockFrame) GetSelector() (*big.Int, error) {
@ -216,196 +83,3 @@ func (frame *ClockFrame) GetAddress() ([]byte, error) {
return addressBytes, nil
}
func ProveDataClockFrame(
previousFrame *ClockFrame,
commitments [][]byte,
aggregateProofs []*InclusionAggregateProof,
provingKey crypto.Signer,
difficulty uint32,
) (*ClockFrame, error) {
var pubkey []byte
pubkeyType := keys.KeyTypeEd448
ed448PublicKey, ok := provingKey.Public().(ed448.PublicKey)
if ok {
pubkey = []byte(ed448PublicKey)
} else {
return nil, errors.Wrap(
errors.New("no valid signature provided"),
"prove clock frame",
)
}
h, err := poseidon.HashBytes(pubkey)
if err != nil {
return nil, errors.Wrap(
errors.New("could not hash proving key"),
"prove clock frame",
)
}
address := h.Bytes()
timestamp := time.Now().UnixMilli()
input := []byte{}
input = append(input, previousFrame.Filter...)
input = binary.BigEndian.AppendUint64(input, previousFrame.FrameNumber+1)
input = binary.BigEndian.AppendUint64(input, uint64(timestamp))
input = binary.BigEndian.AppendUint32(input, difficulty)
input = append(input, address...)
input = append(input, previousFrame.Output[:]...)
commitmentInput := []byte{}
for _, commitment := range commitments {
commitmentInput = append(commitmentInput, commitment...)
}
input = append(input, commitmentInput...)
b := sha3.Sum256(input)
v := vdf.New(difficulty, b)
v.Execute()
o := v.GetOutput()
// TODO: make this configurable for signing algorithms that allow
// user-supplied hash functions
signature, err := provingKey.Sign(
rand.Reader,
append(append([]byte{}, b[:]...), o[:]...),
crypto.Hash(0),
)
if err != nil {
return nil, errors.Wrap(
err,
"prove",
)
}
previousSelectorBytes := [516]byte{}
copy(previousSelectorBytes[:], previousFrame.Output[:516])
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
if err != nil {
return nil, errors.Wrap(err, "prove clock frame")
}
frame := &ClockFrame{
Filter: previousFrame.Filter,
FrameNumber: previousFrame.FrameNumber + 1,
Timestamp: timestamp,
Difficulty: difficulty,
ParentSelector: parent.Bytes(),
Input: append(
append([]byte{}, previousFrame.Output...),
commitmentInput...,
),
AggregateProofs: aggregateProofs,
Output: o[:],
}
switch pubkeyType {
case keys.KeyTypeEd448:
frame.PublicKeySignature = &ClockFrame_PublicKeySignatureEd448{
PublicKeySignatureEd448: &Ed448Signature{
Signature: signature,
PublicKey: &Ed448PublicKey{
KeyValue: pubkey,
},
},
}
default:
return nil, errors.Wrap(
errors.New("unsupported proving key"),
"prove clock frame",
)
}
return frame, nil
}
func (frame *ClockFrame) VerifyDataClockFrame() error {
var pubkey []byte
var signature []byte
pubkeyType := keys.KeyTypeEd448
ed448PublicKey := frame.GetPublicKeySignatureEd448()
if ed448PublicKey != nil {
pubkey = ed448PublicKey.PublicKey.KeyValue
signature = ed448PublicKey.Signature
} else {
return errors.Wrap(
errors.New("no valid signature provided"),
"verify clock frame",
)
}
h, err := poseidon.HashBytes(pubkey)
if err != nil {
return errors.Wrap(
errors.New("could not hash proving key"),
"verify clock frame",
)
}
address := h.Bytes()
input := []byte{}
input = append(input, frame.Filter...)
input = binary.BigEndian.AppendUint64(input, frame.FrameNumber)
input = binary.BigEndian.AppendUint64(input, uint64(frame.Timestamp))
input = binary.BigEndian.AppendUint32(input, frame.Difficulty)
input = append(input, address...)
input = append(input, frame.Input...)
if len(frame.Input) < 516 {
return errors.Wrap(
errors.New("invalid input"),
"verify clock frame",
)
}
b := sha3.Sum256(input)
v := vdf.New(frame.Difficulty, b)
proof := [516]byte{}
copy(proof[:], frame.Output)
// TODO: make this configurable for signing algorithms that allow
// user-supplied hash functions
switch pubkeyType {
case keys.KeyTypeEd448:
if len(pubkey) != 57 || len(signature) != 114 || !ed448.VerifyAny(
pubkey,
append(append([]byte{}, b[:]...), frame.Output...),
signature,
crypto.Hash(0),
) {
return errors.Wrap(
errors.New("invalid signature for issuer"),
"verify clock frame",
)
}
}
if !v.Verify(proof) {
return errors.Wrap(
errors.New("invalid proof"),
"verify clock frame",
)
}
previousSelectorBytes := [516]byte{}
copy(previousSelectorBytes[:], frame.Input[:516])
parent, err := poseidon.HashBytes(previousSelectorBytes[:])
if err != nil {
return errors.Wrap(err, "verify clock frame")
}
selector := new(big.Int).SetBytes(frame.ParentSelector)
if parent.Cmp(selector) != 0 {
return errors.Wrap(
errors.New("selector did not match input"),
"verify clock frame",
)
}
return nil
}

View File

@ -17,7 +17,7 @@ import (
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"
"source.quilibrium.com/quilibrium/monorepo/node/execution"
"source.quilibrium.com/quilibrium/monorepo/node/execution/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/ceremony/application"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/p2p"
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"

View File

@ -4,8 +4,6 @@ import (
"bytes"
"encoding/binary"
"fmt"
"math/big"
"sort"
"github.com/cockroachdb/pebble"
"github.com/iden3/go-iden3-crypto/poseidon"
@ -60,6 +58,12 @@ type ClockStore interface {
frame *protobufs.ClockFrame,
txn Transaction,
) error
GetCandidateDataClockFrame(
filter []byte,
frameNumber uint64,
parentSelector []byte,
distance []byte,
) (*protobufs.ClockFrame, error)
GetCandidateDataClockFrames(
filter []byte,
frameNumber uint64,
@ -1055,6 +1059,45 @@ func (p *PebbleClockStore) PutDataClockFrame(
return nil
}
func (p *PebbleClockStore) GetCandidateDataClockFrame(
filter []byte,
frameNumber uint64,
parentSelector []byte,
distance []byte,
) (*protobufs.ClockFrame, error) {
value, closer, err := p.db.Get(clockDataCandidateFrameKey(
filter,
frameNumber,
parentSelector,
distance,
))
if err != nil {
if errors.Is(err, pebble.ErrNotFound) {
return nil, ErrNotFound
}
return nil, errors.Wrap(err, "get candidate data clock frame")
}
defer closer.Close()
frame := &protobufs.ClockFrame{}
if err := proto.Unmarshal(value, frame); err != nil {
return nil, errors.Wrap(
errors.Wrap(err, ErrInvalidData.Error()),
"get candidate data clock frame",
)
}
if err = p.fillAggregateProofs(frame); err != nil {
return nil, errors.Wrap(
errors.Wrap(err, ErrInvalidData.Error()),
"get candidate data clock frame",
)
}
return frame, nil
}
// GetCandidateDataClockFrames implements ClockStore.
// Distance is 32-byte aligned, so we just use a 0x00 * 32 -> 0xff * 32 range
func (p *PebbleClockStore) GetCandidateDataClockFrames(
@ -1421,162 +1464,6 @@ func (p *PebbleClockStore) GetCompressedDataClockFrames(
return nil, errors.Wrap(err, "get compressed data clock frames")
}
if len(syncMessage.TruncatedClockFrames) < int(
toFrameNumber-fromFrameNumber+1,
) {
newFrom := fromFrameNumber
if len(syncMessage.TruncatedClockFrames) > 0 {
newFrom = syncMessage.TruncatedClockFrames[len(
syncMessage.TruncatedClockFrames,
)-1].FrameNumber + 1
}
from := clockDataCandidateFrameKey(
filter,
newFrom,
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
[]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
)
to := clockDataCandidateFrameKey(
filter,
toFrameNumber+1,
[]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
[]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
)
iter, err := p.db.NewIter(from, to)
if err != nil {
return nil, errors.Wrap(err, "get compressed data clock frames")
}
candidates := []*protobufs.ClockFrame{}
for iter.First(); iter.Valid(); iter.Next() {
value := iter.Value()
frame := &protobufs.ClockFrame{}
if err := proto.Unmarshal(value, frame); err != nil {
return nil, errors.Wrap(err, "get compressed data clock frames")
}
candidates = append(candidates, frame)
}
if err := iter.Close(); err != nil {
return nil, errors.Wrap(err, "get compressed data clock frames")
}
sort.Slice(candidates, func(i, j int) bool {
return candidates[i].FrameNumber < candidates[j].FrameNumber
})
if len(candidates) > 0 {
cursorStart := candidates[0].FrameNumber
paths := [][]*protobufs.ClockFrame{}
for _, frame := range candidates {
frame := frame
if frame.FrameNumber == cursorStart {
paths = append(paths, []*protobufs.ClockFrame{frame})
}
if frame.FrameNumber > cursorStart {
for i, path := range paths {
s, err := path[len(path)-1].GetSelector()
if err != nil {
return nil, errors.Wrap(err, "get compressed data clock frames")
}
parentSelector, _, _, err := frame.GetParentSelectorAndDistance(nil)
if err != nil {
return nil, errors.Wrap(err, "get compressed data clock frames")
}
if s.Cmp(parentSelector) == 0 {
paths[i] = append(paths[i], frame)
}
}
}
}
sort.Slice(paths, func(i, j int) bool {
return len(paths[i]) > len(paths[j])
})
leadingIndex := 0
var leadingScore *big.Int
length := len(paths[0])
for i := 0; i < len(paths); i++ {
if len(paths[i]) < length {
break
}
score := new(big.Int)
for _, path := range paths[i] {
master, err := p.GetMasterClockFrame(
[]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
},
path.FrameNumber,
)
if err != nil {
return nil, errors.Wrap(err, "get compressed data clock frames")
}
discriminator, err := master.GetSelector()
if err != nil {
return nil, errors.Wrap(err, "get compressed data clock frames")
}
_, distance, _, err := path.GetParentSelectorAndDistance(
discriminator,
)
if err != nil {
return nil, errors.Wrap(err, "get compressed data clock frames")
}
score = score.Add(score, distance)
}
if leadingScore == nil || leadingScore.Cmp(score) > 0 {
leadingIndex = i
leadingScore = score
}
}
for _, frame := range paths[leadingIndex] {
frame := frame
syncMessage.TruncatedClockFrames = append(
syncMessage.TruncatedClockFrames,
frame,
)
if frame.FrameNumber == 0 {
continue
}
for i := 0; i < len(frame.Input[516:])/74; i++ {
aggregateCommit := frame.Input[516+(i*74) : 516+((i+1)*74)]
if _, ok := proofs[string(aggregateCommit)]; !ok {
proofs[string(aggregateCommit)] = &protobufs.InclusionProofsMap{
FrameCommit: aggregateCommit,
}
}
}
}
}
}
for k, v := range proofs {
k := k
v := v