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.
248 lines
6.6 KiB
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
|
|
}
|
|
|