From 0cd6b41f5da55d1a51a63fcb52b1dda0ce9a339c Mon Sep 17 00:00:00 2001 From: Cassandra Heart <7929478+CassOnMars@users.noreply.github.com> Date: Tue, 13 Feb 2024 01:04:56 -0600 Subject: [PATCH] v1.2.4 (#43) --- node/app/db_console.go | 6 +- node/app/node.go | 2 +- node/app/wire.go | 9 +- node/app/wire_gen.go | 13 +- .../consensus/ceremony/broadcast_messaging.go | 275 +---- .../ceremony_data_clock_consensus_engine.go | 334 +++--- node/consensus/ceremony/consensus_frames.go | 1001 ++--------------- .../ceremony/execution_registration.go | 18 +- .../ceremony/handle_pending_commit_worker.go | 85 -- node/consensus/ceremony/peer_messaging.go | 9 +- node/consensus/consensus_engine.go | 6 +- node/consensus/master/broadcast_messaging.go | 73 +- node/consensus/master/consensus_frames.go | 305 ++--- .../master/execution_registration.go | 27 +- .../master/master_clock_consensus_engine.go | 98 +- node/consensus/master/peer_messaging.go | 20 +- node/consensus/time/data_time_reel.go | 607 ++++++++++ node/consensus/time/data_time_reel_test.go | 407 +++++++ node/consensus/time/master_time_reel.go | 221 ++++ node/consensus/time/master_time_reel_test.go | 82 ++ node/consensus/time/time_reel.go | 14 + node/crypto/{ => channel}/doubleratchet.go | 2 +- .../{ => channel}/doubleratchet_test.go | 12 +- node/crypto/{ => channel}/x3dh.go | 2 +- node/crypto/{ => channel}/x3dh_test.go | 8 +- node/crypto/frame_prover.go | 43 + node/crypto/inclusion_prover.go | 28 + node/crypto/{ => kzg}/fft.go | 2 +- node/crypto/{ => kzg}/kzg.go | 2 +- node/crypto/{ => kzg}/kzg_test.go | 42 +- node/crypto/kzg_inclusion_prover.go | 464 ++++++++ node/crypto/kzg_inclusion_prover_test.go | 72 ++ node/crypto/{ => shuffle}/matrix.go | 101 +- node/crypto/{ => shuffle}/matrix_test.go | 124 +- node/crypto/wesolowski_frame_prover.go | 469 ++++++++ node/crypto/wesolowski_frame_prover_test.go | 28 + .../application/ceremony_application.go | 391 ++++++- .../ceremony_application_finalizing.go | 46 +- .../ceremony_application_in_progress.go | 0 .../application/ceremony_application_open.go | 0 .../application/ceremony_application_test.go | 24 +- .../ceremony_application_validating.go | 0 .../ceremony_application_validating_test.go | 0 .../ceremony/application/multiply.go | 0 .../application/oblivious_powers_of_tau.go | 0 .../oblivious_powers_of_tau_test.go | 12 +- .../ceremony/ceremony_execution_engine.go | 296 ++++- node/go.mod | 21 +- node/go.sum | 28 +- node/main.go | 8 +- node/p2p/public_channel.go | 22 +- node/protobufs/clock.go | 334 +----- node/rpc/rpc_server.go | 2 +- node/store/clock.go | 203 +--- 54 files changed, 3821 insertions(+), 2577 deletions(-) delete mode 100644 node/consensus/ceremony/handle_pending_commit_worker.go create mode 100644 node/consensus/time/data_time_reel.go create mode 100644 node/consensus/time/data_time_reel_test.go create mode 100644 node/consensus/time/master_time_reel.go create mode 100644 node/consensus/time/master_time_reel_test.go create mode 100644 node/consensus/time/time_reel.go rename node/crypto/{ => channel}/doubleratchet.go (99%) rename node/crypto/{ => channel}/doubleratchet_test.go (92%) rename node/crypto/{ => channel}/x3dh.go (99%) rename node/crypto/{ => channel}/x3dh_test.go (88%) create mode 100644 node/crypto/frame_prover.go create mode 100644 node/crypto/inclusion_prover.go rename node/crypto/{ => kzg}/fft.go (99%) rename node/crypto/{ => kzg}/kzg.go (99%) rename node/crypto/{ => kzg}/kzg_test.go (91%) create mode 100644 node/crypto/kzg_inclusion_prover.go create mode 100644 node/crypto/kzg_inclusion_prover_test.go rename node/crypto/{ => shuffle}/matrix.go (81%) rename node/crypto/{ => shuffle}/matrix_test.go (78%) create mode 100644 node/crypto/wesolowski_frame_prover.go create mode 100644 node/crypto/wesolowski_frame_prover_test.go rename node/execution/{ => intrinsics}/ceremony/application/ceremony_application.go (78%) rename node/execution/{ => intrinsics}/ceremony/application/ceremony_application_finalizing.go (89%) rename node/execution/{ => intrinsics}/ceremony/application/ceremony_application_in_progress.go (100%) rename node/execution/{ => intrinsics}/ceremony/application/ceremony_application_open.go (100%) rename node/execution/{ => intrinsics}/ceremony/application/ceremony_application_test.go (93%) rename node/execution/{ => intrinsics}/ceremony/application/ceremony_application_validating.go (100%) rename node/execution/{ => intrinsics}/ceremony/application/ceremony_application_validating_test.go (100%) rename node/execution/{ => intrinsics}/ceremony/application/multiply.go (100%) rename node/execution/{ => intrinsics}/ceremony/application/oblivious_powers_of_tau.go (100%) rename node/execution/{ => intrinsics}/ceremony/application/oblivious_powers_of_tau_test.go (97%) rename node/execution/{ => intrinsics}/ceremony/ceremony_execution_engine.go (81%) diff --git a/node/app/db_console.go b/node/app/db_console.go index e6d028a..7266674 100644 --- a/node/app/db_console.go +++ b/node/app/db_console.go @@ -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 } diff --git a/node/app/node.go b/node/app/node.go index f6f2ddb..ce9b1fe 100644 --- a/node/app/node.go +++ b/node/app/node.go @@ -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" diff --git a/node/app/wire.go b/node/app/wire.go index 588381f..26b95d9 100644 --- a/node/app/wire.go +++ b/node/app/wire.go @@ -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, ) diff --git a/node/app/wire_gen.go b/node/app/wire_gen.go index 40fb5dc..23ae566 100644 --- a/node/app/wire_gen.go +++ b/node/app/wire_gen.go @@ -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), diff --git a/node/consensus/ceremony/broadcast_messaging.go b/node/consensus/ceremony/broadcast_messaging.go index bf6feca..ed3a55c 100644 --- a/node/consensus/ceremony/broadcast_messaging.go +++ b/node/consensus/ceremony/broadcast_messaging.go @@ -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 diff --git a/node/consensus/ceremony/ceremony_data_clock_consensus_engine.go b/node/consensus/ceremony/ceremony_data_clock_consensus_engine.go index bd4faca..f58cee0 100644 --- a/node/consensus/ceremony/ceremony_data_clock_consensus_engine.go +++ b/node/consensus/ceremony/ceremony_data_clock_consensus_engine.go @@ -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 { diff --git a/node/consensus/ceremony/consensus_frames.go b/node/consensus/ceremony/consensus_frames.go index f3d8228..eab23f1 100644 --- a/node/consensus/ceremony/consensus_frames.go +++ b/node/consensus/ceremony/consensus_frames.go @@ -1,908 +1,142 @@ package ceremony import ( - "bytes" "context" - "encoding/base64" - "encoding/binary" - "encoding/hex" - "encoding/json" - "fmt" "io" - "math/big" - "os" - "strings" "time" - "github.com/iden3/go-iden3-crypto/ff" - "github.com/iden3/go-iden3-crypto/poseidon" "github.com/pkg/errors" "go.uber.org/zap" - "golang.org/x/crypto/sha3" "google.golang.org/grpc" "google.golang.org/protobuf/proto" - "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves" - "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/vdf" "source.quilibrium.com/quilibrium/monorepo/node/consensus" qcrypto "source.quilibrium.com/quilibrium/monorepo/node/crypto" - "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" ) func (e *CeremonyDataClockConsensusEngine) prove( previousFrame *protobufs.ClockFrame, ) (*protobufs.ClockFrame, error) { - if e.state == consensus.EngineStateProving { - if !e.frameProverTrie.Contains(e.provingKeyAddress) { - e.stagedKeyCommitsMx.Lock() - e.stagedKeyCommits = make( - map[curves.PairingPoint]*protobufs.InclusionCommitment, - ) - e.stagedKeyPolynomials = make( - map[curves.PairingPoint][]curves.PairingScalar, - ) - e.stagedKeyCommitsMx.Unlock() - e.stagedLobbyStateTransitionsMx.Lock() - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - - e.state = consensus.EngineStateCollecting - return previousFrame, nil - } - e.logger.Info("proving new frame") - - commitments := [][]byte{} - aggregations := []*protobufs.InclusionAggregateProof{} - - e.stagedKeyCommitsMx.Lock() - if len(e.stagedKeyCommits) > 0 && len(e.stagedKeyPolynomials) > 0 { - e.logger.Debug( - "adding staged key commits to frame", - zap.Uint64("frame_number", previousFrame.FrameNumber+1), - ) - keyCommitments := []curves.PairingPoint{} - keyInclusions := []*protobufs.InclusionCommitment{} - keyPolynomials := [][]curves.PairingScalar{} - i := uint32(0) - - for commit, inclusion := range e.stagedKeyCommits { - e.logger.Debug( - "adding staged key commit to aggregate proof", - zap.Uint64("frame_number", previousFrame.FrameNumber+1), - zap.Uint32("position", i), - ) - keyCommitments = append(keyCommitments, commit) - inclusion.FrameNumber = previousFrame.FrameNumber + 1 - inclusion.Position = i - keyInclusions = append(keyInclusions, inclusion) - keyPolynomials = append(keyPolynomials, e.stagedKeyPolynomials[commit]) - } - - proof, commitment, err := e.prover.ProveAggregate( - keyPolynomials, - keyCommitments, - ) - if err != nil { - e.logger.Error("could not produce proof", zap.Error(err)) - return nil, errors.Wrap(err, "prove") - } - if proof.IsIdentity() { - return nil, errors.Wrap(errors.New("invalid proof"), "prove") - } - - commitments = append(commitments, commitment.ToAffineCompressed()) - - keyAggregation := &protobufs.InclusionAggregateProof{ - Filter: e.filter, - FrameNumber: previousFrame.FrameNumber + 1, - InclusionCommitments: keyInclusions, - Proof: proof.ToAffineCompressed(), - } - - aggregations = append(aggregations, keyAggregation) - - e.stagedKeyCommits = make( - map[curves.PairingPoint]*protobufs.InclusionCommitment, - ) - e.stagedKeyPolynomials = make( - map[curves.PairingPoint][]curves.PairingScalar, - ) - } - e.stagedKeyCommitsMx.Unlock() - + if !e.frameProverTrie.Contains(e.provingKeyAddress) { e.stagedLobbyStateTransitionsMx.Lock() - executionOutput := &protobufs.IntrinsicExecutionOutput{} - app, err := application.MaterializeApplicationFromFrame(previousFrame) - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - - if e.stagedLobbyStateTransitions == nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - } - - app, err = app.ApplyTransition( - previousFrame.FrameNumber, - e.stagedLobbyStateTransitions, - ) - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - - lobbyState, err := app.MaterializeLobbyStateFromApplication() - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - - executionOutput.Address = application.CEREMONY_ADDRESS - executionOutput.Output, err = proto.Marshal(lobbyState) - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - - executionOutput.Proof, err = proto.Marshal(e.stagedLobbyStateTransitions) - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - - data, err := proto.Marshal(executionOutput) - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - - e.logger.Debug("encoded execution output") - - // Execution data in the ceremony is plaintext, we do not need to leverage - // full encoding for commit/proof reference. - digest := sha3.NewShake256() - _, err = digest.Write(data) - if err != nil { - e.logger.Error( - "error converting key bundle to polynomial", - zap.Error(err), - ) - return nil, errors.Wrap(err, "prove") - } - - expand := make([]byte, 1024) - _, err = digest.Read(expand) - if err != nil { - e.logger.Error( - "error converting key bundle to polynomial", - zap.Error(err), - ) - return nil, errors.Wrap(err, "prove") - } - poly, err := e.prover.BytesToPolynomial(expand) - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - - e.logger.Debug("proving execution output for inclusion") - polys, err := qcrypto.FFT( - poly, - *curves.BLS48581( - curves.BLS48581G1().NewGeneratorPoint(), - ), - 16, - false, - ) - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - - e.logger.Debug("converted execution output chunk to evaluation form") - - e.logger.Debug("creating kzg commitment") - commitment, err := e.prover.Commit(polys) - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - - e.logger.Debug("creating kzg proof") - proof, aggregate, err := e.prover.ProveAggregate( - [][]curves.PairingScalar{polys}, - []curves.PairingPoint{commitment}, - ) - if err != nil { - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} - e.stagedLobbyStateTransitionsMx.Unlock() - return nil, errors.Wrap(err, "prove") - } - if proof.IsIdentity() { - return nil, errors.Wrap(errors.New("invalid proof"), "prove") - } - - commitments = append(commitments, aggregate.ToAffineCompressed()) - - e.logger.Debug("finalizing execution proof") - e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} e.stagedLobbyStateTransitionsMx.Unlock() - execInclusion := &protobufs.InclusionCommitment{ - Filter: e.filter, - FrameNumber: previousFrame.FrameNumber + 1, - TypeUrl: protobufs.IntrinsicExecutionOutputType, - Data: data, - Commitment: commitment.ToAffineCompressed(), - } - - execAggregation := &protobufs.InclusionAggregateProof{ - Filter: e.filter, - FrameNumber: previousFrame.FrameNumber + 1, - InclusionCommitments: []*protobufs.InclusionCommitment{ - execInclusion, - }, - Proof: proof.ToAffineCompressed(), - } - - aggregations = append(aggregations, execAggregation) - - frame, err := protobufs.ProveDataClockFrame( - previousFrame, - commitments, - aggregations, - e.provingKey, - e.difficulty, - ) - if err != nil { - return nil, errors.Wrap(err, "prove") - } - e.state = consensus.EngineStatePublishing - e.logger.Debug( - "returning new proven frame", - zap.Int("proof_count", len(aggregations)), - zap.Int("commitment_count", len(commitments)), - ) - return frame, nil + e.state = consensus.EngineStateCollecting + return previousFrame, nil } - return nil, nil -} - -func (e *CeremonyDataClockConsensusEngine) setFrame( - frame *protobufs.ClockFrame, -) { - pubkey := []byte{} - discriminator := big.NewInt(0) - ed448PublicKey := frame.GetPublicKeySignatureEd448() - if frame.PublicKeySignature == nil && frame.FrameNumber != 0 { - e.logger.Error("could not set frame, signature invalid for non-zero frame") - return - } else if ed448PublicKey != nil { - pubkey = ed448PublicKey.PublicKey.KeyValue - } - - if len(pubkey) != 0 { - var err error - discriminator, err = poseidon.HashBytes(pubkey) - if err != nil { - e.logger.Error( - "could not set frame", - zap.Error(err), - ) - return - } - } - - selector := new(big.Int).SetBytes(frame.ParentSelector) - - 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 - } - - previousSelectorBytes := [516]byte{} - copy(previousSelectorBytes[:], frame.Output[:516]) - - parent, err := poseidon.HashBytes(previousSelectorBytes[:]) + e.stagedLobbyStateTransitionsMx.Lock() + executionOutput := &protobufs.IntrinsicExecutionOutput{} + app, err := application.MaterializeApplicationFromFrame(previousFrame) if err != nil { - panic(errors.Wrap(err, "set frame")) - } - e.logger.Debug("set frame", zap.Uint64("frame_number", frame.FrameNumber)) - e.currentDistance = distance - e.frame = frame - e.parentSelector = parent.Bytes() - go func() { - e.frameChan <- frame - }() -} - -func ( - e *CeremonyDataClockConsensusEngine, -) CreateGenesisFrame(testProverKeys [][]byte) *protobufs.ClockFrame { - e.logger.Info("creating genesis frame") - for _, l := range strings.Split(string(e.input), "\n") { - e.logger.Info(l) + e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} + e.stagedLobbyStateTransitionsMx.Unlock() + return nil, errors.Wrap(err, "prove") } - b := sha3.Sum256(e.input) - v := vdf.New(e.difficulty, b) - - v.Execute() - o := v.GetOutput() - inputMessage := o[:] - - e.logger.Info("encoding ceremony and phase one signatories") - transcript := &protobufs.CeremonyTranscript{} - for p, s := range qcrypto.CeremonyBLS48581G1 { - transcript.G1Powers = append( - transcript.G1Powers, - &protobufs.BLS48581G1PublicKey{ - KeyValue: s.ToAffineCompressed(), - }, - ) - e.logger.Info(fmt.Sprintf("encoded G1 power %d", p)) - } - for p, s := range qcrypto.CeremonyBLS48581G2 { - transcript.G2Powers = append( - transcript.G2Powers, - &protobufs.BLS48581G2PublicKey{ - KeyValue: s.ToAffineCompressed(), - }, - ) - e.logger.Info(fmt.Sprintf("encoded G2 power %d", p)) + if e.stagedLobbyStateTransitions == nil { + e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} } - transcript.RunningG1_256Witnesses = append( - transcript.RunningG1_256Witnesses, - &protobufs.BLS48581G1PublicKey{ - KeyValue: qcrypto.CeremonyRunningProducts[0].ToAffineCompressed(), - }, + e.logger.Info( + "proving new frame", + zap.Int("state_transitions", len(e.stagedLobbyStateTransitions.TypeUrls)), ) - transcript.RunningG2_256Powers = append( - transcript.RunningG2_256Powers, - &protobufs.BLS48581G2PublicKey{ - KeyValue: qcrypto.CeremonyBLS48581G2[len(qcrypto.CeremonyBLS48581G2)-1]. - ToAffineCompressed(), - }, + var validLobbyTransitions *protobufs.CeremonyLobbyStateTransition + var skippedTransition *protobufs.CeremonyLobbyStateTransition + app, validLobbyTransitions, skippedTransition, err = app.ApplyTransition( + previousFrame.FrameNumber, + e.stagedLobbyStateTransitions, + true, ) - - outputProof := &protobufs.CeremonyLobbyStateTransition{ - TypeUrls: []string{}, - TransitionInputs: [][]byte{}, - } - - proofBytes, err := proto.Marshal(outputProof) if err != nil { - panic(err) + e.stagedLobbyStateTransitions = &protobufs.CeremonyLobbyStateTransition{} + e.stagedLobbyStateTransitionsMx.Unlock() + return nil, errors.Wrap(err, "prove") } - e.logger.Info("encoded transcript") - e.logger.Info("encoding ceremony signatories into application state") + e.stagedLobbyStateTransitions = skippedTransition + defer e.stagedLobbyStateTransitionsMx.Unlock() - rewardTrie := &tries.RewardCritbitTrie{} - for _, s := range qcrypto.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") + lobbyState, err := app.MaterializeLobbyStateFromApplication() if err != nil { - panic(err) + return nil, errors.Wrap(err, "prove") } - 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) + executionOutput.Address = application.CEREMONY_ADDRESS + executionOutput.Output, err = proto.Marshal(lobbyState) if err != nil { - panic(err) + return nil, errors.Wrap(err, "prove") } - e.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() + executionOutput.Proof, err = proto.Marshal(validLobbyTransitions) 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: []byte(e.filter), - Output: outputBytes, - Proof: proofBytes, + return nil, errors.Wrap(err, "prove") } data, err := proto.Marshal(executionOutput) if err != nil { - panic(err) + return nil, errors.Wrap(err, "prove") } - e.logger.Info("encoded execution output") + e.logger.Debug("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) - } - - poly, err := e.prover.BytesToPolynomial(expand) - if err != nil { - panic(err) - } - - e.logger.Info("proving execution output for inclusion") - evalPoly, err := qcrypto.FFT( - poly, - *curves.BLS48581( - curves.BLS48581G1().NewGeneratorPoint(), - ), - 16, - false, + commitment, err := e.inclusionProver.Commit( + data, + protobufs.IntrinsicExecutionOutputType, ) if err != nil { - panic(err) + return nil, errors.Wrap(err, "prove") } - e.logger.Info( - "converted execution output chunk to evaluation form", - zap.Int("poly_size", len(evalPoly)), - ) - - e.logger.Info("creating kzg commitment") - commitment, err := e.prover.Commit(evalPoly) - if err != nil { - panic(err) - } - - e.logger.Info("creating kzg proof") - proof, aggregate, err := e.prover.ProveAggregate( - [][]curves.PairingScalar{evalPoly}, - []curves.PairingPoint{commitment}, + e.logger.Debug("creating kzg proof") + proof, err := e.inclusionProver.ProveAggregate( + []*qcrypto.InclusionCommitment{commitment}, ) if err != nil { - panic(err) + return nil, errors.Wrap(err, "prove") } - e.logger.Info("finalizing execution proof") + e.logger.Debug("finalizing execution proof") - inputMessage = append( - append([]byte{}, inputMessage...), - aggregate.ToAffineCompressed()..., - ) - - ceremonyExecutiveProof := &protobufs.InclusionAggregateProof{ - Filter: e.filter, - FrameNumber: 0, - InclusionCommitments: []*protobufs.InclusionCommitment{ + frame, err := e.frameProver.ProveDataClockFrame( + previousFrame, + [][]byte{proof.AggregateCommitment}, + []*protobufs.InclusionAggregateProof{ { Filter: e.filter, - FrameNumber: 0, - Position: 0, - TypeUrl: protobufs.IntrinsicExecutionOutputType, - Data: data, - Commitment: commitment.ToAffineCompressed(), + FrameNumber: previousFrame.FrameNumber + 1, + InclusionCommitments: []*protobufs.InclusionCommitment{ + { + Filter: e.filter, + FrameNumber: previousFrame.FrameNumber + 1, + TypeUrl: proof.InclusionCommitments[0].TypeUrl, + Commitment: proof.InclusionCommitments[0].Commitment, + Data: data, + Position: 0, + }, + }, + Proof: proof.Proof, }, }, - Proof: proof.ToAffineCompressed(), - } - - // 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: - e.logger.Info("encoding signatories to prover trie") - - if len(testProverKeys) != 0 { - e.logger.Warn( - "TEST PROVER ENTRIES BEING ADDED, YOUR NODE WILL BE KICKED IF IN" + - " PRODUCTION", - ) - for _, s := range testProverKeys { - addr, err := poseidon.HashBytes(s) - if err != nil { - panic(err) - } - - addrBytes := addr.Bytes() - addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...) - e.frameProverTrie.Add(addrBytes, 0) - } - } else { - for _, s := range qcrypto.CeremonySignatories { - pubkey := s.ToAffineCompressed() - e.logger.Info("0x" + hex.EncodeToString(pubkey)) - - addr, err := poseidon.HashBytes(pubkey) - if err != nil { - panic(err) - } - - addrBytes := addr.Bytes() - addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...) - e.frameProverTrie.Add(addrBytes, 0) - } - } - - e.logger.Info("proving genesis frame") - input := []byte{} - input = append(input, e.filter...) - input = binary.BigEndian.AppendUint64(input, 0) - input = binary.BigEndian.AppendUint64(input, 0) - input = binary.BigEndian.AppendUint32(input, e.difficulty) - 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: e.parentSelector, - AggregateProofs: []*protobufs.InclusionAggregateProof{ - ceremonyExecutiveProof, - }, - PublicKeySignature: nil, - } - - parent, _, selector, err := frame.GetParentSelectorAndDistance(nil) - if err != nil { - panic(err) - } - - txn, err := e.clockStore.NewTransaction() - if err != nil { - panic(err) - } - - if err := e.clockStore.PutCandidateDataClockFrame( - parent.FillBytes(make([]byte, 32)), - big.NewInt(0).FillBytes(make([]byte, 32)), - selector.FillBytes(make([]byte, 32)), - frame, - txn, - ); err != nil { - panic(err) - } - - if err := e.clockStore.PutDataClockFrame( - frame, - e.frameProverTrie, - txn, - false, - ); err != nil { - panic(err) - } - - if err := txn.Commit(); err != nil { - panic(err) - } - - e.setFrame(frame) - return frame -} - -func (e *CeremonyDataClockConsensusEngine) commitLongestPath( - latest *protobufs.ClockFrame, -) ( - *protobufs.ClockFrame, - error, -) { - current := latest - e.logger.Info( - "searching from committed frame", - zap.Uint64("frame_number", current.FrameNumber), + e.provingKey, + time.Now().UnixMilli(), + e.difficulty, ) - - runningFrames := [][]*protobufs.ClockFrame{{current}} - commitReady := false - currentDepth := 0 - - for { - nextRunningFrames := [][]*protobufs.ClockFrame{} - for _, s := range runningFrames { - e.logger.Info( - "ranging over candidates for frame", - zap.Uint64("frame_number", s[currentDepth].FrameNumber), - ) - selector, err := s[currentDepth].GetSelector() - if err != nil { - return nil, errors.Wrap(err, "commit longest path") - } - - iter, err := e.clockStore.RangeCandidateDataClockFrames( - e.filter, - selector.Bytes(), - s[currentDepth].FrameNumber+1, - ) - if err != nil { - return nil, errors.Wrap(err, "commit longest path") - } - - for iter.First(); iter.Valid(); iter.Next() { - value, err := iter.Value() - if err != nil { - return nil, errors.Wrap(err, "commit longest path") - } - - 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, - }, s[currentDepth].GetFrameNumber()) - if err != nil { - return nil, errors.Wrap(err, "commit longest path") - } - - proverSelector, err := masterFrame.GetSelector() - if err != nil { - return nil, errors.Wrap(err, "commit longest path") - } - - nearest := e.frameProverTrie.FindNearest( - proverSelector.FillBytes(make([]byte, 32)), - ) - addr, err := value.GetAddress() - - // If we got the outright nearest, then skip all this, we know this is - // the right frame for the selector. - if err != nil && bytes.Equal(nearest.Bits(), addr) { - nextRunningFrames = append( - nextRunningFrames, - append( - append([]*protobufs.ClockFrame{}, s...), - value, - ), - ) - break - } - - // Iterated values will always be in order of shortest distance, this - // will always keep closest selected, longest path - if current.FrameNumber < value.FrameNumber { - e.logger.Info( - "setting longest path cursor to frame", - zap.Uint64("frame_number", value.FrameNumber), - ) - current = value - } - - e.logger.Info( - "adding candidate", - zap.Uint64("frame_number", value.FrameNumber), - ) - - nextRunningFrames = append( - nextRunningFrames, - append( - append([]*protobufs.ClockFrame{}, s...), - value, - ), - ) - } - - iter.Close() - - if len(nextRunningFrames) == 1 && len(nextRunningFrames[0]) > 32 { - break - } - } - - if commitReady && len(nextRunningFrames) == 1 { - commitReady = false - e.logger.Info( - "consensus found, committing frames", - zap.Int("commit_depth", len(runningFrames[0])), - ) - - for _, s := range runningFrames[0][0:] { - if s.FrameNumber == 0 { - continue - } - s := s - txn, err := e.clockStore.NewTransaction() - if err != nil { - return nil, errors.Wrap(err, "commit longest path") - } - - e.logger.Info( - "committing candidate", - zap.Uint64("frame_number", s.FrameNumber), - zap.Binary( - "prover", - s.GetPublicKeySignatureEd448().PublicKey.KeyValue, - ), - ) - - addr, err := s.GetAddress() - if err != nil { - return nil, errors.Wrap(err, "commit longest path") - } - - e.frameProverTrie.Add(addr, s.FrameNumber) - if err := e.clockStore.PutDataClockFrame( - s, - e.frameProverTrie, - txn, - false, - ); err != nil { - e.logger.Error( - "could not commit candidate", - zap.Error(err), - zap.Uint64("frame_number", s.FrameNumber), - zap.Binary("output", s.Output), - ) - return nil, errors.Wrap(err, "commit longest path") - } - - e.logger.Debug( - "committing aggregate proofs", - zap.Int("proof_count", len(s.AggregateProofs)), - ) - - for _, p := range s.AggregateProofs { - p := p - e.logger.Debug( - "committing inclusions", - zap.Int("inclusions_count", len(p.InclusionCommitments)), - ) - - for _, c := range p.InclusionCommitments { - c := c - switch c.TypeUrl { - case protobufs.ProvingKeyAnnouncementType: - provingKey := &protobufs.ProvingKeyAnnouncement{} - if err := proto.Unmarshal(c.Data, provingKey); err != nil { - e.logger.Error( - "could not commit candidate", - zap.Error(err), - zap.Uint64("frame_number", s.FrameNumber), - zap.Binary("commitment", c.Commitment), - ) - return nil, errors.Wrap(err, "commit longest path") - } - - e.logger.Debug( - "committing proving key", - zap.Uint64("frame_number", s.FrameNumber), - zap.Binary("commitment", c.Commitment), - ) - - if err := e.keyStore.IncludeProvingKey(c, txn); err != nil { - e.logger.Error( - "could not commit candidate", - zap.Error(err), - zap.Uint64("frame_number", s.FrameNumber), - zap.Binary("output", s.Output), - ) - return nil, errors.Wrap(err, "commit longest path") - } - } - } - } - - if err := txn.Commit(); err != nil { - e.logger.Error( - "could not commit candidates", - zap.Error(err), - ) - return nil, errors.Wrap(err, "commit longest path") - } - } - - runningFrames = [][]*protobufs.ClockFrame{ - {nextRunningFrames[0][currentDepth]}, - } - currentDepth = 0 - } else { - e.logger.Info( - "not ready to commit", - zap.Int("forks", len(nextRunningFrames)), - zap.Int("current_depth", currentDepth), - ) - commitReady = len(nextRunningFrames) == 1 - runningFrames = nextRunningFrames - currentDepth++ - } - - if len(nextRunningFrames) == 0 { - e.logger.Info("deepest consensus reached") - break - } + if err != nil { + return nil, errors.Wrap(err, "prove") } - - if current.FrameNumber != latest.FrameNumber { - to := current.FrameNumber - if to-16 > to { // underflow - to = 1 - } else { - to = to - 16 - } - - if 1 < to { - err := e.clockStore.DeleteCandidateDataClockFrameRange(e.filter, 1, to) - if err != nil { - e.logger.Error("error while purging candidate frames", zap.Error(err)) - } - } - } - - return current, nil + e.logger.Info( + "returning new proven frame", + zap.Uint64("frame_number", frame.FrameNumber), + zap.Int("proof_count", len(frame.AggregateProofs)), + zap.Int("commitment_count", len(frame.Input[516:])/74), + ) + return frame, nil } func (e *CeremonyDataClockConsensusEngine) GetMostAheadPeer() ( @@ -911,7 +145,12 @@ func (e *CeremonyDataClockConsensusEngine) GetMostAheadPeer() ( error, ) { e.peerMapMx.Lock() - max := e.frame.FrameNumber + frame, err := e.dataTimeReel.Head() + if err != nil { + panic(err) + } + + max := frame.FrameNumber var peer []byte = nil for _, v := range e.peerMap { _, ok := e.uncooperativePeersMap[string(v.peerId)] @@ -1084,58 +323,40 @@ func (e *CeremonyDataClockConsensusEngine) sync( func (e *CeremonyDataClockConsensusEngine) collect( currentFramePublished *protobufs.ClockFrame, ) (*protobufs.ClockFrame, error) { - if e.state == consensus.EngineStateCollecting { - e.logger.Info("collecting vdf proofs") + e.logger.Info("collecting vdf proofs") - latest := currentFramePublished - if e.syncingStatus == SyncStatusFailed { - latest = e.previousHead - e.syncingStatus = SyncStatusNotSyncing - } - - // With large networks and slow syncing machines, this can lead to an - // infinite loop if the list is refreshing fast enough, so make the size - // of the map the maximum loop case: - e.peerMapMx.Lock() - size := len(e.peerMap) - e.peerMapMx.Unlock() - - for i := 0; i < size; i++ { - peerId, maxFrame, err := e.GetMostAheadPeer() - if err != nil { - e.logger.Warn("no peers available, skipping sync") - break - } else if peerId == nil { - e.logger.Info("currently up to date, skipping sync") - break - } else if maxFrame-2 > latest.FrameNumber { - latest, err = e.sync(latest, maxFrame, peerId) - if err == nil { - break - } - } - } - - e.logger.Info( - "returning leader frame", - zap.Uint64("frame_number", latest.FrameNumber), - ) - - e.logger.Info("selecting leader") - - latest, err := e.commitLongestPath(latest) - if err != nil { - e.logger.Error("could not collect longest path", zap.Error(err)) - latest, _, err = e.clockStore.GetDataClockFrame(e.filter, 0) - if err != nil { - panic(err) - } - } - - e.setFrame(latest) - e.state = consensus.EngineStateProving - return latest, nil + latest := currentFramePublished + if e.syncingStatus == SyncStatusFailed { + e.syncingStatus = SyncStatusNotSyncing } - return nil, nil + // With large networks and slow syncing machines, this can lead to an + // infinite loop if the list is refreshing fast enough, so make the size + // of the map the maximum loop case: + e.peerMapMx.Lock() + size := len(e.peerMap) + e.peerMapMx.Unlock() + + for i := 0; i < size; i++ { + peerId, maxFrame, err := e.GetMostAheadPeer() + if err != nil { + e.logger.Warn("no peers available, skipping sync") + break + } else if peerId == nil { + e.logger.Info("currently up to date, skipping sync") + break + } else if maxFrame-2 > latest.FrameNumber { + latest, err = e.sync(latest, maxFrame, peerId) + if err == nil { + break + } + } + } + + e.logger.Info( + "returning leader frame", + zap.Uint64("frame_number", latest.FrameNumber), + ) + + return latest, nil } diff --git a/node/consensus/ceremony/execution_registration.go b/node/consensus/ceremony/execution_registration.go index b781d1a..b4285ae 100644 --- a/node/consensus/ceremony/execution_registration.go +++ b/node/consensus/ceremony/execution_registration.go @@ -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", diff --git a/node/consensus/ceremony/handle_pending_commit_worker.go b/node/consensus/ceremony/handle_pending_commit_worker.go deleted file mode 100644 index f316c5c..0000000 --- a/node/consensus/ceremony/handle_pending_commit_worker.go +++ /dev/null @@ -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 -} diff --git a/node/consensus/ceremony/peer_messaging.go b/node/consensus/ceremony/peer_messaging.go index df30bf0..4cbb7b6 100644 --- a/node/consensus/ceremony/peer_messaging.go +++ b/node/consensus/ceremony/peer_messaging.go @@ -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 diff --git a/node/consensus/consensus_engine.go b/node/consensus/consensus_engine.go index 8aaf0cb..3379363 100644 --- a/node/consensus/consensus_engine.go +++ b/node/consensus/consensus_engine.go @@ -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} } diff --git a/node/consensus/master/broadcast_messaging.go b/node/consensus/master/broadcast_messaging.go index a956a3e..ce7f0b5 100644 --- a/node/consensus/master/broadcast_messaging.go +++ b/node/consensus/master/broadcast_messaging.go @@ -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 } diff --git a/node/consensus/master/consensus_frames.go b/node/consensus/master/consensus_frames.go index 04bd699..278047e 100644 --- a/node/consensus/master/consensus_frames.go +++ b/node/consensus/master/consensus_frames.go @@ -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 } diff --git a/node/consensus/master/execution_registration.go b/node/consensus/master/execution_registration.go index e5dde0c..83c8021 100644 --- a/node/consensus/master/execution_registration.go +++ b/node/consensus/master/execution_registration.go @@ -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", diff --git a/node/consensus/master/master_clock_consensus_engine.go b/node/consensus/master/master_clock_consensus_engine.go index e8f6c22..a9f8309 100644 --- a/node/consensus/master/master_clock_consensus_engine.go +++ b/node/consensus/master/master_clock_consensus_engine.go @@ -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 { diff --git a/node/consensus/master/peer_messaging.go b/node/consensus/master/peer_messaging.go index 9d64c6b..3799901 100644 --- a/node/consensus/master/peer_messaging.go +++ b/node/consensus/master/peer_messaging.go @@ -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( diff --git a/node/consensus/time/data_time_reel.go b/node/consensus/time/data_time_reel.go new file mode 100644 index 0000000..46fe6a9 --- /dev/null +++ b/node/consensus/time/data_time_reel.go @@ -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) diff --git a/node/consensus/time/data_time_reel_test.go b/node/consensus/time/data_time_reel_test.go new file mode 100644 index 0000000..453e0e1 --- /dev/null +++ b/node/consensus/time/data_time_reel_test.go @@ -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) + } + } +} diff --git a/node/consensus/time/master_time_reel.go b/node/consensus/time/master_time_reel.go new file mode 100644 index 0000000..8ba02ab --- /dev/null +++ b/node/consensus/time/master_time_reel.go @@ -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) diff --git a/node/consensus/time/master_time_reel_test.go b/node/consensus/time/master_time_reel_test.go new file mode 100644 index 0000000..142538f --- /dev/null +++ b/node/consensus/time/master_time_reel_test.go @@ -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)) + } +} diff --git a/node/consensus/time/time_reel.go b/node/consensus/time/time_reel.go new file mode 100644 index 0000000..e18557d --- /dev/null +++ b/node/consensus/time/time_reel.go @@ -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 +} diff --git a/node/crypto/doubleratchet.go b/node/crypto/channel/doubleratchet.go similarity index 99% rename from node/crypto/doubleratchet.go rename to node/crypto/channel/doubleratchet.go index 3e7d6a1..29da5c2 100644 --- a/node/crypto/doubleratchet.go +++ b/node/crypto/channel/doubleratchet.go @@ -1,4 +1,4 @@ -package crypto +package channel import ( "crypto/aes" diff --git a/node/crypto/doubleratchet_test.go b/node/crypto/channel/doubleratchet_test.go similarity index 92% rename from node/crypto/doubleratchet_test.go rename to node/crypto/channel/doubleratchet_test.go index 76ad924..0db250d 100644 --- a/node/crypto/doubleratchet_test.go +++ b/node/crypto/channel/doubleratchet_test.go @@ -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:], diff --git a/node/crypto/x3dh.go b/node/crypto/channel/x3dh.go similarity index 99% rename from node/crypto/x3dh.go rename to node/crypto/channel/x3dh.go index 983b0c5..9a99352 100644 --- a/node/crypto/x3dh.go +++ b/node/crypto/channel/x3dh.go @@ -1,4 +1,4 @@ -package crypto +package channel import ( "crypto/sha512" diff --git a/node/crypto/x3dh_test.go b/node/crypto/channel/x3dh_test.go similarity index 88% rename from node/crypto/x3dh_test.go rename to node/crypto/channel/x3dh_test.go index 99def19..05b853a 100644 --- a/node/crypto/x3dh_test.go +++ b/node/crypto/channel/x3dh_test.go @@ -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, diff --git a/node/crypto/frame_prover.go b/node/crypto/frame_prover.go new file mode 100644 index 0000000..8cf2ef2 --- /dev/null +++ b/node/crypto/frame_prover.go @@ -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 +} diff --git a/node/crypto/inclusion_prover.go b/node/crypto/inclusion_prover.go new file mode 100644 index 0000000..e49c518 --- /dev/null +++ b/node/crypto/inclusion_prover.go @@ -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 +} diff --git a/node/crypto/fft.go b/node/crypto/kzg/fft.go similarity index 99% rename from node/crypto/fft.go rename to node/crypto/kzg/fft.go index 23ad694..1aab893 100644 --- a/node/crypto/fft.go +++ b/node/crypto/kzg/fft.go @@ -1,4 +1,4 @@ -package crypto +package kzg import ( "math/big" diff --git a/node/crypto/kzg.go b/node/crypto/kzg/kzg.go similarity index 99% rename from node/crypto/kzg.go rename to node/crypto/kzg/kzg.go index 4daa3d0..9da890a 100644 --- a/node/crypto/kzg.go +++ b/node/crypto/kzg/kzg.go @@ -1,4 +1,4 @@ -package crypto +package kzg import ( "encoding/binary" diff --git a/node/crypto/kzg_test.go b/node/crypto/kzg/kzg_test.go similarity index 91% rename from node/crypto/kzg_test.go rename to node/crypto/kzg/kzg_test.go index 7f684af..12eefc2 100644 --- a/node/crypto/kzg_test.go +++ b/node/crypto/kzg/kzg_test.go @@ -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)) diff --git a/node/crypto/kzg_inclusion_prover.go b/node/crypto/kzg_inclusion_prover.go new file mode 100644 index 0000000..c940bbe --- /dev/null +++ b/node/crypto/kzg_inclusion_prover.go @@ -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) diff --git a/node/crypto/kzg_inclusion_prover_test.go b/node/crypto/kzg_inclusion_prover_test.go new file mode 100644 index 0000000..c869173 --- /dev/null +++ b/node/crypto/kzg_inclusion_prover_test.go @@ -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) +} diff --git a/node/crypto/matrix.go b/node/crypto/shuffle/matrix.go similarity index 81% rename from node/crypto/matrix.go rename to node/crypto/shuffle/matrix.go index 524351f..6a85a2d 100644 --- a/node/crypto/matrix.go +++ b/node/crypto/shuffle/matrix.go @@ -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 { diff --git a/node/crypto/matrix_test.go b/node/crypto/shuffle/matrix_test.go similarity index 78% rename from node/crypto/matrix_test.go rename to node/crypto/shuffle/matrix_test.go index aca0d53..a292a3b 100644 --- a/node/crypto/matrix_test.go +++ b/node/crypto/shuffle/matrix_test.go @@ -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) => = - , = - , = -i * e * f + f * + e * + - 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++ { diff --git a/node/crypto/wesolowski_frame_prover.go b/node/crypto/wesolowski_frame_prover.go new file mode 100644 index 0000000..53e035a --- /dev/null +++ b/node/crypto/wesolowski_frame_prover.go @@ -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 +} diff --git a/node/crypto/wesolowski_frame_prover_test.go b/node/crypto/wesolowski_frame_prover_test.go new file mode 100644 index 0000000..8d35e68 --- /dev/null +++ b/node/crypto/wesolowski_frame_prover_test.go @@ -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) +} diff --git a/node/execution/ceremony/application/ceremony_application.go b/node/execution/intrinsics/ceremony/application/ceremony_application.go similarity index 78% rename from node/execution/ceremony/application/ceremony_application.go rename to node/execution/intrinsics/ceremony/application/ceremony_application.go index b1c7614..38b0dcb 100644 --- a/node/execution/ceremony/application/ceremony_application.go +++ b/node/execution/intrinsics/ceremony/application/ceremony_application.go @@ -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< 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") } } diff --git a/node/execution/ceremony/application/ceremony_application_finalizing.go b/node/execution/intrinsics/ceremony/application/ceremony_application_finalizing.go similarity index 89% rename from node/execution/ceremony/application/ceremony_application_finalizing.go rename to node/execution/intrinsics/ceremony/application/ceremony_application_finalizing.go index b0f6a34..20eed6a 100644 --- a/node/execution/ceremony/application/ceremony_application_finalizing.go +++ b/node/execution/intrinsics/ceremony/application/ceremony_application_finalizing.go @@ -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 diff --git a/node/execution/ceremony/application/ceremony_application_in_progress.go b/node/execution/intrinsics/ceremony/application/ceremony_application_in_progress.go similarity index 100% rename from node/execution/ceremony/application/ceremony_application_in_progress.go rename to node/execution/intrinsics/ceremony/application/ceremony_application_in_progress.go diff --git a/node/execution/ceremony/application/ceremony_application_open.go b/node/execution/intrinsics/ceremony/application/ceremony_application_open.go similarity index 100% rename from node/execution/ceremony/application/ceremony_application_open.go rename to node/execution/intrinsics/ceremony/application/ceremony_application_open.go diff --git a/node/execution/ceremony/application/ceremony_application_test.go b/node/execution/intrinsics/ceremony/application/ceremony_application_test.go similarity index 93% rename from node/execution/ceremony/application/ceremony_application_test.go rename to node/execution/intrinsics/ceremony/application/ceremony_application_test.go index cfcec43..6ac3655 100644 --- a/node/execution/ceremony/application/ceremony_application_test.go +++ b/node/execution/intrinsics/ceremony/application/ceremony_application_test.go @@ -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) diff --git a/node/execution/ceremony/application/ceremony_application_validating.go b/node/execution/intrinsics/ceremony/application/ceremony_application_validating.go similarity index 100% rename from node/execution/ceremony/application/ceremony_application_validating.go rename to node/execution/intrinsics/ceremony/application/ceremony_application_validating.go diff --git a/node/execution/ceremony/application/ceremony_application_validating_test.go b/node/execution/intrinsics/ceremony/application/ceremony_application_validating_test.go similarity index 100% rename from node/execution/ceremony/application/ceremony_application_validating_test.go rename to node/execution/intrinsics/ceremony/application/ceremony_application_validating_test.go diff --git a/node/execution/ceremony/application/multiply.go b/node/execution/intrinsics/ceremony/application/multiply.go similarity index 100% rename from node/execution/ceremony/application/multiply.go rename to node/execution/intrinsics/ceremony/application/multiply.go diff --git a/node/execution/ceremony/application/oblivious_powers_of_tau.go b/node/execution/intrinsics/ceremony/application/oblivious_powers_of_tau.go similarity index 100% rename from node/execution/ceremony/application/oblivious_powers_of_tau.go rename to node/execution/intrinsics/ceremony/application/oblivious_powers_of_tau.go diff --git a/node/execution/ceremony/application/oblivious_powers_of_tau_test.go b/node/execution/intrinsics/ceremony/application/oblivious_powers_of_tau_test.go similarity index 97% rename from node/execution/ceremony/application/oblivious_powers_of_tau_test.go rename to node/execution/intrinsics/ceremony/application/oblivious_powers_of_tau_test.go index afbfdfc..ca5a9af 100644 --- a/node/execution/ceremony/application/oblivious_powers_of_tau_test.go +++ b/node/execution/intrinsics/ceremony/application/oblivious_powers_of_tau_test.go @@ -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:], diff --git a/node/execution/ceremony/ceremony_execution_engine.go b/node/execution/intrinsics/ceremony/ceremony_execution_engine.go similarity index 81% rename from node/execution/ceremony/ceremony_execution_engine.go rename to node/execution/intrinsics/ceremony/ceremony_execution_engine.go index edc9343..01f30fc 100644 --- a/node/execution/ceremony/ceremony_execution_engine.go +++ b/node/execution/intrinsics/ceremony/ceremony_execution_engine.go @@ -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") } diff --git a/node/go.mod b/node/go.mod index 3cd7cfc..6c1e63b 100644 --- a/node/go.mod +++ b/node/go.mod @@ -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 diff --git a/node/go.sum b/node/go.sum index 72df8bd..477ae9e 100644 --- a/node/go.sum +++ b/node/go.sum @@ -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= diff --git a/node/main.go b/node/main.go index 446efa2..fd20de0 100644 --- a/node/main.go +++ b/node/main.go @@ -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") } diff --git a/node/p2p/public_channel.go b/node/p2p/public_channel.go index d608aeb..86438e2 100644 --- a/node/p2p/public_channel.go +++ b/node/p2p/public_channel.go @@ -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 { diff --git a/node/protobufs/clock.go b/node/protobufs/clock.go index e1c5077..361b13a 100644 --- a/node/protobufs/clock.go +++ b/node/protobufs/clock.go @@ -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 -} diff --git a/node/rpc/rpc_server.go b/node/rpc/rpc_server.go index ec53c1e..9357e52 100644 --- a/node/rpc/rpc_server.go +++ b/node/rpc/rpc_server.go @@ -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" diff --git a/node/store/clock.go b/node/store/clock.go index d73974c..b43adae 100644 --- a/node/store/clock.go +++ b/node/store/clock.go @@ -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