Merge pull request #471 from harmony-one/rj_branch

Add a simple POC VDF and integrate into the drand protocol
pull/475/head
Rongjian Lan 6 years ago committed by GitHub
commit babd515a52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      api/service/consensus/service.go
  2. 1
      cmd/harmony.go
  3. 31
      consensus/consensus.go
  4. 9
      consensus/consensus_leader.go
  5. 54
      crypto/vdf/vdf.go
  6. 2
      drand/drand.go
  7. 31
      drand/drand_leader.go
  8. 5
      node/node.go

@ -25,6 +25,7 @@ func (s *Service) StartService() {
s.stopChan = make(chan struct{}) s.stopChan = make(chan struct{})
s.stoppedChan = make(chan struct{}) s.stoppedChan = make(chan struct{})
s.consensus.WaitForNewBlock(s.blockChannel, s.stopChan, s.stoppedChan, s.startChan) s.consensus.WaitForNewBlock(s.blockChannel, s.stopChan, s.stoppedChan, s.startChan)
s.consensus.WaitForNewRandomness()
} }
// StopService stops consensus service. // StopService stops consensus service.

@ -270,6 +270,7 @@ func main() {
// TODO: put this in a better place other than main. // TODO: put this in a better place other than main.
dRand := drand.New(host, shardID, peers, leader, currentNode.ConfirmedBlockChannel) dRand := drand.New(host, shardID, peers, leader, currentNode.ConfirmedBlockChannel)
currentNode.Consensus.RegisterPRndChannel(dRand.PRndChannel) currentNode.Consensus.RegisterPRndChannel(dRand.PRndChannel)
currentNode.Consensus.RegisterRndChannel(dRand.RndChannel)
currentNode.DRand = dRand currentNode.DRand = dRand
// If there is a client configured in the node list. // If there is a client configured in the node list.

@ -99,6 +99,9 @@ type Consensus struct {
// Channel for DRG protocol to send pRnd (preimage of randomness resulting from combined vrf randomnesses) to consensus. The first 32 bytes are randomness, the rest is for bitmap. // Channel for DRG protocol to send pRnd (preimage of randomness resulting from combined vrf randomnesses) to consensus. The first 32 bytes are randomness, the rest is for bitmap.
PRndChannel chan []byte PRndChannel chan []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
RndChannel chan [64]byte
pendingRnds [][64]byte // A list of pending randomness
uniqueIDInstance *utils.UniqueValidatorID uniqueIDInstance *utils.UniqueValidatorID
@ -140,6 +143,29 @@ func (consensus *Consensus) UpdateConsensusID(consensusID uint32) {
} }
} }
// WaitForNewRandomness listens to the RndChannel to receive new VDF randomness.
func (consensus *Consensus) WaitForNewRandomness() {
go func() {
for {
vdfOutput := <-consensus.RndChannel
consensus.pendingRnds = append(consensus.pendingRnds, vdfOutput)
}
}()
}
// GetNextRnd returns the oldest available randomness along with the hash of the block there randomness preimage is committed.
func (consensus *Consensus) GetNextRnd() ([32]byte, [32]byte, error) {
if len(consensus.pendingRnds) == 0 {
return [32]byte{}, [32]byte{}, errors.New("No available randomness")
}
vdfOutput := consensus.pendingRnds[0]
rnd := [32]byte{}
blockHash := [32]byte{}
copy(rnd[:], vdfOutput[:32])
copy(blockHash[:], vdfOutput[32:])
return rnd, blockHash, nil
}
// New creates a new Consensus object // New creates a new Consensus object
func New(host p2p.Host, ShardID string, peers []p2p.Peer, leader p2p.Peer) *Consensus { func New(host p2p.Host, ShardID string, peers []p2p.Peer, leader p2p.Peer) *Consensus {
consensus := Consensus{} consensus := Consensus{}
@ -222,6 +248,11 @@ func (consensus *Consensus) RegisterPRndChannel(pRndChannel chan []byte) {
consensus.PRndChannel = pRndChannel consensus.PRndChannel = pRndChannel
} }
// RegisterRndChannel registers the channel for receiving final randomness from DRG protocol
func (consensus *Consensus) RegisterRndChannel(rndChannel chan [64]byte) {
consensus.RndChannel = rndChannel
}
// Checks the basic meta of a consensus message, including the signature. // Checks the basic meta of a consensus message, including the signature.
func (consensus *Consensus) checkConsensusMessage(message consensus_proto.Message, publicKey *bls.PublicKey) error { func (consensus *Consensus) checkConsensusMessage(message consensus_proto.Message, publicKey *bls.PublicKey) error {
consensusID := message.ConsensusId consensusID := message.ConsensusId

@ -78,6 +78,15 @@ func (consensus *Consensus) WaitForNewBlock(blockChannel chan *types.Block, stop
// TODO: check validity of pRnd // TODO: check validity of pRnd
newBlock.AddRandPreimage(binary.BigEndian.Uint32(pRnd)) newBlock.AddRandPreimage(binary.BigEndian.Uint32(pRnd))
} }
rnd, blockHash, err := consensus.GetNextRnd()
if err == nil {
// Verify the randomness
_ = blockHash
utils.GetLogInstance().Info("Adding randomness into new block", "rnd", rnd)
newBlock.AddRandSeed(binary.BigEndian.Uint32(rnd[:]))
} else {
utils.GetLogInstance().Info("Failed to get randomness", "error", err)
}
startTime = time.Now() startTime = time.Now()
utils.GetLogInstance().Debug("STARTING CONSENSUS", "numTxs", len(newBlock.Transactions()), "consensus", consensus, "startTime", startTime, "publicKeys", len(consensus.PublicKeys)) utils.GetLogInstance().Debug("STARTING CONSENSUS", "numTxs", len(newBlock.Transactions()), "consensus", consensus, "startTime", startTime, "publicKeys", len(consensus.PublicKeys))
for { // Wait until last consensus is finished for { // Wait until last consensus is finished

@ -0,0 +1,54 @@
// Package vdf is a proof-of-concept implementation of a delay function
// and the security properties are not guaranteed.
// A more secure implementation of the VDF by Wesolowski (https://eprint.iacr.org/2018/623.pdf)
// will be done soon.
package vdf
import "golang.org/x/crypto/sha3"
// VDF is the struct holding necessary state for a hash chain delay function.
type VDF struct {
difficulty int
input [32]byte
output [32]byte
outputChan chan [32]byte
finished bool
}
// New create a new instance of VDF.
func New(difficulty int, input [32]byte) *VDF {
return &VDF{
difficulty: difficulty,
input: input,
outputChan: make(chan [32]byte),
}
}
// GetOutputChannel returns the vdf output channel.
func (vdf *VDF) GetOutputChannel() chan [32]byte {
return vdf.outputChan
}
// Execute runs the VDF until it's finished and put the result into output channel.
func (vdf *VDF) Execute() {
vdf.finished = false
tempResult := vdf.input
for i := 0; i < vdf.difficulty; i++ {
tempResult = sha3.Sum256(tempResult[:])
}
vdf.output = tempResult
go func() {
vdf.outputChan <- vdf.output
}()
vdf.finished = true
}
// IsFinished returns whether the vdf execution is finished or not.
func (vdf *VDF) IsFinished() bool {
return vdf.finished
}
// GetOutput returns the vdf output, which can be bytes of 0s is the vdf is not finished.
func (vdf *VDF) GetOutput() [32]byte {
return vdf.output
}

@ -28,6 +28,7 @@ type DRand struct {
rand *[32]byte rand *[32]byte
ConfirmedBlockChannel chan *types.Block // Channel to receive confirmed blocks 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. 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 // global consensus mutex
mutex sync.Mutex mutex sync.Mutex
@ -77,6 +78,7 @@ func New(host p2p.Host, ShardID string, peers []p2p.Peer, leader p2p.Peer, confi
} }
dRand.PRndChannel = make(chan []byte) dRand.PRndChannel = make(chan []byte)
dRand.RndChannel = make(chan [64]byte)
selfPeer := host.GetSelfPeer() selfPeer := host.GetSelfPeer()
if leader.Port == selfPeer.Port && leader.IP == selfPeer.IP { if leader.Port == selfPeer.Port && leader.IP == selfPeer.IP {

@ -2,6 +2,10 @@ package drand
import ( import (
"bytes" "bytes"
"encoding/binary"
"time"
"github.com/harmony-one/harmony/crypto/vdf"
protobuf "github.com/golang/protobuf/proto" protobuf "github.com/golang/protobuf/proto"
drand_proto "github.com/harmony-one/harmony/api/drand" drand_proto "github.com/harmony-one/harmony/api/drand"
@ -13,6 +17,10 @@ import (
"github.com/harmony-one/harmony/p2p/host" "github.com/harmony-one/harmony/p2p/host"
) )
const (
vdfDifficulty = 5000000 // This takes about 20s to finish the vdf
)
// WaitForEpochBlock waits for the first epoch block to run DRG on // WaitForEpochBlock waits for the first epoch block to run DRG on
func (dRand *DRand) WaitForEpochBlock(blockChannel chan *types.Block, stopChan chan struct{}, stoppedChan chan struct{}) { func (dRand *DRand) WaitForEpochBlock(blockChannel chan *types.Block, stopChan chan struct{}, stoppedChan chan struct{}) {
go func() { go func() {
@ -25,6 +33,29 @@ func (dRand *DRand) WaitForEpochBlock(blockChannel chan *types.Block, stopChan c
if core.IsEpochLastBlock(newBlock) { if core.IsEpochLastBlock(newBlock) {
dRand.init(newBlock) dRand.init(newBlock)
} }
if core.IsEpochBlock(newBlock) && newBlock.Header().RandPreimage != 0 {
// The epoch block should contain the randomness preimage pRnd
go func() {
input := [32]byte{}
binary.BigEndian.PutUint32(input[:], newBlock.Header().RandPreimage)
vdf := vdf.New(vdfDifficulty, input)
outputChannel := vdf.GetOutputChannel()
start := time.Now()
vdf.Execute()
duration := time.Now().Sub(start)
utils.GetLogInstance().Info("VDF computation finished", "time spent", duration.String())
output := <-outputChannel
rndBytes := [64]byte{} // The first 32 bytes are the randomness and the last 32 bytes are the hash of the block where the corresponding pRnd was generated
copy(rndBytes[:32], output[:])
blockHash := newBlock.Hash()
copy(rndBytes[32:], blockHash[:])
dRand.RndChannel <- rndBytes
}()
}
case <-stopChan: case <-stopChan:
return return
} }

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"crypto/ecdsa" "crypto/ecdsa"
"encoding/binary" "encoding/binary"
"encoding/hex"
"fmt" "fmt"
"math/big" "math/big"
"os" "os"
@ -25,7 +24,7 @@ import (
proto_node "github.com/harmony-one/harmony/api/proto/node" proto_node "github.com/harmony-one/harmony/api/proto/node"
"github.com/harmony-one/harmony/api/service" "github.com/harmony-one/harmony/api/service"
service_manager "github.com/harmony-one/harmony/api/service" service_manager "github.com/harmony-one/harmony/api/service"
blockproposal "github.com/harmony-one/harmony/api/service/blockproposal" "github.com/harmony-one/harmony/api/service/blockproposal"
"github.com/harmony-one/harmony/api/service/clientsupport" "github.com/harmony-one/harmony/api/service/clientsupport"
consensus_service "github.com/harmony-one/harmony/api/service/consensus" consensus_service "github.com/harmony-one/harmony/api/service/consensus"
"github.com/harmony-one/harmony/api/service/discovery" "github.com/harmony-one/harmony/api/service/discovery"
@ -287,8 +286,6 @@ func New(host p2p.Host, consensus *bft.Consensus, db ethdb.Database) *Node {
if node.Role == BeaconLeader || node.Role == BeaconValidator { if node.Role == BeaconLeader || node.Role == BeaconValidator {
node.CurrentStakes = make(map[common.Address]int64) node.CurrentStakes = make(map[common.Address]int64)
} }
bytes, _ := rlp.EncodeToBytes(chain.GetBlockByNumber(0))
utils.GetLogInstance().Debug("TESTTEST", "block", hex.EncodeToString(bytes))
utils.GetLogInstance().Debug("Received", "blockHash", chain.GetBlockByNumber(0).Hash().Hex()) utils.GetLogInstance().Debug("Received", "blockHash", chain.GetBlockByNumber(0).Hash().Hex())
node.Consensus.ConsensusBlock = make(chan *bft.BFTBlockInfo) node.Consensus.ConsensusBlock = make(chan *bft.BFTBlockInfo)
node.Consensus.VerifiedNewBlock = make(chan *types.Block) node.Consensus.VerifiedNewBlock = make(chan *types.Block)

Loading…
Cancel
Save