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

248 lines
6.6 KiB

package drand
import (
"crypto/sha256"
"encoding/binary"
"errors"
"strconv"
"sync"
"github.com/harmony-one/harmony/crypto/vrf"
"github.com/harmony-one/harmony/crypto/vrf/p256"
"github.com/harmony-one/harmony/core/types"
protobuf "github.com/golang/protobuf/proto"
"github.com/harmony-one/bls/ffi/go/bls"
drand_proto "github.com/harmony-one/harmony/api/drand"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
"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[uint32][]byte
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.
// global consensus mutex
mutex sync.Mutex
// map of nodeID to validator Peer object
// FIXME: should use PubKey of p2p.Peer as the hashkey
validators sync.Map // key is uint16, value is p2p.Peer
// Leader's address
leader p2p.Peer
// Public keys of the committee including leader and validators
PublicKeys []*bls.PublicKey
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 Id - 4 byte
nodeID uint32
// 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 string, peers []p2p.Peer, leader p2p.Peer, confirmedBlockChannel chan *types.Block) *DRand {
dRand := DRand{}
dRand.host = host
if confirmedBlockChannel != nil {
dRand.ConfirmedBlockChannel = confirmedBlockChannel
}
dRand.PRndChannel = make(chan []byte)
selfPeer := host.GetSelfPeer()
if leader.Port == selfPeer.Port && leader.IP == selfPeer.IP {
dRand.IsLeader = true
} else {
dRand.IsLeader = false
}
dRand.leader = leader
for _, peer := range peers {
dRand.validators.Store(utils.GetUniqueIDFromPeer(peer), peer)
}
dRand.vrfs = &map[uint32][]byte{}
// Initialize cosign bitmap
allPublicKeys := make([]*bls.PublicKey, 0)
for _, validatorPeer := range peers {
allPublicKeys = append(allPublicKeys, validatorPeer.PubKey)
}
allPublicKeys = append(allPublicKeys, leader.PubKey)
dRand.PublicKeys = allPublicKeys
bitmap, _ := bls_cosi.NewMask(dRand.PublicKeys, dRand.leader.PubKey)
dRand.bitmap = bitmap
dRand.pRand = nil
dRand.rand = nil
// For now use socket address as ID
// TODO: populate Id derived from address
dRand.nodeID = utils.GetUniqueIDFromPeer(selfPeer)
// Set private key for myself so that I can sign messages.
nodeIDBytes := make([]byte, 32)
binary.LittleEndian.PutUint32(nodeIDBytes, dRand.nodeID)
privateKey := bls.SecretKey{}
err := privateKey.SetLittleEndian(nodeIDBytes)
dRand.priKey = &privateKey
dRand.pubKey = privateKey.GetPublicKey()
// VRF keys
priKey, pubKey := p256.GenerateKey()
dRand.vrfPriKey = &priKey
dRand.vrfPubKey = &pubKey
myShardID, err := strconv.Atoi(ShardID)
if err != nil {
panic("Unparseable shard Id" + ShardID)
}
dRand.ShardID = uint32(myShardID)
return &dRand
}
// AddPeers adds new peers into the validator map of the consensus
// and add the public keys
func (dRand *DRand) AddPeers(peers []*p2p.Peer) int {
count := 0
for _, peer := range peers {
_, ok := dRand.validators.Load(utils.GetUniqueIDFromPeer(*peer))
if !ok {
dRand.validators.Store(utils.GetUniqueIDFromPeer(*peer), *peer)
dRand.pubKeyLock.Lock()
dRand.PublicKeys = append(dRand.PublicKeys, peer.PubKey)
dRand.pubKeyLock.Unlock()
}
count++
}
return count
}
// Sign on the drand message signature field.
func (dRand *DRand) signDRandMessage(message *drand_proto.Message) error {
message.Signature = nil
// TODO: use custom serialization method rather than protobuf
marshaledMessage, err := protobuf.Marshal(message)
if err != nil {
return err
}
// 64 byte of signature on previous data
hash := sha256.Sum256(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 *drand_proto.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
}
// GetValidatorPeers returns list of validator peers.
func (dRand *DRand) GetValidatorPeers() []p2p.Peer {
validatorPeers := make([]p2p.Peer, 0)
dRand.validators.Range(func(k, v interface{}) bool {
if peer, ok := v.(p2p.Peer); ok {
validatorPeers = append(validatorPeers, peer)
return true
}
return false
})
return validatorPeers
}
// Verify the signature of the message are valid from the signer's public key.
func verifyMessageSig(signerPubKey *bls.PublicKey, message drand_proto.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 := sha256.Sum256(messageBytes)
if !msgSig.VerifyHash(signerPubKey, msgHash[:]) {
return errors.New("failed to verify the signature")
}
return nil
}
// Gets the validator peer based on validator ID.
func (dRand *DRand) getValidatorPeerByID(validatorID uint32) *p2p.Peer {
v, ok := dRand.validators.Load(validatorID)
if !ok {
utils.GetLogInstance().Warn("Unrecognized validator", "validatorID", validatorID, "dRand", dRand)
return nil
}
value, ok := v.(p2p.Peer)
if !ok {
utils.GetLogInstance().Warn("Invalid validator", "validatorID", validatorID, "dRand", dRand)
return nil
}
return &value
}
// ResetState resets the state of the randomness protocol
func (dRand *DRand) ResetState() {
dRand.vrfs = &map[uint32][]byte{}
bitmap, _ := bls_cosi.NewMask(dRand.PublicKeys, dRand.leader.PubKey)
dRand.bitmap = bitmap
dRand.pRand = nil
dRand.rand = nil
}