Refactor quorum calculation and signature submission process (#3201)

pull/3202/head
Rongjian Lan 4 years ago committed by GitHub
parent c659001827
commit 68ab784d63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      consensus/construct_test.go
  2. 2
      consensus/double_sign.go
  3. 10
      consensus/fbft_log.go
  4. 33
      consensus/leader.go
  5. 13
      consensus/quorum/one-node-one-vote.go
  6. 141
      consensus/quorum/one-node-staked-vote.go
  7. 5
      consensus/quorum/one-node-staked-vote_test.go
  8. 33
      consensus/quorum/quorum.go
  9. 4
      consensus/threshold.go
  10. 4
      consensus/view_change.go
  11. 8
      multibls/multibls.go
  12. 4
      node/node_explorer.go
  13. 1
      shard/shard_state.go

@ -63,9 +63,13 @@ func TestConstructPreparedMessage(test *testing.T) {
consensus.blockHash = [32]byte{}
message := "test string"
leaderKey := shard.BLSPublicKey{}
leaderKey.FromLibBLSPublicKey(leaderPubKey)
validatorKey := shard.BLSPublicKey{}
validatorKey.FromLibBLSPublicKey(validatorPubKey)
consensus.Decider.SubmitVote(
quorum.Prepare,
leaderPubKey,
leaderKey,
leaderPriKey.Sign(message),
common.BytesToHash(consensus.blockHash[:]),
consensus.blockNum,
@ -73,7 +77,7 @@ func TestConstructPreparedMessage(test *testing.T) {
)
if _, err := consensus.Decider.SubmitVote(
quorum.Prepare,
validatorPubKey,
validatorKey,
validatorPriKey.Sign(message),
common.BytesToHash(consensus.blockHash[:]),
consensus.blockNum,

@ -13,7 +13,7 @@ import (
func (consensus *Consensus) checkDoubleSign(recvMsg *FBFTMessage) bool {
if consensus.couldThisBeADoubleSigner(recvMsg) {
if alreadyCastBallot := consensus.Decider.ReadBallot(
quorum.Commit, recvMsg.SenderPubkey,
quorum.Commit, recvMsg.SenderPubkeyBytes,
); alreadyCastBallot != nil {
firstPubKey := bls.PublicKey{}
alreadyCastBallot.SignerPubKey.ToLibBLSPublicKey(&firstPubKey)

@ -3,6 +3,8 @@ package consensus
import (
"fmt"
"github.com/harmony-one/harmony/shard"
mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls"
@ -27,6 +29,7 @@ type FBFTMessage struct {
BlockHash common.Hash
Block []byte
SenderPubkey *bls.PublicKey
SenderPubkeyBytes shard.BLSPublicKey
LeaderPubkey *bls.PublicKey
Payload []byte
ViewchangeSig *bls.Sign
@ -45,7 +48,7 @@ func (m *FBFTMessage) String() string {
m.ViewID,
m.BlockNum,
m.BlockHash.Hex(),
m.SenderPubkey.SerializeToHexStr(),
m.SenderPubkeyBytes.Hex(),
m.LeaderPubkey.SerializeToHexStr(),
)
}
@ -242,11 +245,14 @@ func ParseFBFTMessage(msg *msg_pb.Message) (*FBFTMessage, error) {
copy(pbftMsg.Payload[:], consensusMsg.Payload[:])
pbftMsg.Block = make([]byte, len(consensusMsg.Block))
copy(pbftMsg.Block[:], consensusMsg.Block[:])
pubKey, err := bls_cosi.BytesToBLSPublicKey(consensusMsg.SenderPubkey)
if err != nil {
return nil, err
}
pbftMsg.SenderPubkey = pubKey
copy(pbftMsg.SenderPubkeyBytes[:], consensusMsg.SenderPubkey[:])
return &pbftMsg, nil
}
@ -291,6 +297,7 @@ func ParseViewChangeMessage(msg *msg_pb.Message) (*FBFTMessage, error) {
return nil, err
}
pbftMsg.SenderPubkey = pubKey
copy(pbftMsg.SenderPubkeyBytes[:], vcMsg.SenderPubkey[:])
pbftMsg.LeaderPubkey = leaderKey
pbftMsg.ViewchangeSig = &vcSig
pbftMsg.ViewidSig = &vcSig1
@ -320,6 +327,7 @@ func (consensus *Consensus) ParseNewViewMessage(msg *msg_pb.Message) (*FBFTMessa
return nil, err
}
FBFTMsg.SenderPubkey = pubKey
copy(FBFTMsg.SenderPubkeyBytes[:], vcMsg.SenderPubkey[:])
if len(vcMsg.M3Aggsigs) > 0 {
m3Sig := bls.Sign{}

@ -64,9 +64,9 @@ func (consensus *Consensus) announce(block *types.Block) {
continue
}
if _, err := consensus.Decider.SubmitVote(
if _, err := consensus.Decider.AddNewVote(
quorum.Prepare,
key,
consensus.PubKey.PublicKeyBytes[i],
consensus.priKey.PrivateKey[i].SignHash(consensus.blockHash[:]),
block.Hash(),
block.NumberU64(),
@ -115,6 +115,7 @@ func (consensus *Consensus) onPrepare(msg *msg_pb.Message) {
return
}
// TODO(audit): make FBFT lookup using map instead of looping through all items.
if !consensus.FBFTLog.HasMatchingViewAnnounce(
consensus.blockNum, consensus.viewID, recvMsg.BlockHash,
) {
@ -123,29 +124,27 @@ func (consensus *Consensus) onPrepare(msg *msg_pb.Message) {
Uint64("MsgBlockNum", recvMsg.BlockNum).
Uint64("blockNum", consensus.blockNum).
Msg("[OnPrepare] No Matching Announce message")
//return
}
validatorPubKey := recvMsg.SenderPubkey
prepareSig := recvMsg.Payload
prepareBitmap := consensus.prepareBitmap
consensus.mutex.Lock()
defer consensus.mutex.Unlock()
logger := consensus.getLogger().With().
Str("validatorPubKey", validatorPubKey.SerializeToHexStr()).Logger()
// proceed only when the message is not received before
signed := consensus.Decider.ReadBallot(quorum.Prepare, validatorPubKey)
signed := consensus.Decider.ReadBallot(quorum.Prepare, recvMsg.SenderPubkeyBytes)
if signed != nil {
logger.Debug().
consensus.getLogger().Debug().
Str("validatorPubKey", recvMsg.SenderPubkeyBytes.Hex()).
Msg("[OnPrepare] Already Received prepare message from the validator")
return
}
if consensus.Decider.IsQuorumAchieved(quorum.Prepare) {
// already have enough signatures
logger.Debug().Msg("[OnPrepare] Received Additional Prepare Message")
consensus.getLogger().Debug().
Str("validatorPubKey", recvMsg.SenderPubkeyBytes.Hex()).
Msg("[OnPrepare] Received Additional Prepare Message")
return
}
@ -162,12 +161,12 @@ func (consensus *Consensus) onPrepare(msg *msg_pb.Message) {
return
}
logger = logger.With().
consensus.getLogger().Debug().
Int64("NumReceivedSoFar", consensus.Decider.SignersCount(quorum.Prepare)).
Int64("PublicKeys", consensus.Decider.ParticipantsCount()).Logger()
logger.Debug().Msg("[OnPrepare] Received New Prepare Signature")
if _, err := consensus.Decider.SubmitVote(
quorum.Prepare, validatorPubKey,
Int64("PublicKeys", consensus.Decider.ParticipantsCount()).
Msg("[OnPrepare] Received New Prepare Signature")
if _, err := consensus.Decider.AddNewVote(
quorum.Prepare, recvMsg.SenderPubkeyBytes,
&sign, recvMsg.BlockHash,
recvMsg.BlockNum, recvMsg.ViewID,
); err != nil {
@ -249,8 +248,8 @@ func (consensus *Consensus) onCommit(msg *msg_pb.Message) {
Logger()
logger.Debug().Msg("[OnCommit] Received new commit message")
if _, err := consensus.Decider.SubmitVote(
quorum.Commit, validatorPubKey,
if _, err := consensus.Decider.AddNewVote(
quorum.Commit, recvMsg.SenderPubkeyBytes,
&sign, recvMsg.BlockHash,
recvMsg.BlockNum, recvMsg.ViewID,
); err != nil {

@ -4,6 +4,10 @@ import (
"encoding/json"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus/votepower"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
@ -21,6 +25,15 @@ func (v *uniformVoteWeight) Policy() Policy {
return SuperMajorityVote
}
// AddNewVote ..
func (v *uniformVoteWeight) AddNewVote(
p Phase, pubKeyBytes shard.BLSPublicKey,
sig *bls.Sign, headerHash common.Hash,
height, viewID uint64) (*votepower.Ballot, error) {
return v.SubmitVote(p, pubKeyBytes, sig, headerHash, height, viewID)
}
// IsQuorumAchieved ..
func (v *uniformVoteWeight) IsQuorumAchieved(p Phase) bool {
r := v.SignersCount(p) >= v.TwoThirdsSignersCount()

@ -2,13 +2,17 @@ package quorum
import (
"encoding/json"
errors2 "errors"
"math/big"
"github.com/harmony-one/harmony/internal/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/pkg/errors"
"github.com/harmony-one/harmony/consensus/votepower"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
common2 "github.com/harmony-one/harmony/internal/common"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
)
@ -24,15 +28,16 @@ type TallyResult struct {
theirPercent numeric.Dec
}
type voteBox struct {
voters map[shard.BLSPublicKey]struct{}
currentTotal numeric.Dec
type tallyAndQuorum struct {
tally numeric.Dec
quorumAchieved bool
}
type box struct {
Prepare *voteBox
Commit *voteBox
ViewChange *voteBox
// VoteTally is the vote tally for each phase
type VoteTally struct {
Prepare *tallyAndQuorum
Commit *tallyAndQuorum
ViewChange *tallyAndQuorum
}
type stakedVoteWeight struct {
@ -40,7 +45,7 @@ type stakedVoteWeight struct {
DependencyInjectionWriter
DependencyInjectionReader
roster votepower.Roster
ballotBox box
voteTally VoteTally
}
// Policy ..
@ -48,25 +53,63 @@ func (v *stakedVoteWeight) Policy() Policy {
return SuperMajorityStake
}
// IsQuorumAchieved ..
func (v *stakedVoteWeight) IsQuorumAchieved(p Phase) bool {
t := v.QuorumThreshold()
currentTotalPower, err := v.computeCurrentTotalPower(p)
// AddNewVote ..
func (v *stakedVoteWeight) AddNewVote(
p Phase, pubKeyBytes shard.BLSPublicKey,
sig *bls.Sign, headerHash common.Hash,
height, viewID uint64) (*votepower.Ballot, error) {
ballet, err := v.SubmitVote(p, pubKeyBytes, sig, headerHash, height, viewID)
if err != nil {
utils.Logger().Error().
AnErr("bls error", err).
Msg("Failure in attempt bls-key reading")
return false
return ballet, err
}
// Accumulate total voting power
additionalVotePower := v.roster.Voters[pubKeyBytes].OverallPercent
tallyQuorum := func() *tallyAndQuorum {
switch p {
case Prepare:
return v.voteTally.Prepare
case Commit:
return v.voteTally.Commit
case ViewChange:
return v.voteTally.ViewChange
default:
// Should not happen
return nil
}
}()
tallyQuorum.tally = tallyQuorum.tally.Add(additionalVotePower)
t := v.QuorumThreshold()
tallyQuorum.quorumAchieved = tallyQuorum.tally.GT(t)
msg := "Attempt to reach quorum"
if tallyQuorum.quorumAchieved {
msg = "Quorum Achieved!"
}
utils.Logger().Info().
Str("policy", v.Policy().String()).
Str("phase", p.String()).
Str("threshold", t.String()).
Str("total-power-of-signers", currentTotalPower.String()).
Msg("Attempt to reach quorum")
return currentTotalPower.GT(t)
Str("total-power-of-signers", tallyQuorum.tally.String()).
Msg(msg)
return ballet, nil
}
// IsQuorumAchieved ..
func (v *stakedVoteWeight) IsQuorumAchieved(p Phase) bool {
switch p {
case Prepare:
return v.voteTally.Prepare.quorumAchieved
case Commit:
return v.voteTally.Commit.quorumAchieved
case ViewChange:
return v.voteTally.ViewChange.quorumAchieved
default:
// Should not happen
return false
}
}
// IsQuorumAchivedByMask ..
@ -78,39 +121,19 @@ func (v *stakedVoteWeight) IsQuorumAchievedByMask(mask *bls_cosi.Mask) bool {
}
return (*currentTotalPower).GT(threshold)
}
func (v *stakedVoteWeight) computeCurrentTotalPower(p Phase) (*numeric.Dec, error) {
ballot := func() *voteBox {
func (v *stakedVoteWeight) currentTotalPower(p Phase) (*numeric.Dec, error) {
switch p {
case Prepare:
return v.ballotBox.Prepare
return &v.voteTally.Prepare.tally, nil
case Commit:
return v.ballotBox.Commit
return &v.voteTally.Commit.tally, nil
case ViewChange:
return v.ballotBox.ViewChange
return &v.voteTally.ViewChange.tally, nil
default:
// Should not happen
return nil
}
}()
members := v.Participants()
membersKeys := v.ParticipantsKeyBytes()
if len(members) != len(membersKeys) {
return nil, errors2.New("Participant keys are not matching")
}
for i := range members {
w := membersKeys[i]
if _, didVote := ballot.voters[w]; !didVote &&
v.ReadBallot(p, members[i]) != nil {
ballot.currentTotal = ballot.currentTotal.Add(
v.roster.Voters[w].OverallPercent,
)
ballot.voters[w] = struct{}{}
}
return nil, errors.New("wrong phase is provided")
}
return &ballot.currentTotal, nil
}
// ComputeTotalPowerByMask computes the total power indicated by bitmap mask
@ -250,25 +273,21 @@ func (v *stakedVoteWeight) AmIMemberOfCommitee() bool {
return false
}
func newBox() *voteBox {
return &voteBox{map[shard.BLSPublicKey]struct{}{}, numeric.ZeroDec()}
}
func newBallotBox() box {
return box{
Prepare: newBox(),
Commit: newBox(),
ViewChange: newBox(),
func newVoteTally() VoteTally {
return VoteTally{
Prepare: &tallyAndQuorum{numeric.NewDec(0), false},
Commit: &tallyAndQuorum{numeric.NewDec(0), false},
ViewChange: &tallyAndQuorum{numeric.NewDec(0), false},
}
}
func (v *stakedVoteWeight) ResetPrepareAndCommitVotes() {
v.reset([]Phase{Prepare, Commit})
v.ballotBox.Prepare = newBox()
v.ballotBox.Commit = newBox()
v.voteTally.Prepare = &tallyAndQuorum{numeric.NewDec(0), false}
v.voteTally.Commit = &tallyAndQuorum{numeric.NewDec(0), false}
}
func (v *stakedVoteWeight) ResetViewChangeVotes() {
v.reset([]Phase{ViewChange})
v.ballotBox.ViewChange = newBox()
v.voteTally.ViewChange = &tallyAndQuorum{numeric.NewDec(0), false}
}

@ -105,11 +105,10 @@ func setupEdgeCase() (Decider, *TallyResult, shard.SlotList, secretKeyMap) {
}
func sign(d Decider, k secretKeyMap, p Phase) {
for _, v := range k {
pubKey := v.GetPublicKey()
for k, v := range k {
sig := v.Sign(msg)
// TODO Make upstream test provide meaningful test values
d.SubmitVote(p, pubKey, sig, common.Hash{}, 0, 0)
d.AddNewVote(p, k, sig, common.Hash{}, 0, 0)
}
}

@ -79,7 +79,7 @@ type ParticipantTracker interface {
type SignatoryTracker interface {
ParticipantTracker
SubmitVote(
p Phase, PubKey *bls.PublicKey,
p Phase, pubkey shard.BLSPublicKey,
sig *bls.Sign, headerHash common.Hash,
height, viewID uint64,
) (*votepower.Ballot, error)
@ -92,7 +92,7 @@ type SignatoryTracker interface {
type SignatureReader interface {
SignatoryTracker
ReadAllBallots(Phase) []*votepower.Ballot
ReadBallot(p Phase, PubKey *bls.PublicKey) *votepower.Ballot
ReadBallot(p Phase, pubkey shard.BLSPublicKey) *votepower.Ballot
TwoThirdsSignersCount() int64
// 96 bytes aggregated signature
AggregateVotes(p Phase) *bls.Sign
@ -115,6 +115,11 @@ type Decider interface {
DependencyInjectionWriter
SetVoters(subCommittee *shard.Committee, epoch *big.Int) (*TallyResult, error)
Policy() Policy
AddNewVote(
p Phase, pubkey shard.BLSPublicKey,
sig *bls.Sign, headerHash common.Hash,
height, viewID uint64,
) (*votepower.Ballot, error)
IsQuorumAchieved(Phase) bool
IsQuorumAchievedByMask(mask *bls_cosi.Mask) bool
QuorumThreshold() numeric.Dec
@ -237,15 +242,16 @@ func (s *cIdentities) SignersCount(p Phase) int64 {
}
func (s *cIdentities) SubmitVote(
p Phase, PubKey *bls.PublicKey,
p Phase, pubkey shard.BLSPublicKey,
sig *bls.Sign, headerHash common.Hash,
height, viewID uint64,
) (*votepower.Ballot, error) {
// Note safe to assume by this point because key has been
// checked earlier
key := *shard.FromLibBLSPublicKeyUnsafe(PubKey)
if ballet := s.ReadBallot(p, pubkey); ballet != nil {
return nil, errors.Errorf("vote is already submitted %x", pubkey)
}
ballot := &votepower.Ballot{
SignerPubKey: key,
SignerPubKey: pubkey,
BlockHeaderHash: headerHash,
Signature: common.Hex2Bytes(sig.SerializeToHexStr()),
Height: height,
@ -253,11 +259,11 @@ func (s *cIdentities) SubmitVote(
}
switch p {
case Prepare:
s.prepare.BallotBox[key] = ballot
s.prepare.BallotBox[pubkey] = ballot
case Commit:
s.commit.BallotBox[key] = ballot
s.commit.BallotBox[pubkey] = ballot
case ViewChange:
s.viewChange.BallotBox[key] = ballot
s.viewChange.BallotBox[pubkey] = ballot
default:
return nil, errors.Wrapf(errPhaseUnknown, "given: %s", p.String())
}
@ -281,9 +287,8 @@ func (s *cIdentities) TwoThirdsSignersCount() int64 {
return s.ParticipantsCount()*2/3 + 1
}
func (s *cIdentities) ReadBallot(p Phase, PubKey *bls.PublicKey) *votepower.Ballot {
func (s *cIdentities) ReadBallot(p Phase, pubkey shard.BLSPublicKey) *votepower.Ballot {
ballotBox := map[shard.BLSPublicKey]*votepower.Ballot{}
key := *shard.FromLibBLSPublicKeyUnsafe(PubKey)
switch p {
case Prepare:
@ -294,7 +299,7 @@ func (s *cIdentities) ReadBallot(p Phase, PubKey *bls.PublicKey) *votepower.Ball
ballotBox = s.viewChange.BallotBox
}
payload, ok := ballotBox[key]
payload, ok := ballotBox[pubkey]
if !ok {
return nil
}
@ -359,7 +364,7 @@ func NewDecider(p Policy, shardID uint32) Decider {
c.DependencyInjectionWriter,
c.DependencyInjectionWriter.(DependencyInjectionReader),
*votepower.NewRoster(shardID),
newBallotBox(),
newVoteTally(),
}
default:
// Should not be possible

@ -56,9 +56,9 @@ func (consensus *Consensus) didReachPrepareQuorum() error {
continue
}
if _, err := consensus.Decider.SubmitVote(
if _, err := consensus.Decider.AddNewVote(
quorum.Commit,
key,
consensus.PubKey.PublicKeyBytes[i],
consensus.priKey.PrivateKey[i].SignHash(commitPayload),
blockObj.Hash(),
blockObj.NumberU64(),

@ -345,6 +345,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
preparedMsg.Payload = make([]byte, len(recvMsg.Payload)-32)
copy(preparedMsg.Payload[:], recvMsg.Payload[32:])
preparedMsg.SenderPubkey = newLeaderKey
copy(preparedMsg.SenderPubkeyBytes[:], newLeaderKey.Serialize())
consensus.getLogger().Info().Msg("[onViewChange] New Leader Prepared Message Added")
consensus.FBFTLog.AddMessage(&preparedMsg)
@ -432,7 +433,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
priKey := consensus.priKey.PrivateKey[i]
if _, err := consensus.Decider.SubmitVote(
quorum.Commit,
key,
consensus.PubKey.PublicKeyBytes[i],
priKey.SignHash(commitPayload),
common.BytesToHash(consensus.blockHash[:]),
block.NumberU64(),
@ -597,6 +598,7 @@ func (consensus *Consensus) onNewView(msg *msg_pb.Message) {
preparedMsg.Payload = make([]byte, len(recvMsg.Payload)-32)
copy(preparedMsg.Payload[:], recvMsg.Payload[32:])
preparedMsg.SenderPubkey = senderKey
preparedMsg.SenderPubkeyBytes = recvMsg.SenderPubkeyBytes
consensus.FBFTLog.AddMessage(&preparedMsg)
if hasBlock {

@ -3,6 +3,8 @@ package multibls
import (
"strings"
"github.com/harmony-one/harmony/shard"
"github.com/harmony-one/bls/ffi/go/bls"
)
@ -14,6 +16,7 @@ type PrivateKey struct {
// PublicKey stores the bls public keys that belongs to the node
type PublicKey struct {
PublicKey []*bls.PublicKey
PublicKeyBytes []shard.BLSPublicKey
}
// SerializeToHexStr wrapper
@ -41,10 +44,13 @@ func (multiKey PublicKey) Contains(pubKey *bls.PublicKey) bool {
// GetPublicKey wrapper
func (multiKey PrivateKey) GetPublicKey() *PublicKey {
pubKeys := make([]*bls.PublicKey, len(multiKey.PrivateKey))
pubKeysBytes := make([]shard.BLSPublicKey, len(multiKey.PrivateKey))
for i, key := range multiKey.PrivateKey {
pubKeys[i] = key.GetPublicKey()
pubKeysBytes[i].FromLibBLSPublicKey(pubKeys[i])
}
return &PublicKey{PublicKey: pubKeys}
return &PublicKey{PublicKey: pubKeys, PublicKeyBytes: pubKeysBytes}
}
// GetPrivateKey creates a multibls PrivateKey using bls.SecretKey

@ -231,7 +231,7 @@ func (node *Node) GetTransactionsCount(address, txType string) (uint64, error) {
key := explorer.GetAddressKey(address)
bytes, err := explorer.GetStorageInstance(node.SelfPeer.IP, node.SelfPeer.Port).GetDB().Get([]byte(key), nil)
if err != nil {
utils.Logger().Error().Err(err).Msg("[Explorer] Cannot get storage db instance")
utils.Logger().Error().Err(err).Str("addr", address).Msg("[Explorer] Address not found")
return 0, nil
}
if err = rlp.DecodeBytes(bytes, &addressData); err != nil {
@ -254,7 +254,7 @@ func (node *Node) GetStakingTransactionsCount(address, txType string) (uint64, e
key := explorer.GetAddressKey(address)
bytes, err := explorer.GetStorageInstance(node.SelfPeer.IP, node.SelfPeer.Port).GetDB().Get([]byte(key), nil)
if err != nil {
utils.Logger().Error().Err(err).Msg("[Explorer] Cannot get storage db instance")
utils.Logger().Error().Err(err).Str("addr", address).Msg("[Explorer] Address not found")
return 0, nil
}
if err = rlp.DecodeBytes(bytes, &addressData); err != nil {

@ -38,6 +38,7 @@ type State struct {
}
// BLSPublicKey defines the bls public key
// TODO(audit): wrap c bls key object with the raw bytes
type BLSPublicKey [PublicKeySizeInBytes]byte
// BLSSignature defines the bls signature

Loading…
Cancel
Save