The core protocol of WoopChain
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
woop/drand/drand.go

217 lines
6.5 KiB

package drand
import (
"errors"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/harmony/crypto/hash"
protobuf "github.com/golang/protobuf/proto"
"github.com/harmony-one/bls/ffi/go/bls"
msg_pb "github.com/harmony-one/harmony/api/proto/message"
"github.com/harmony-one/harmony/core/types"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/crypto/vrf"
"github.com/harmony-one/harmony/crypto/vrf/p256"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p"
)
// DRand is the main struct which contains state for the distributed randomness protocol.
type DRand struct {
vrfs *map[common.Address][]byte // Key is the address hex
bitmap *bls_cosi.Mask
pRand *[32]byte
rand *[32]byte
ConfirmedBlockChannel chan *types.Block // Channel to receive confirmed blocks
PRndChannel chan []byte // Channel to send pRnd (preimage of randomness resulting from combined vrf randomnesses) to consensus. The first 32 bytes are randomness, the rest is for bitmap.
RndChannel chan [64]byte // Channel for DRG protocol to send the final randomness to consensus. The first 32 bytes are the randomness and the last 32 bytes are the hash of the block where the corresponding pRnd was generated
// global consensus mutex
mutex sync.Mutex
// Leader's address
leader p2p.Peer
// Public keys of the committee including leader and validators
PublicKeys []*bls.PublicKey
// The addresses of my committee
CommitteeAddresses map[common.Address]bool
pubKeyLock sync.Mutex
// private/public keys of current node
priKey *bls.SecretKey
pubKey *bls.PublicKey
// VRF private and public key
// TODO: directly use signature signing key (BLS) for vrf
vrfPriKey *vrf.PrivateKey
vrfPubKey *vrf.PublicKey
// Whether I am leader. False means I am validator
IsLeader bool
// Leader or validator address
SelfAddress common.Address
// The p2p host used to send/receive p2p messages
host p2p.Host
// Shard Id which this node belongs to
ShardID uint32
// Blockhash - 32 byte
blockHash [32]byte
}
// New creates a new dRand object
func New(host p2p.Host, ShardID uint32, peers []p2p.Peer, leader p2p.Peer, confirmedBlockChannel chan *types.Block, blsPriKey *bls.SecretKey) *DRand {
dRand := DRand{}
dRand.host = host
if confirmedBlockChannel != nil {
dRand.ConfirmedBlockChannel = confirmedBlockChannel
}
dRand.PRndChannel = make(chan []byte)
dRand.RndChannel = make(chan [64]byte)
selfPeer := host.GetSelfPeer()
if leader.Port == selfPeer.Port && leader.IP == selfPeer.IP {
dRand.IsLeader = true
} else {
dRand.IsLeader = false
}
dRand.leader = leader
dRand.CommitteeAddresses = map[common.Address]bool{}
for _, peer := range peers {
dRand.CommitteeAddresses[utils.GetBlsAddress(peer.ConsensusPubKey)] = true
}
dRand.vrfs = &map[common.Address][]byte{}
// Initialize cosign bitmap
allPublicKeys := make([]*bls.PublicKey, 0)
for _, validatorPeer := range peers {
allPublicKeys = append(allPublicKeys, validatorPeer.ConsensusPubKey)
}
allPublicKeys = append(allPublicKeys, leader.ConsensusPubKey)
dRand.PublicKeys = allPublicKeys
bitmap, _ := bls_cosi.NewMask(dRand.PublicKeys, dRand.leader.ConsensusPubKey)
dRand.bitmap = bitmap
dRand.pRand = nil
dRand.rand = nil
// For now use socket address as ID
dRand.SelfAddress = utils.GetBlsAddress(selfPeer.ConsensusPubKey)
// Set private key for myself so that I can sign messages.
if blsPriKey != nil {
dRand.priKey = blsPriKey
dRand.pubKey = blsPriKey.GetPublicKey()
}
// VRF keys
priKey, pubKey := p256.GenerateKey()
dRand.vrfPriKey = &priKey
dRand.vrfPubKey = &pubKey
dRand.ShardID = ShardID
return &dRand
}
// Sign on the drand message signature field.
func (dRand *DRand) signDRandMessage(message *msg_pb.Message) error {
message.Signature = nil
marshaledMessage, err := protobuf.Marshal(message)
if err != nil {
return err
}
// 64 byte of signature on previous data
hash := hash.Keccak256(marshaledMessage)
signature := dRand.priKey.SignHash(hash[:])
message.Signature = signature.Serialize()
return nil
}
// Signs the drand message and returns the marshaled message.
func (dRand *DRand) signAndMarshalDRandMessage(message *msg_pb.Message) ([]byte, error) {
err := dRand.signDRandMessage(message)
if err != nil {
return []byte{}, err
}
marshaledMessage, err := protobuf.Marshal(message)
if err != nil {
return []byte{}, err
}
return marshaledMessage, nil
}
func (dRand *DRand) vrf(blockHash [32]byte) (rand [32]byte, proof []byte) {
rand, proof = (*dRand.vrfPriKey).Evaluate(blockHash[:])
return
}
// Verify the signature of the message are valid from the signer's public key.
func verifyMessageSig(signerPubKey *bls.PublicKey, message *msg_pb.Message) error {
signature := message.Signature
message.Signature = nil
messageBytes, err := protobuf.Marshal(message)
if err != nil {
return err
}
msgSig := bls.Sign{}
err = msgSig.Deserialize(signature)
if err != nil {
return err
}
msgHash := hash.Keccak256(messageBytes)
if !msgSig.VerifyHash(signerPubKey, msgHash[:]) {
return errors.New("failed to verify the signature")
}
return nil
}
// IsValidatorInCommittee returns whether the given validator BLS address is part of my committee
func (dRand *DRand) IsValidatorInCommittee(validatorBlsAddress common.Address) bool {
_, ok := dRand.CommitteeAddresses[validatorBlsAddress]
return ok
}
// ResetState resets the state of the randomness protocol
func (dRand *DRand) ResetState() {
dRand.vrfs = &map[common.Address][]byte{}
bitmap, _ := bls_cosi.NewMask(dRand.PublicKeys, dRand.leader.ConsensusPubKey)
dRand.bitmap = bitmap
dRand.pRand = nil
dRand.rand = nil
}
// SetLeaderPubKey deserialize the public key of drand leader
func (dRand *DRand) SetLeaderPubKey(k []byte) error {
dRand.leader.ConsensusPubKey = &bls.PublicKey{}
return dRand.leader.ConsensusPubKey.Deserialize(k)
}
// UpdatePublicKeys updates the PublicKeys variable, protected by a mutex
func (dRand *DRand) UpdatePublicKeys(pubKeys []*bls.PublicKey) int {
dRand.pubKeyLock.Lock()
dRand.PublicKeys = append(pubKeys[:0:0], pubKeys...)
dRand.CommitteeAddresses = map[common.Address]bool{}
for _, pubKey := range dRand.PublicKeys {
dRand.CommitteeAddresses[utils.GetBlsAddress(pubKey)] = true
}
// TODO: use pubkey to identify leader rather than p2p.Peer.
dRand.leader = p2p.Peer{ConsensusPubKey: pubKeys[0]}
dRand.pubKeyLock.Unlock()
return len(dRand.PublicKeys)
}