mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-11-10 18:25:17 +00:00
5a3af70dc8
* v1.4.13 * adjust blossomsub param
1500 lines
36 KiB
Go
1500 lines
36 KiB
Go
package ceremony
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/iden3/go-iden3-crypto/poseidon"
|
|
"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/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/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 {
|
|
logger *zap.Logger
|
|
clock *ceremony.CeremonyDataClockConsensusEngine
|
|
clockStore store.ClockStore
|
|
keyStore store.KeyStore
|
|
keyManager keys.KeyManager
|
|
engineConfig *config.EngineConfig
|
|
pubSub p2p.PubSub
|
|
peerIdHash []byte
|
|
provingKey crypto.Signer
|
|
proverPublicKey []byte
|
|
provingKeyAddress []byte
|
|
inclusionProver qcrypto.InclusionProver
|
|
participantMx sync.Mutex
|
|
peerChannels map[string]*p2p.PublicP2PChannel
|
|
activeSecrets []curves.Scalar
|
|
activeClockFrame *protobufs.ClockFrame
|
|
alreadyPublishedShare bool
|
|
alreadyPublishedTranscript bool
|
|
seenMessageMap map[string]bool
|
|
seenMessageMx sync.Mutex
|
|
intrinsicFilter []byte
|
|
frameProver qcrypto.FrameProver
|
|
}
|
|
|
|
const validCeremonySelector = "253f3a6383dcfe91cf49abd20204b3e6ef5afd4c70c1968bb1f0b827a72af53b"
|
|
|
|
func NewCeremonyExecutionEngine(
|
|
logger *zap.Logger,
|
|
engineConfig *config.EngineConfig,
|
|
keyManager keys.KeyManager,
|
|
pubSub p2p.PubSub,
|
|
frameProver qcrypto.FrameProver,
|
|
inclusionProver qcrypto.InclusionProver,
|
|
clockStore store.ClockStore,
|
|
masterTimeReel *time.MasterTimeReel,
|
|
peerInfoManager p2p.PeerInfoManager,
|
|
keyStore store.KeyStore,
|
|
) *CeremonyExecutionEngine {
|
|
if logger == nil {
|
|
panic(errors.New("logger is nil"))
|
|
}
|
|
|
|
seed, err := hex.DecodeString(engineConfig.GenesisSeed)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
intrinsicFilter := append(
|
|
p2p.GetBloomFilter(application.CEREMONY_ADDRESS, 256, 3),
|
|
p2p.GetBloomFilterIndices(application.CEREMONY_ADDRESS, 65536, 24)...,
|
|
)
|
|
|
|
frame, _, err := clockStore.GetDataClockFrame(intrinsicFilter, 0, false)
|
|
var origin []byte
|
|
var inclusionProof *qcrypto.InclusionAggregateProof
|
|
var proverKeys [][]byte
|
|
|
|
rebuildGenesisFrame := false
|
|
if frame != nil {
|
|
selector, err := frame.GetSelector()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
if selector.Text(16) != validCeremonySelector {
|
|
logger.Warn("corrupted genesis frame detected, rebuilding")
|
|
|
|
err = clockStore.ResetDataClockFrames(intrinsicFilter)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
rebuildGenesisFrame = true
|
|
}
|
|
}
|
|
|
|
if err != nil && errors.Is(err, store.ErrNotFound) || rebuildGenesisFrame {
|
|
origin, inclusionProof, proverKeys = CreateGenesisState(
|
|
logger,
|
|
engineConfig,
|
|
nil,
|
|
inclusionProver,
|
|
)
|
|
}
|
|
|
|
dataTimeReel := time.NewDataTimeReel(
|
|
intrinsicFilter,
|
|
logger,
|
|
clockStore,
|
|
engineConfig,
|
|
frameProver,
|
|
origin,
|
|
inclusionProof,
|
|
proverKeys,
|
|
)
|
|
|
|
clock := ceremony.NewCeremonyDataClockConsensusEngine(
|
|
engineConfig,
|
|
logger,
|
|
keyManager,
|
|
clockStore,
|
|
keyStore,
|
|
pubSub,
|
|
frameProver,
|
|
inclusionProver,
|
|
masterTimeReel,
|
|
dataTimeReel,
|
|
peerInfoManager,
|
|
intrinsicFilter,
|
|
seed,
|
|
)
|
|
|
|
e := &CeremonyExecutionEngine{
|
|
logger: logger,
|
|
clock: clock,
|
|
engineConfig: engineConfig,
|
|
keyManager: keyManager,
|
|
clockStore: clockStore,
|
|
keyStore: keyStore,
|
|
pubSub: pubSub,
|
|
inclusionProver: inclusionProver,
|
|
frameProver: frameProver,
|
|
participantMx: sync.Mutex{},
|
|
peerChannels: map[string]*p2p.PublicP2PChannel{},
|
|
alreadyPublishedShare: false,
|
|
seenMessageMx: sync.Mutex{},
|
|
seenMessageMap: map[string]bool{},
|
|
intrinsicFilter: intrinsicFilter,
|
|
}
|
|
|
|
peerId := e.pubSub.GetPeerID()
|
|
addr, err := poseidon.HashBytes(peerId)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
addrBytes := addr.Bytes()
|
|
addrBytes = append(make([]byte, 32-len(addrBytes)), addrBytes...)
|
|
e.peerIdHash = addrBytes
|
|
provingKey, _, publicKeyBytes, provingKeyAddress := e.clock.GetProvingKey(
|
|
engineConfig,
|
|
)
|
|
e.provingKey = provingKey
|
|
e.proverPublicKey = publicKeyBytes
|
|
e.provingKeyAddress = provingKeyAddress
|
|
|
|
return e
|
|
}
|
|
|
|
var _ execution.ExecutionEngine = (*CeremonyExecutionEngine)(nil)
|
|
|
|
// GetName implements ExecutionEngine
|
|
func (*CeremonyExecutionEngine) GetName() string {
|
|
return "ceremony"
|
|
}
|
|
|
|
// GetSupportedApplications implements ExecutionEngine
|
|
func (
|
|
*CeremonyExecutionEngine,
|
|
) GetSupportedApplications() []*protobufs.Application {
|
|
return []*protobufs.Application{
|
|
{
|
|
Address: application.CEREMONY_ADDRESS,
|
|
ExecutionContext: protobufs.ExecutionContext_EXECUTION_CONTEXT_INTRINSIC,
|
|
},
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
difficulty := engineConfig.Difficulty
|
|
if difficulty == 0 {
|
|
difficulty = 10000
|
|
}
|
|
|
|
b := sha3.Sum256(seed)
|
|
v := vdf.New(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)
|
|
}
|
|
|
|
intrinsicFilter := append(
|
|
p2p.GetBloomFilter(application.CEREMONY_ADDRESS, 256, 3),
|
|
p2p.GetBloomFilterIndices(application.CEREMONY_ADDRESS, 65536, 24)...,
|
|
)
|
|
|
|
// Compat: there was a bug that went unnoticed in prior versions,
|
|
// the raw filter was used instead of the application address, which didn't
|
|
// affect execution because we forcibly stashed it. Preserving this to ensure
|
|
// no rebuilding of frame history is required.
|
|
executionOutput := &protobufs.IntrinsicExecutionOutput{
|
|
Address: intrinsicFilter,
|
|
Output: outputBytes,
|
|
Proof: proofBytes,
|
|
}
|
|
|
|
data, err := proto.Marshal(executionOutput)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
logger.Info("proving execution output for inclusion")
|
|
commitment, err := inclusionProver.Commit(
|
|
data,
|
|
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",
|
|
kzg.CeremonyBLS48581G2[1].ToAffineCompressed(),
|
|
))
|
|
|
|
go func() {
|
|
err := <-e.clock.Start()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = <-e.clock.RegisterExecutor(e, 0)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
go e.RunWorker()
|
|
|
|
errChan <- nil
|
|
}()
|
|
|
|
return errChan
|
|
}
|
|
|
|
// Stop implements ExecutionEngine
|
|
func (e *CeremonyExecutionEngine) Stop(force bool) <-chan error {
|
|
errChan := make(chan error)
|
|
|
|
go func() {
|
|
errChan <- <-e.clock.Stop(force)
|
|
}()
|
|
|
|
return errChan
|
|
}
|
|
|
|
// ProcessMessage implements ExecutionEngine
|
|
func (e *CeremonyExecutionEngine) ProcessMessage(
|
|
address []byte,
|
|
message *protobufs.Message,
|
|
) ([]*protobufs.Message, error) {
|
|
if bytes.Equal(address, e.GetSupportedApplications()[0].Address) {
|
|
e.logger.Debug("processing execution message")
|
|
any := &anypb.Any{}
|
|
if err := proto.Unmarshal(message.Payload, any); err != nil {
|
|
return nil, errors.Wrap(err, "process message")
|
|
}
|
|
|
|
switch any.TypeUrl {
|
|
case protobufs.ClockFrameType:
|
|
frame := &protobufs.ClockFrame{}
|
|
if err := any.UnmarshalTo(frame); err != nil {
|
|
return nil, errors.Wrap(err, "process message")
|
|
}
|
|
|
|
if frame.FrameNumber < e.clock.GetFrame().FrameNumber {
|
|
return nil, nil
|
|
}
|
|
|
|
if err := e.frameProver.VerifyDataClockFrame(frame); err != nil {
|
|
return nil, errors.Wrap(err, "process message")
|
|
}
|
|
|
|
if err := e.VerifyExecution(frame); err != nil {
|
|
return nil, errors.Wrap(err, "process message")
|
|
}
|
|
case protobufs.CeremonyLobbyJoinType:
|
|
fallthrough
|
|
case protobufs.CeremonySeenProverAttestationType:
|
|
fallthrough
|
|
case protobufs.CeremonyDroppedProverAttestationType:
|
|
fallthrough
|
|
case protobufs.CeremonyTranscriptCommitType:
|
|
fallthrough
|
|
case protobufs.CeremonyTranscriptShareType:
|
|
fallthrough
|
|
case protobufs.CeremonyTranscriptType:
|
|
hash := sha3.Sum256(any.Value)
|
|
if any.TypeUrl == protobufs.CeremonyTranscriptType {
|
|
e.seenMessageMx.Lock()
|
|
ref := string(hash[:])
|
|
if _, ok := e.seenMessageMap[ref]; !ok {
|
|
e.seenMessageMap[ref] = true
|
|
} else {
|
|
return nil, errors.Wrap(
|
|
errors.New("message already received"),
|
|
"process message",
|
|
)
|
|
}
|
|
e.seenMessageMx.Unlock()
|
|
}
|
|
if e.clock.IsInProverTrie(e.proverPublicKey) {
|
|
proposedTransition := &protobufs.CeremonyLobbyStateTransition{
|
|
TypeUrls: []string{any.TypeUrl},
|
|
TransitionInputs: [][]byte{
|
|
any.Value,
|
|
},
|
|
}
|
|
|
|
any := &anypb.Any{}
|
|
if err := any.MarshalFrom(proposedTransition); err != nil {
|
|
return nil, errors.Wrap(err, "process message")
|
|
}
|
|
|
|
any.TypeUrl = strings.Replace(
|
|
any.TypeUrl,
|
|
"type.googleapis.com",
|
|
"types.quilibrium.com",
|
|
1,
|
|
)
|
|
|
|
payload, err := proto.Marshal(any)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "process message")
|
|
}
|
|
|
|
h, err := poseidon.HashBytes(payload)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "process message")
|
|
}
|
|
|
|
msg := &protobufs.Message{
|
|
Hash: h.Bytes(),
|
|
Address: e.provingKeyAddress,
|
|
Payload: payload,
|
|
}
|
|
return []*protobufs.Message{
|
|
msg,
|
|
}, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) RunWorker() {
|
|
frameChan := e.clock.GetFrameChannel()
|
|
for {
|
|
select {
|
|
case frame := <-frameChan:
|
|
e.activeClockFrame = frame
|
|
e.logger.Info(
|
|
"evaluating next frame",
|
|
zap.Uint64(
|
|
"frame_number",
|
|
frame.FrameNumber,
|
|
),
|
|
)
|
|
app, err := application.MaterializeApplicationFromFrame(frame)
|
|
if err != nil {
|
|
e.logger.Error(
|
|
"error while materializing application from frame",
|
|
zap.Error(err),
|
|
)
|
|
panic(err)
|
|
}
|
|
|
|
_, _, reward := app.RewardTrie.Get(e.provingKeyAddress)
|
|
_, _, retro := app.RewardTrie.Get(e.peerIdHash)
|
|
e.logger.Info(
|
|
"current application state",
|
|
zap.Uint64("my_balance", reward+retro),
|
|
zap.String("lobby_state", app.LobbyState.String()),
|
|
)
|
|
|
|
switch app.LobbyState {
|
|
case application.CEREMONY_APPLICATION_STATE_OPEN:
|
|
e.alreadyPublishedShare = false
|
|
e.alreadyPublishedTranscript = false
|
|
alreadyJoined := false
|
|
for _, join := range app.LobbyJoins {
|
|
if bytes.Equal(
|
|
join.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
e.proverPublicKey,
|
|
) {
|
|
alreadyJoined = true
|
|
break
|
|
}
|
|
}
|
|
|
|
e.logger.Info(
|
|
"lobby open for joins",
|
|
zap.Int("joined_participants", len(app.LobbyJoins)),
|
|
zap.Int(
|
|
"preferred_participants",
|
|
len(app.NextRoundPreferredParticipants),
|
|
),
|
|
zap.Bool("in_lobby", alreadyJoined),
|
|
zap.Uint64("state_count", app.StateCount),
|
|
)
|
|
|
|
if !alreadyJoined {
|
|
e.logger.Info(
|
|
"joining lobby",
|
|
zap.Binary("proving_key", e.proverPublicKey),
|
|
)
|
|
if err := e.announceJoin(frame); err != nil {
|
|
e.logger.Error(
|
|
"failed to announce join",
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
|
|
e.logger.Info("preparing contribution")
|
|
// Calculate this now after announcing, this gives 10 frames of buffer
|
|
e.ensureSecrets(app)
|
|
}
|
|
case application.CEREMONY_APPLICATION_STATE_IN_PROGRESS:
|
|
inRound := false
|
|
for _, p := range app.ActiveParticipants {
|
|
if bytes.Equal(
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
e.proverPublicKey,
|
|
) {
|
|
inRound = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(e.activeSecrets) == 0 && inRound {
|
|
// If we ended up in the scenario where we do not have any secrets
|
|
// available but we're in the round, we should politely leave.
|
|
e.publishDroppedParticipant(e.proverPublicKey)
|
|
continue
|
|
}
|
|
|
|
e.logger.Info(
|
|
"round in progress",
|
|
zap.Any("participants", app.ActiveParticipants),
|
|
zap.Any(
|
|
"current_seen_attestations",
|
|
len(app.LatestSeenProverAttestations),
|
|
),
|
|
zap.Any(
|
|
"current_dropped_attestations",
|
|
len(app.DroppedParticipantAttestations),
|
|
),
|
|
zap.Any(
|
|
"preferred_participants_for_next_round",
|
|
len(app.NextRoundPreferredParticipants),
|
|
),
|
|
zap.Bool("in_round", inRound),
|
|
zap.Uint64("current_sub_round", app.RoundCount),
|
|
zap.Uint64("stale_state_count", app.StateCount),
|
|
)
|
|
|
|
shouldConnect := false
|
|
position := 0
|
|
if len(e.peerChannels) == 0 && app.RoundCount == 1 &&
|
|
len(app.ActiveParticipants) > 1 {
|
|
for i, p := range app.ActiveParticipants {
|
|
if bytes.Equal(
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
e.proverPublicKey,
|
|
) {
|
|
shouldConnect = true
|
|
position = i
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if shouldConnect {
|
|
e.logger.Info(
|
|
"connecting to peers",
|
|
zap.Any("participants", app.ActiveParticipants),
|
|
)
|
|
err := e.connectToActivePeers(app, position)
|
|
if err != nil {
|
|
e.logger.Error("error while connecting to peers", zap.Error(err))
|
|
e.publishDroppedParticipant(e.proverPublicKey)
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(e.peerChannels) != 0 {
|
|
done := false
|
|
rounds := app.TranscriptRoundAdvanceCommits
|
|
if len(rounds) != 0 {
|
|
for _, c := range rounds[app.RoundCount-1].Commits {
|
|
if bytes.Equal(
|
|
c.ProverSignature.PublicKey.KeyValue,
|
|
e.proverPublicKey,
|
|
) {
|
|
done = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if !done {
|
|
e.logger.Info(
|
|
"participating in round",
|
|
zap.Any("participants", app.ActiveParticipants),
|
|
zap.Uint64("current_round", app.RoundCount),
|
|
)
|
|
err := e.participateRound(app)
|
|
if err != nil {
|
|
e.logger.Error("error while participating in round", zap.Error(err))
|
|
e.publishDroppedParticipant(e.proverPublicKey)
|
|
}
|
|
}
|
|
} else if len(app.ActiveParticipants) == 1 &&
|
|
bytes.Equal(
|
|
app.ActiveParticipants[0].PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
e.proverPublicKey,
|
|
) {
|
|
if err = e.commitRound(e.activeSecrets); err != nil {
|
|
e.logger.Error("error while participating in round", zap.Error(err))
|
|
}
|
|
}
|
|
case application.CEREMONY_APPLICATION_STATE_FINALIZING:
|
|
e.logger.Info(
|
|
"round contribution finalizing",
|
|
zap.Any("participants", len(app.ActiveParticipants)),
|
|
zap.Any(
|
|
"current_seen_attestations",
|
|
len(app.LatestSeenProverAttestations),
|
|
),
|
|
zap.Any(
|
|
"current_dropped_attestations",
|
|
len(app.DroppedParticipantAttestations),
|
|
),
|
|
zap.Any(
|
|
"preferred_participants_for_next_round",
|
|
len(app.NextRoundPreferredParticipants),
|
|
),
|
|
zap.Int("finalized_shares", len(app.TranscriptShares)),
|
|
)
|
|
|
|
for _, s := range app.TranscriptShares {
|
|
if bytes.Equal(
|
|
s.ProverSignature.PublicKey.KeyValue,
|
|
e.proverPublicKey,
|
|
) {
|
|
e.alreadyPublishedShare = true
|
|
}
|
|
}
|
|
|
|
shouldPublish := false
|
|
for _, p := range app.ActiveParticipants {
|
|
if bytes.Equal(
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
e.proverPublicKey,
|
|
) {
|
|
shouldPublish = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !e.alreadyPublishedShare && shouldPublish {
|
|
if len(e.activeSecrets) == 0 {
|
|
e.publishDroppedParticipant(e.proverPublicKey)
|
|
continue
|
|
}
|
|
err := e.publishTranscriptShare(app)
|
|
if err != nil {
|
|
e.logger.Error(
|
|
"error while publishing transcript share",
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
}
|
|
case application.CEREMONY_APPLICATION_STATE_VALIDATING:
|
|
e.logger.Info("round contribution validating")
|
|
e.alreadyPublishedShare = false
|
|
for _, c := range e.peerChannels {
|
|
c.Close()
|
|
}
|
|
|
|
e.peerChannels = map[string]*p2p.PublicP2PChannel{}
|
|
if app.UpdatedTranscript != nil && !e.alreadyPublishedTranscript {
|
|
if err := e.publishTranscript(app); err != nil {
|
|
e.logger.Error(
|
|
"error while publishing transcript",
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) publishMessage(
|
|
filter []byte,
|
|
message proto.Message,
|
|
) error {
|
|
any := &anypb.Any{}
|
|
if err := any.MarshalFrom(message); err != nil {
|
|
return errors.Wrap(err, "publish message")
|
|
}
|
|
|
|
any.TypeUrl = strings.Replace(
|
|
any.TypeUrl,
|
|
"type.googleapis.com",
|
|
"types.quilibrium.com",
|
|
1,
|
|
)
|
|
|
|
payload, err := proto.Marshal(any)
|
|
if err != nil {
|
|
return errors.Wrap(err, "publish message")
|
|
}
|
|
|
|
h, err := poseidon.HashBytes(payload)
|
|
if err != nil {
|
|
return errors.Wrap(err, "publish message")
|
|
}
|
|
|
|
msg := &protobufs.Message{
|
|
Hash: h.Bytes(),
|
|
Address: application.CEREMONY_ADDRESS,
|
|
Payload: payload,
|
|
}
|
|
data, err := proto.Marshal(msg)
|
|
if err != nil {
|
|
return errors.Wrap(err, "publish message")
|
|
}
|
|
return e.pubSub.PublishToBitmask(filter, data)
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) announceJoin(
|
|
frame *protobufs.ClockFrame,
|
|
) error {
|
|
idk, err := e.keyManager.GetAgreementKey("q-ratchet-idk")
|
|
if err != nil {
|
|
if errors.Is(err, keys.KeyNotFoundErr) {
|
|
idk, err = e.keyManager.CreateAgreementKey(
|
|
"q-ratchet-idk",
|
|
keys.KeyTypeX448,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "announce join")
|
|
}
|
|
} else {
|
|
return errors.Wrap(err, "announce join")
|
|
}
|
|
}
|
|
|
|
spk, err := e.keyManager.GetAgreementKey("q-ratchet-spk")
|
|
if err != nil {
|
|
if errors.Is(err, keys.KeyNotFoundErr) {
|
|
spk, err = e.keyManager.CreateAgreementKey(
|
|
"q-ratchet-spk",
|
|
keys.KeyTypeX448,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "announce join")
|
|
}
|
|
} else {
|
|
return errors.Wrap(err, "announce join")
|
|
}
|
|
}
|
|
|
|
g := curves.ED448().Point.Generator()
|
|
|
|
join := &protobufs.CeremonyLobbyJoin{
|
|
FrameNumber: frame.FrameNumber,
|
|
IdentityKey: &protobufs.X448PublicKey{
|
|
KeyValue: g.Mul(idk).ToAffineCompressed(),
|
|
},
|
|
SignedPreKey: &protobufs.X448PublicKey{
|
|
KeyValue: g.Mul(spk).ToAffineCompressed(),
|
|
},
|
|
PeerId: e.pubSub.GetPeerID(),
|
|
}
|
|
sig, err := join.SignWithProverKey(e.provingKey)
|
|
if err != nil {
|
|
return errors.Wrap(err, "announce join")
|
|
}
|
|
|
|
join.PublicKeySignatureEd448 = &protobufs.Ed448Signature{
|
|
Signature: sig,
|
|
PublicKey: &protobufs.Ed448PublicKey{
|
|
KeyValue: e.proverPublicKey,
|
|
},
|
|
}
|
|
|
|
return errors.Wrap(
|
|
e.publishMessage(
|
|
e.intrinsicFilter,
|
|
join,
|
|
),
|
|
"announce join",
|
|
)
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) connectToActivePeers(
|
|
app *application.CeremonyApplication,
|
|
position int,
|
|
) error {
|
|
idk, err := e.keyManager.GetAgreementKey("q-ratchet-idk")
|
|
if err != nil {
|
|
return errors.Wrap(err, "connect to active peers")
|
|
}
|
|
spk, err := e.keyManager.GetAgreementKey("q-ratchet-spk")
|
|
if err != nil {
|
|
return errors.Wrap(err, "connect to active peers")
|
|
}
|
|
|
|
for i, p := range app.LobbyJoins {
|
|
if !bytes.Equal(
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
e.proverPublicKey,
|
|
) {
|
|
receiverIdk, err := curves.ED448().Point.FromAffineCompressed(
|
|
p.IdentityKey.KeyValue,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "connect to active peers")
|
|
}
|
|
|
|
receiverSpk, err := curves.ED448().Point.FromAffineCompressed(
|
|
p.SignedPreKey.KeyValue,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "connect to active peers")
|
|
}
|
|
|
|
client, err := e.clock.GetPublicChannelForProvingKey(
|
|
i > position,
|
|
p.PeerId,
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
)
|
|
if err != nil {
|
|
e.logger.Error(
|
|
"peer does not support direct public channels",
|
|
zap.Binary(
|
|
"proving_key",
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
),
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
e.peerChannels[string(
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
)], err = p2p.NewPublicP2PChannel(
|
|
client,
|
|
e.proverPublicKey,
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
i > position,
|
|
idk,
|
|
spk,
|
|
receiverIdk,
|
|
receiverSpk,
|
|
curves.ED448(),
|
|
e.keyManager,
|
|
e.pubSub,
|
|
)
|
|
if err != nil {
|
|
e.logger.Error(
|
|
"could not establish p2p channel",
|
|
zap.Binary(
|
|
"proving_key",
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
),
|
|
zap.Error(err),
|
|
)
|
|
return errors.Wrap(err, "connect to active peers")
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) participateRound(
|
|
app *application.CeremonyApplication,
|
|
) error {
|
|
idk, err := e.keyManager.GetAgreementKey("q-ratchet-idk")
|
|
if err != nil {
|
|
return errors.Wrap(err, "participate round")
|
|
}
|
|
|
|
spk, err := e.keyManager.GetAgreementKey("q-ratchet-spk")
|
|
if err != nil {
|
|
return errors.Wrap(err, "participate round")
|
|
}
|
|
|
|
idkPoint := curves.ED448().Point.Generator().Mul(idk)
|
|
idks := []curves.Point{}
|
|
initiator := false
|
|
for _, p := range app.ActiveParticipants {
|
|
if !bytes.Equal(
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
e.proverPublicKey,
|
|
) {
|
|
ic, err := e.keyStore.GetLatestKeyBundle(
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "participate round")
|
|
}
|
|
|
|
var kba *protobufs.KeyBundleAnnouncement
|
|
switch ic.TypeUrl {
|
|
case protobufs.KeyBundleAnnouncementType:
|
|
kba = &protobufs.KeyBundleAnnouncement{}
|
|
if err := proto.Unmarshal(
|
|
ic.Data,
|
|
kba,
|
|
); err != nil {
|
|
return errors.Wrap(err, "participate round")
|
|
}
|
|
}
|
|
|
|
receiverIdk, err := curves.ED448().Point.FromAffineCompressed(
|
|
kba.IdentityKey.GetPublicKeySignatureEd448().PublicKey.KeyValue,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "participate round")
|
|
}
|
|
|
|
receiverSpk, err := curves.ED448().Point.FromAffineCompressed(
|
|
kba.SignedPreKey.GetPublicKeySignatureEd448().PublicKey.KeyValue,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "participate round")
|
|
}
|
|
|
|
if _, ok := e.peerChannels[string(
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
)]; !ok {
|
|
client, err := e.clock.GetPublicChannelForProvingKey(
|
|
initiator,
|
|
p.PeerId,
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
)
|
|
if err != nil {
|
|
e.logger.Error(
|
|
"peer does not support direct public channels",
|
|
zap.Binary(
|
|
"proving_key",
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
),
|
|
zap.Error(err),
|
|
)
|
|
}
|
|
e.peerChannels[string(
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
)], err = p2p.NewPublicP2PChannel(
|
|
client,
|
|
e.proverPublicKey,
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
initiator,
|
|
idk,
|
|
spk,
|
|
receiverIdk,
|
|
receiverSpk,
|
|
curves.ED448(),
|
|
e.keyManager,
|
|
e.pubSub,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "participate round")
|
|
}
|
|
}
|
|
|
|
idks = append(idks, receiverIdk)
|
|
} else {
|
|
initiator = true
|
|
idks = append(idks, idkPoint)
|
|
}
|
|
}
|
|
|
|
pubKeys := [][]byte{}
|
|
for _, p := range app.ActiveParticipants {
|
|
pubKeys = append(
|
|
pubKeys,
|
|
p.PublicKeySignatureEd448.PublicKey.KeyValue,
|
|
)
|
|
}
|
|
|
|
newSecrets, err := application.ProcessRound(
|
|
e.proverPublicKey,
|
|
idk,
|
|
int(app.RoundCount),
|
|
pubKeys,
|
|
idks,
|
|
e.activeSecrets,
|
|
curves.BLS48581G1(),
|
|
func(i int, receiver []byte, msg []byte) error {
|
|
return e.peerChannels[string(receiver)].Send(msg)
|
|
},
|
|
func(i int, sender []byte) ([]byte, error) {
|
|
msg, err := e.peerChannels[string(
|
|
sender,
|
|
)].Receive()
|
|
if err != nil {
|
|
e.publishDroppedParticipant(sender)
|
|
return nil, err
|
|
} else {
|
|
if i == 0 {
|
|
e.publishLastSeenParticipant(sender)
|
|
}
|
|
return msg, nil
|
|
}
|
|
},
|
|
app.LatestTranscript.G1Powers[1].KeyValue,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "participate round")
|
|
}
|
|
|
|
return errors.Wrap(e.commitRound(newSecrets), "participate round")
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) commitRound(secrets []curves.Scalar) error {
|
|
g2Pub := curves.BLS48581G2().Point.Generator().Mul(secrets[0])
|
|
|
|
sig, err := application.SignProverKeyForCommit(
|
|
e.proverPublicKey,
|
|
secrets[0],
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "commit round")
|
|
}
|
|
|
|
proverSig, err := e.provingKey.Sign(
|
|
rand.Reader,
|
|
g2Pub.ToAffineCompressed(),
|
|
crypto.Hash(0),
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "commit round")
|
|
}
|
|
|
|
advance := &protobufs.CeremonyTranscriptCommit{
|
|
ProverSignature: &protobufs.Ed448Signature{
|
|
Signature: proverSig,
|
|
PublicKey: &protobufs.Ed448PublicKey{
|
|
KeyValue: e.proverPublicKey,
|
|
},
|
|
},
|
|
ContributionSignature: &protobufs.BLS48581Signature{
|
|
Signature: sig,
|
|
PublicKey: &protobufs.BLS48581G2PublicKey{
|
|
KeyValue: g2Pub.ToAffineCompressed(),
|
|
},
|
|
},
|
|
}
|
|
|
|
if err := e.publishMessage(
|
|
e.intrinsicFilter,
|
|
advance,
|
|
); err != nil {
|
|
return errors.Wrap(err, "commit round")
|
|
}
|
|
|
|
e.activeSecrets = secrets
|
|
return nil
|
|
}
|
|
|
|
// Publishes a dropped participant attestation, logs any errors but does not
|
|
// forward them on.
|
|
func (e *CeremonyExecutionEngine) publishDroppedParticipant(
|
|
participant []byte,
|
|
) {
|
|
frameNumber := e.clock.GetFrame().FrameNumber
|
|
|
|
b := binary.BigEndian.AppendUint64([]byte("dropped"), frameNumber)
|
|
b = append(b, participant...)
|
|
sig, err := e.provingKey.Sign(rand.Reader, b, crypto.Hash(0))
|
|
if err != nil {
|
|
e.logger.Error(
|
|
"error while signing dropped participant attestation",
|
|
zap.Error(err),
|
|
)
|
|
return
|
|
}
|
|
|
|
dropped := &protobufs.CeremonyDroppedProverAttestation{
|
|
DroppedProverKey: &protobufs.Ed448PublicKey{
|
|
KeyValue: participant,
|
|
},
|
|
LastSeenFrame: frameNumber,
|
|
ProverSignature: &protobufs.Ed448Signature{
|
|
Signature: sig,
|
|
PublicKey: &protobufs.Ed448PublicKey{
|
|
KeyValue: e.proverPublicKey,
|
|
},
|
|
},
|
|
}
|
|
|
|
err = e.publishMessage(
|
|
e.intrinsicFilter,
|
|
dropped,
|
|
)
|
|
if err != nil {
|
|
e.logger.Error(
|
|
"error while publishing dropped participant attestation",
|
|
zap.Error(err),
|
|
)
|
|
return
|
|
}
|
|
}
|
|
|
|
// Publishes a last seen participant attestation, logs any errors but does not
|
|
// forward them on.
|
|
func (e *CeremonyExecutionEngine) publishLastSeenParticipant(
|
|
participant []byte,
|
|
) {
|
|
frameNumber := e.clock.GetFrame().FrameNumber
|
|
|
|
b := binary.BigEndian.AppendUint64([]byte("lastseen"), frameNumber)
|
|
b = append(b, participant...)
|
|
sig, err := e.provingKey.Sign(rand.Reader, b, crypto.Hash(0))
|
|
if err != nil {
|
|
e.logger.Error(
|
|
"error while signing last seen participant attestation",
|
|
zap.Error(err),
|
|
)
|
|
return
|
|
}
|
|
|
|
seen := &protobufs.CeremonySeenProverAttestation{
|
|
SeenProverKey: &protobufs.Ed448PublicKey{
|
|
KeyValue: participant,
|
|
},
|
|
LastSeenFrame: frameNumber,
|
|
ProverSignature: &protobufs.Ed448Signature{
|
|
Signature: sig,
|
|
PublicKey: &protobufs.Ed448PublicKey{
|
|
KeyValue: e.proverPublicKey,
|
|
},
|
|
},
|
|
}
|
|
err = e.publishMessage(
|
|
e.intrinsicFilter,
|
|
seen,
|
|
)
|
|
if err != nil {
|
|
e.logger.Error(
|
|
"error while publishing dropped participant attestation",
|
|
zap.Error(err),
|
|
)
|
|
return
|
|
}
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) publishTranscriptShare(
|
|
app *application.CeremonyApplication,
|
|
) error {
|
|
transcriptShare := &protobufs.CeremonyTranscriptShare{}
|
|
transcriptShare.AdditiveG1Powers = make(
|
|
[]*protobufs.BLS48581G1PublicKey,
|
|
len(e.activeSecrets),
|
|
)
|
|
transcriptShare.AdditiveG2Powers = make(
|
|
[]*protobufs.BLS48581G2PublicKey,
|
|
len(app.LatestTranscript.G2Powers)-1,
|
|
)
|
|
|
|
eg := errgroup.Group{}
|
|
eg.SetLimit(100)
|
|
e.logger.Info("creating transcript share")
|
|
for i, s := range e.activeSecrets {
|
|
i := i
|
|
s := s
|
|
eg.Go(func() error {
|
|
if i%100 == 0 {
|
|
e.logger.Info(
|
|
"writing transcript share chunk",
|
|
zap.Int("chunk_start", i),
|
|
)
|
|
}
|
|
|
|
basisG1, err := curves.BLS48581G1().Point.FromAffineCompressed(
|
|
app.LatestTranscript.G1Powers[i+1].KeyValue,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "publish transcript share")
|
|
}
|
|
|
|
transcriptShare.AdditiveG1Powers[i] = &protobufs.BLS48581G1PublicKey{
|
|
KeyValue: basisG1.Mul(s).ToAffineCompressed(),
|
|
}
|
|
|
|
if i+1 < len(app.LatestTranscript.G2Powers) {
|
|
basisG2, err := curves.BLS48581G2().Point.FromAffineCompressed(
|
|
app.LatestTranscript.G2Powers[i+1].KeyValue,
|
|
)
|
|
if err != nil {
|
|
return errors.Wrap(err, "publish transcript share")
|
|
}
|
|
|
|
transcriptShare.AdditiveG2Powers[i] = &protobufs.BLS48581G2PublicKey{
|
|
KeyValue: basisG2.Mul(s).ToAffineCompressed(),
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
if err := eg.Wait(); err != nil {
|
|
return err
|
|
}
|
|
|
|
e.logger.Info(
|
|
"done writing transcript chunks, adding witnesses and signing",
|
|
)
|
|
|
|
transcriptShare.AdditiveG1_256Witness = &protobufs.BLS48581G1PublicKey{
|
|
KeyValue: curves.BLS48581G1().Point.Generator().Mul(
|
|
e.activeSecrets[len(app.LatestTranscript.G2Powers)-2],
|
|
).ToAffineCompressed(),
|
|
}
|
|
|
|
transcriptShare.AdditiveG2_256Witness = &protobufs.BLS48581G2PublicKey{
|
|
KeyValue: curves.BLS48581G2().Point.Generator().Mul(
|
|
e.activeSecrets[len(app.LatestTranscript.G2Powers)-2],
|
|
).ToAffineCompressed(),
|
|
}
|
|
|
|
sig, err := transcriptShare.SignWithProverKey(e.provingKey)
|
|
if err != nil {
|
|
errors.Wrap(err, "publish transcript share")
|
|
}
|
|
|
|
transcriptShare.ProverSignature = &protobufs.Ed448Signature{
|
|
Signature: sig,
|
|
PublicKey: &protobufs.Ed448PublicKey{
|
|
KeyValue: e.proverPublicKey,
|
|
},
|
|
}
|
|
|
|
err = errors.Wrap(
|
|
e.publishMessage(
|
|
e.intrinsicFilter,
|
|
transcriptShare,
|
|
),
|
|
"publish transcript share",
|
|
)
|
|
if err != nil {
|
|
return err
|
|
} else {
|
|
e.alreadyPublishedShare = true
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) VerifyExecution(
|
|
frame *protobufs.ClockFrame,
|
|
) error {
|
|
if e.clock.GetFrame().FrameNumber != frame.FrameNumber-1 {
|
|
return nil
|
|
}
|
|
|
|
if len(frame.AggregateProofs) > 0 {
|
|
for _, proofs := range frame.AggregateProofs {
|
|
for _, inclusion := range proofs.InclusionCommitments {
|
|
if inclusion.TypeUrl == protobufs.IntrinsicExecutionOutputType {
|
|
transition, _, err := application.GetOutputsFromClockFrame(frame)
|
|
if err != nil {
|
|
return errors.Wrap(err, "verify execution")
|
|
}
|
|
|
|
parent, err := e.clockStore.GetStagedDataClockFrame(
|
|
append(
|
|
p2p.GetBloomFilter(application.CEREMONY_ADDRESS, 256, 3),
|
|
p2p.GetBloomFilterIndices(
|
|
application.CEREMONY_ADDRESS,
|
|
65536,
|
|
24,
|
|
)...,
|
|
),
|
|
frame.FrameNumber-1,
|
|
frame.ParentSelector,
|
|
false,
|
|
)
|
|
if err != nil && !errors.Is(err, store.ErrNotFound) {
|
|
return errors.Wrap(err, "verify execution")
|
|
}
|
|
|
|
if parent == nil {
|
|
return errors.Wrap(
|
|
errors.New("missing parent frame"),
|
|
"verify execution",
|
|
)
|
|
}
|
|
|
|
a, err := application.MaterializeApplicationFromFrame(parent)
|
|
if err != nil {
|
|
return errors.Wrap(err, "verify execution")
|
|
}
|
|
|
|
a, _, _, err = a.ApplyTransition(frame.FrameNumber, transition, false)
|
|
if err != nil {
|
|
return errors.Wrap(err, "verify execution")
|
|
}
|
|
|
|
a2, err := application.MaterializeApplicationFromFrame(frame)
|
|
if err != nil {
|
|
return errors.Wrap(err, "verify execution")
|
|
}
|
|
|
|
if !a.Equals(a2) {
|
|
return errors.Wrap(
|
|
application.ErrInvalidStateTransition,
|
|
"verify execution",
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) publishTranscript(
|
|
app *application.CeremonyApplication,
|
|
) error {
|
|
e.logger.Info("publishing updated transcript")
|
|
e.alreadyPublishedTranscript = true
|
|
err := errors.Wrap(
|
|
e.publishMessage(
|
|
e.intrinsicFilter,
|
|
app.UpdatedTranscript,
|
|
),
|
|
"publish transcript share",
|
|
)
|
|
if err != nil {
|
|
e.alreadyPublishedTranscript = false
|
|
return err
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) ensureSecrets(
|
|
app *application.CeremonyApplication,
|
|
) {
|
|
if len(e.activeSecrets) == 0 {
|
|
e.activeSecrets = []curves.Scalar{}
|
|
t := curves.BLS48581G1().Scalar.Random(rand.Reader)
|
|
x := t.Clone()
|
|
|
|
for i := 0; i < len(app.LatestTranscript.G1Powers)-1; i++ {
|
|
if i%1000 == 0 {
|
|
e.logger.Info(
|
|
"calculating secrets for contribution",
|
|
zap.Int("secrets_calculated", i),
|
|
zap.Int("total_secrets", len(app.LatestTranscript.G1Powers)-1),
|
|
)
|
|
}
|
|
e.activeSecrets = append(e.activeSecrets, x)
|
|
x = x.Mul(t)
|
|
}
|
|
|
|
e.logger.Info(
|
|
"done preparing contribution",
|
|
zap.Int("secrets_calculated", len(e.activeSecrets)),
|
|
)
|
|
}
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) GetPeerInfo() *protobufs.PeerInfoResponse {
|
|
return e.clock.GetPeerInfo()
|
|
}
|
|
|
|
func (e *CeremonyExecutionEngine) GetFrame() *protobufs.ClockFrame {
|
|
return e.clock.GetFrame()
|
|
}
|