package app import ( "bytes" "encoding/binary" "math/big" "slices" "github.com/iden3/go-iden3-crypto/poseidon" "github.com/pkg/errors" "source.quilibrium.com/quilibrium/monorepo/consensus/models" tconsensus "source.quilibrium.com/quilibrium/monorepo/types/consensus" ) type ConsensusWeightedIdentity struct { prover *tconsensus.ProverInfo } // Identity implements models.WeightedIdentity. func (c *ConsensusWeightedIdentity) Identity() models.Identity { return models.Identity(c.prover.Address) } // PublicKey implements models.WeightedIdentity. func (c *ConsensusWeightedIdentity) PublicKey() []byte { return c.prover.PublicKey } // Weight implements models.WeightedIdentity. func (c *ConsensusWeightedIdentity) Weight() uint64 { return c.prover.Seniority } // IdentitiesByRank implements consensus.DynamicCommittee. func (e *AppConsensusEngine) IdentitiesByRank( rank uint64, ) ([]models.WeightedIdentity, error) { proverInfo, err := e.proverRegistry.GetActiveProvers(e.appAddress) if err != nil { return nil, errors.Wrap(err, "identities by rank") } return internalProversToWeightedIdentity(proverInfo), nil } // IdentitiesByState implements consensus.DynamicCommittee. func (e *AppConsensusEngine) IdentitiesByState( stateID models.Identity, ) ([]models.WeightedIdentity, error) { proverInfo, err := e.proverRegistry.GetActiveProvers(e.appAddress) if err != nil { return nil, errors.Wrap(err, "identities by state") } return internalProversToWeightedIdentity(proverInfo), nil } // IdentityByRank implements consensus.DynamicCommittee. func (e *AppConsensusEngine) IdentityByRank( rank uint64, participantID models.Identity, ) (models.WeightedIdentity, error) { proverInfo, err := e.proverRegistry.GetActiveProvers(e.appAddress) if err != nil { return nil, errors.Wrap(err, "identity by rank") } var found *tconsensus.ProverInfo for _, p := range proverInfo { if bytes.Equal(p.Address, []byte(participantID)) { found = p break } } if found == nil { return nil, errors.Wrap(errors.New("prover not found"), "identity by rank") } return internalProverToWeightedIdentity(found), nil } // IdentityByState implements consensus.DynamicCommittee. func (e *AppConsensusEngine) IdentityByState( stateID models.Identity, participantID models.Identity, ) (models.WeightedIdentity, error) { proverInfo, err := e.proverRegistry.GetActiveProvers(e.appAddress) if err != nil { return nil, errors.Wrap(err, "identity by state") } var found *tconsensus.ProverInfo for _, p := range proverInfo { if bytes.Equal(p.Address, []byte(participantID)) { found = p break } } if found == nil { return nil, errors.Wrap(errors.New("prover not found"), "identity by state") } return internalProverToWeightedIdentity(found), nil } // LeaderForRank implements consensus.DynamicCommittee. func (e *AppConsensusEngine) LeaderForRank(rank uint64) ( models.Identity, error, ) { // TODO(2.2): revisit this inputBI, err := poseidon.HashBytes(slices.Concat( binary.BigEndian.AppendUint64(slices.Clone(e.appAddress), rank), )) if err != nil { return "", errors.Wrap(err, "leader for rank") } proverSet, err := e.proverRegistry.GetActiveProvers(e.appAddress) if err != nil { return "", errors.Wrap(err, "leader for rank") } if e.config.P2P.Network == 0 && len(proverSet) < 3 { return models.Identity(make([]byte, 32)), nil } // Handle condition where prover cannot be yet known due to lack of sync: if len(proverSet) == 0 { return models.Identity(make([]byte, 32)), nil } inputBI.Mod(inputBI, big.NewInt(int64(len(proverSet)))) index := inputBI.Int64() return models.Identity(proverSet[int(index)].Address), nil } // QuorumThresholdForRank implements consensus.DynamicCommittee. func (e *AppConsensusEngine) QuorumThresholdForRank( rank uint64, ) (uint64, error) { proverInfo, err := e.proverRegistry.GetActiveProvers(e.appAddress) if err != nil { return 0, errors.Wrap(err, "quorum threshold for rank") } total := uint64(0) for _, p := range proverInfo { total += p.Seniority } return (total * 2) / 3, nil } // Self implements consensus.DynamicCommittee. func (e *AppConsensusEngine) Self() models.Identity { return e.getPeerID().Identity() } // TimeoutThresholdForRank implements consensus.DynamicCommittee. func (e *AppConsensusEngine) TimeoutThresholdForRank( rank uint64, ) (uint64, error) { proverInfo, err := e.proverRegistry.GetActiveProvers(e.appAddress) if err != nil { return 0, errors.Wrap(err, "quorum threshold for rank") } total := uint64(0) for _, p := range proverInfo { total += p.Seniority } return (total * 2) / 3, nil } func internalProversToWeightedIdentity( provers []*tconsensus.ProverInfo, ) []models.WeightedIdentity { wis := []models.WeightedIdentity{} for _, p := range provers { wis = append(wis, internalProverToWeightedIdentity(p)) } return wis } func internalProverToWeightedIdentity( prover *tconsensus.ProverInfo, ) models.WeightedIdentity { return &ConsensusWeightedIdentity{prover} }