From c55d2ef04a585014bbf9ae8d19041de1049046d7 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Thu, 14 Feb 2019 15:48:36 -0800 Subject: [PATCH 1/2] Add real vrf into drand with validator generating vrf and leader verifying them --- crypto/vrf/p256/p256.go | 13 ++++++++++++- crypto/vrf/vrf.go | 4 ++++ drand/drand.go | 19 +++++++++++++++++-- drand/drand_leader.go | 22 +++++++++++++++++----- drand/drand_validator_msg.go | 4 +++- drand/drand_validator_msg_test.go | 2 +- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/crypto/vrf/p256/p256.go b/crypto/vrf/p256/p256.go index 553dbc8ce..097566037 100644 --- a/crypto/vrf/p256/p256.go +++ b/crypto/vrf/p256/p256.go @@ -37,8 +37,8 @@ import ( "fmt" "math/big" - "github.com/google/keytransparency/core/crypto/vrf" "github.com/google/trillian/crypto/keys" + "github.com/harmony-one/harmony/crypto/vrf" "github.com/golang/protobuf/proto" ) @@ -67,6 +67,17 @@ type PrivateKey struct { *ecdsa.PrivateKey } +// Serialize serialize the public key into bytes +func (pk *PublicKey) Serialize() []byte { + return append(pk.PublicKey.X.Bytes(), pk.PublicKey.Y.Bytes()...) +} + +// Deserialize de-serialize bytes into public key +func (pk *PublicKey) Deserialize(data []byte) { + pk.X.SetBytes(data[:len(data)/2]) + pk.Y.SetBytes(data[len(data)/2:]) +} + // GenerateKey generates a fresh keypair for this VRF func GenerateKey() (vrf.PrivateKey, vrf.PublicKey) { key, err := ecdsa.GenerateKey(curve, rand.Reader) diff --git a/crypto/vrf/vrf.go b/crypto/vrf/vrf.go index c0f9f0cbe..400b1b32d 100644 --- a/crypto/vrf/vrf.go +++ b/crypto/vrf/vrf.go @@ -37,4 +37,8 @@ type PrivateKey interface { type PublicKey interface { // ProofToHash verifies the NP-proof supplied by Proof and outputs Index. ProofToHash(m, proof []byte) (index [32]byte, err error) + // Serialize serialize the public key into bytes + Serialize() []byte + // Deserialize de-serialize bytes into public key + Deserialize([]byte) } diff --git a/drand/drand.go b/drand/drand.go index 2409daa7e..492b09792 100644 --- a/drand/drand.go +++ b/drand/drand.go @@ -7,6 +7,9 @@ import ( "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" @@ -26,6 +29,9 @@ type DRand struct { 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 @@ -40,6 +46,10 @@ type DRand struct { // 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 @@ -109,6 +119,11 @@ func New(host p2p.Host, ShardID string, peers []p2p.Peer, leader p2p.Peer, confi 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) @@ -167,8 +182,8 @@ func (dRand *DRand) signAndMarshalDRandMessage(message *drand_proto.Message) ([] } func (dRand *DRand) vrf(blockHash [32]byte) (rand [32]byte, proof []byte) { - // TODO: implement vrf - return [32]byte{}, []byte{} + rand, proof = (*dRand.vrfPriKey).Evaluate(blockHash[:]) + return } // GetValidatorPeers returns list of validator peers. diff --git a/drand/drand_leader.go b/drand/drand_leader.go index 6a37f97be..43479a67b 100644 --- a/drand/drand_leader.go +++ b/drand/drand_leader.go @@ -1,10 +1,13 @@ package drand import ( + "bytes" + protobuf "github.com/golang/protobuf/proto" drand_proto "github.com/harmony-one/harmony/api/drand" "github.com/harmony-one/harmony/core" "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/crypto/vrf/p256" "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/p2p/host" ) @@ -69,6 +72,9 @@ func (dRand *DRand) processCommitMessage(message drand_proto.Message) { return } + dRand.mutex.Lock() + defer dRand.mutex.Unlock() + validatorID := message.SenderId validatorPeer := dRand.getValidatorPeerByID(validatorID) vrfs := dRand.vrfs @@ -85,10 +91,17 @@ func (dRand *DRand) processCommitMessage(message drand_proto.Message) { } rand := message.Payload[:32] - proof := message.Payload[32:] - _ = rand - _ = proof - // TODO: check the validity of the vrf commit + proof := message.Payload[32 : len(message.Payload)-64] + pubKeyBytes := message.Payload[len(message.Payload)-64:] + _, pubKey := p256.GenerateKey() + pubKey.Deserialize(pubKeyBytes) + + expectedRand, err := pubKey.ProofToHash(dRand.blockHash[:], proof) + + if err != nil || !bytes.Equal(expectedRand[:], rand) { + utils.GetLogInstance().Error("Failed to verify the VRF", "error", err, "validatorID", validatorID, "expectedRand", expectedRand, "receivedRand", rand) + return + } utils.GetLogInstance().Debug("Received new commit", "numReceivedSoFar", len((*vrfs)), "validatorID", validatorID, "PublicKeys", len(dRand.PublicKeys)) @@ -98,7 +111,6 @@ func (dRand *DRand) processCommitMessage(message drand_proto.Message) { if len((*vrfs)) >= ((len(dRand.PublicKeys))/3 + 1) { // Construct pRand and initiate consensus on it utils.GetLogInstance().Debug("Received enough randomness commit", "numReceivedSoFar", len((*vrfs)), "validatorID", validatorID, "PublicKeys", len(dRand.PublicKeys)) - // TODO: communicate the pRand to consensus pRnd := [32]byte{} // Bitwise XOR on all the submitted vrfs diff --git a/drand/drand_validator_msg.go b/drand/drand_validator_msg.go index 6c3d25d1e..067458015 100644 --- a/drand/drand_validator_msg.go +++ b/drand/drand_validator_msg.go @@ -14,7 +14,9 @@ func (dRand *DRand) constructCommitMessage(vrf [32]byte, proof []byte) []byte { message.BlockHash = dRand.blockHash[:] message.Payload = append(vrf[:], proof...) - + // Adding the public key into payload so leader can verify the vrf + // TODO: change the curve to follow the same curve with consensus, so the public key doesn't need to be attached. + message.Payload = append(message.Payload, (*dRand.vrfPubKey).Serialize()...) marshaledMessage, err := dRand.signAndMarshalDRandMessage(&message) if err != nil { utils.GetLogInstance().Error("Failed to sign and marshal the commit message", "error", err) diff --git a/drand/drand_validator_msg_test.go b/drand/drand_validator_msg_test.go index 14a6917be..21bd0b6e8 100644 --- a/drand/drand_validator_msg_test.go +++ b/drand/drand_validator_msg_test.go @@ -20,7 +20,7 @@ func TestConstructCommitMessage(test *testing.T) { dRand.blockHash = [32]byte{} msg := dRand.constructCommitMessage([32]byte{}, []byte{}) - if len(msg) != 127 { + if len(msg) != 191 { test.Errorf("Commit message is not constructed in the correct size: %d", len(msg)) } } From 91d7567c94863b878818c8ad2cd14891df1beb2d Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Thu, 14 Feb 2019 15:50:23 -0800 Subject: [PATCH 2/2] Reduce delay on the manager test --- api/service/manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/service/manager_test.go b/api/service/manager_test.go index c1780a627..7e0235859 100644 --- a/api/service/manager_test.go +++ b/api/service/manager_test.go @@ -24,7 +24,7 @@ func TestTakeAction(t *testing.T) { for i := 0; i < 2; i++ { select { - case <-time.After(WaitForStatusUpdate): + case <-time.After(100 * time.Millisecond): m.SendAction(&Action{Action: Start, ServiceType: SupportSyncing}) } }