Merge pull request #318 from harmony-one/rj_branch

Migrate to BLS signature for consensus and other crypto signatures
pull/336/head
Rongjian Lan 6 years ago committed by GitHub
commit c182c270af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 79
      api/consensus/consensus.pb.go
  2. 11
      api/consensus/consensus.proto
  3. 21
      api/proto/bcconn/bcconn_test.go
  4. 22
      api/proto/node/pingpong.go
  5. 15
      api/proto/node/pingpong_test.go
  6. 138
      consensus/consensus.go
  7. 317
      consensus/consensus_leader.go
  8. 106
      consensus/consensus_leader_msg.go
  9. 34
      consensus/consensus_leader_msg_test.go
  10. 51
      consensus/consensus_leader_test.go
  11. 20
      consensus/consensus_state.go
  12. 10
      consensus/consensus_test.go
  13. 243
      consensus/consensus_validator.go
  14. 44
      consensus/consensus_validator_msg.go
  15. 18
      consensus/consensus_validator_msg_test.go
  16. 32
      consensus/consensus_validator_test.go
  17. 225
      crypto/bls/bls.go
  18. 32
      crypto/pki/utils.go
  19. 13
      crypto/pki/utils_test.go
  20. 11
      internal/beaconchain/libs/beaconchain.go
  21. 16
      internal/newnode/newnode.go
  22. 16
      internal/utils/utils.go
  23. 19
      node/node_handler.go
  24. 6
      node/node_handler_test.go
  25. 25
      node/node_test.go
  26. 8
      p2p/p2p.go
  27. 15
      scripts/go_executable_build.sh
  28. 15
      scripts/travis_checker.sh
  29. 18
      test/crypto/bls/main.go
  30. 15
      test/deploy.sh

@ -23,39 +23,30 @@ const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type MessageType int32
const (
MessageType_UNKNOWN MessageType = 0
MessageType_ANNOUNCE MessageType = 1
MessageType_COMMIT MessageType = 2
MessageType_CHALLENGE MessageType = 3
MessageType_RESPONSE MessageType = 4
MessageType_COLLECTIVE_SIG MessageType = 5
MessageType_FINAL_COMMIT MessageType = 6
MessageType_FINAL_CHALLENGE MessageType = 7
MessageType_FINAL_RESPONSE MessageType = 8
MessageType_UNKNOWN MessageType = 0
MessageType_ANNOUNCE MessageType = 1
MessageType_PREPARE MessageType = 2
MessageType_PREPARED MessageType = 3
MessageType_COMMIT MessageType = 4
MessageType_COMMITTED MessageType = 5
)
var MessageType_name = map[int32]string{
0: "UNKNOWN",
1: "ANNOUNCE",
2: "COMMIT",
3: "CHALLENGE",
4: "RESPONSE",
5: "COLLECTIVE_SIG",
6: "FINAL_COMMIT",
7: "FINAL_CHALLENGE",
8: "FINAL_RESPONSE",
2: "PREPARE",
3: "PREPARED",
4: "COMMIT",
5: "COMMITTED",
}
var MessageType_value = map[string]int32{
"UNKNOWN": 0,
"ANNOUNCE": 1,
"COMMIT": 2,
"CHALLENGE": 3,
"RESPONSE": 4,
"COLLECTIVE_SIG": 5,
"FINAL_COMMIT": 6,
"FINAL_CHALLENGE": 7,
"FINAL_RESPONSE": 8,
"UNKNOWN": 0,
"ANNOUNCE": 1,
"PREPARE": 2,
"PREPARED": 3,
"COMMIT": 4,
"COMMITTED": 5,
}
func (x MessageType) String() string {
@ -153,24 +144,22 @@ func init() {
func init() { proto.RegisterFile("consensus.proto", fileDescriptor_56f0f2c53b3de771) }
var fileDescriptor_56f0f2c53b3de771 = []byte{
// 302 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x91, 0xdf, 0x4e, 0xc2, 0x30,
0x14, 0xc6, 0x2d, 0x7f, 0x36, 0x76, 0x18, 0xd0, 0x1c, 0x13, 0xd3, 0x44, 0x4d, 0xd0, 0x2b, 0xc2,
0x05, 0x17, 0xfa, 0x04, 0x64, 0xa9, 0xd0, 0x38, 0x3a, 0x33, 0x40, 0x2f, 0x97, 0xc2, 0x1a, 0x20,
0x92, 0x6d, 0xa1, 0x70, 0xc1, 0xdb, 0xf8, 0x40, 0x3e, 0x94, 0x61, 0x60, 0xf5, 0xb2, 0xbf, 0xdf,
0xf7, 0x7d, 0x69, 0x72, 0xa0, 0xb3, 0xcc, 0x33, 0xa3, 0x33, 0x73, 0x30, 0x83, 0x62, 0x97, 0xef,
0x73, 0xf4, 0x2c, 0x78, 0xfc, 0x26, 0xe0, 0x4e, 0xb4, 0x31, 0x6a, 0xa5, 0xb1, 0x0f, 0xb5, 0xfd,
0xb1, 0xd0, 0x8c, 0x74, 0x49, 0xaf, 0xfd, 0x74, 0x33, 0xf8, 0xab, 0x5d, 0x12, 0xb3, 0x63, 0xa1,
0xe3, 0x32, 0x83, 0x0f, 0xe0, 0x5b, 0x9d, 0x6c, 0x52, 0x56, 0xe9, 0x92, 0x5e, 0x2b, 0x6e, 0x5a,
0x26, 0x52, 0xbc, 0x05, 0xcf, 0xe8, 0x2c, 0xd5, 0xbb, 0x93, 0xaf, 0x96, 0xbe, 0x71, 0x06, 0x22,
0xc5, 0x7b, 0x80, 0xc5, 0x36, 0x5f, 0x7e, 0x26, 0x6b, 0x65, 0xd6, 0xac, 0xd6, 0x25, 0x3d, 0x3f,
0xf6, 0x4a, 0x32, 0x56, 0x66, 0x8d, 0x0c, 0xdc, 0x42, 0x1d, 0xb7, 0xb9, 0x4a, 0x59, 0xbd, 0x74,
0xbf, 0x4f, 0xbc, 0x03, 0xcf, 0x6c, 0x56, 0x99, 0xda, 0x1f, 0x76, 0x9a, 0x39, 0xe7, 0x9e, 0x05,
0xfd, 0x2f, 0x02, 0xcd, 0x7f, 0x9f, 0xc5, 0x26, 0xb8, 0x73, 0xf9, 0x2a, 0xa3, 0x0f, 0x49, 0xaf,
0xd0, 0x87, 0xc6, 0x50, 0xca, 0x68, 0x2e, 0x03, 0x4e, 0x09, 0x02, 0x38, 0x41, 0x34, 0x99, 0x88,
0x19, 0xad, 0x60, 0x0b, 0xbc, 0x60, 0x3c, 0x0c, 0x43, 0x2e, 0x47, 0x9c, 0x56, 0x4f, 0xc1, 0x98,
0x4f, 0xdf, 0x22, 0x39, 0xe5, 0xb4, 0x86, 0x08, 0xed, 0x20, 0x0a, 0x43, 0x1e, 0xcc, 0xc4, 0x3b,
0x4f, 0xa6, 0x62, 0x44, 0xeb, 0x48, 0xc1, 0x7f, 0x11, 0x72, 0x18, 0x26, 0x97, 0x09, 0x07, 0xaf,
0xa1, 0x73, 0x21, 0x76, 0xc8, 0x3d, 0x55, 0xcf, 0xd0, 0xce, 0x35, 0x16, 0x4e, 0x79, 0x83, 0xe7,
0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x86, 0xa2, 0x8a, 0x4f, 0x96, 0x01, 0x00, 0x00,
// 263 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x90, 0xc1, 0x6a, 0xb3, 0x40,
0x14, 0x85, 0xff, 0x49, 0x8c, 0xc6, 0xab, 0xf9, 0x3b, 0xdc, 0x45, 0x19, 0x68, 0x0b, 0xb6, 0x2b,
0xc9, 0x22, 0x8b, 0xf6, 0x09, 0x42, 0x22, 0x54, 0x8a, 0x63, 0x10, 0x43, 0x77, 0x0d, 0x93, 0x38,
0xc4, 0xd0, 0xa0, 0xe2, 0x98, 0x85, 0xef, 0xd8, 0x87, 0x2a, 0x8e, 0xa9, 0xed, 0x6e, 0xce, 0xf7,
0x9d, 0x03, 0xc3, 0x85, 0x9b, 0x43, 0x59, 0x28, 0x59, 0xa8, 0x8b, 0x5a, 0x54, 0x75, 0xd9, 0x94,
0x68, 0x0f, 0xe0, 0xe9, 0x8b, 0x80, 0x15, 0x49, 0xa5, 0xc4, 0x51, 0xe2, 0x1c, 0x8c, 0xa6, 0xad,
0x24, 0x23, 0x1e, 0xf1, 0xff, 0x3f, 0xdf, 0x2e, 0x7e, 0x67, 0xd7, 0x46, 0xda, 0x56, 0x32, 0xd1,
0x1d, 0x7c, 0x04, 0x77, 0xd0, 0xbb, 0x53, 0xc6, 0x46, 0x1e, 0xf1, 0x67, 0x89, 0x33, 0xb0, 0x30,
0xc3, 0x3b, 0xb0, 0x95, 0x2c, 0x32, 0x59, 0x77, 0x7e, 0xac, 0xfd, 0xb4, 0x07, 0x61, 0x86, 0x0f,
0x00, 0xfb, 0x73, 0x79, 0xf8, 0xdc, 0xe5, 0x42, 0xe5, 0xcc, 0xf0, 0x88, 0xef, 0x26, 0xb6, 0x26,
0xaf, 0x42, 0xe5, 0xc8, 0xc0, 0xaa, 0x44, 0x7b, 0x2e, 0x45, 0xc6, 0x26, 0xda, 0xfd, 0x44, 0xbc,
0x07, 0x5b, 0x9d, 0x8e, 0x85, 0x68, 0x2e, 0xb5, 0x64, 0x66, 0xbf, 0x1b, 0xc0, 0xfc, 0x03, 0x9c,
0x3f, 0x7f, 0x45, 0x07, 0xac, 0x2d, 0x7f, 0xe3, 0xf1, 0x3b, 0xa7, 0xff, 0xd0, 0x85, 0xe9, 0x92,
0xf3, 0x78, 0xcb, 0x57, 0x01, 0x25, 0x9d, 0xda, 0x24, 0xc1, 0x66, 0x99, 0x04, 0x74, 0xd4, 0xa9,
0x6b, 0x58, 0xd3, 0x31, 0x02, 0x98, 0xab, 0x38, 0x8a, 0xc2, 0x94, 0x1a, 0x38, 0x03, 0xbb, 0x7f,
0xa7, 0xc1, 0x9a, 0x4e, 0xf6, 0xa6, 0x3e, 0xe0, 0xcb, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd5,
0x30, 0x8a, 0xaf, 0x53, 0x01, 0x00, 0x00,
}

@ -5,13 +5,10 @@ package consensus;
enum MessageType {
UNKNOWN = 0;
ANNOUNCE = 1;
COMMIT = 2;
CHALLENGE = 3;
RESPONSE = 4;
COLLECTIVE_SIG = 5;
FINAL_COMMIT = 6;
FINAL_CHALLENGE = 7;
FINAL_RESPONSE = 8;
PREPARE = 2;
PREPARED = 3;
COMMIT = 4;
COMMITTED = 5;
}
message Message {

@ -13,11 +13,8 @@ func TestSerializeDeserializeNodeInfo(t *testing.T) {
var ip, port string
ip = "127.0.0.1"
port = "8080"
_, pk := utils.GenKey(ip, port)
pkb, err := pk.MarshalBinary()
if err != nil {
fmt.Println("problem marshalling binary from public key")
}
_, pk := utils.GenKeyBLS(ip, port)
pkb := pk.Serialize()
nodeInfo := &node.Info{IP: ip, Port: port, PubKey: pkb}
serializedNI := SerializeNodeInfo(nodeInfo)
deserializedNI := DeserializeNodeInfo(serializedNI)
@ -32,20 +29,14 @@ func TestSerializeDeserializeRandomInfo(t *testing.T) {
ip = "127.0.0.1"
port = "8080"
_, pk := utils.GenKey(ip, port)
pkb, err := pk.MarshalBinary()
if err != nil {
fmt.Println("problem marshalling binary from public key")
}
_, pk := utils.GenKeyBLS(ip, port)
pkb := pk.Serialize()
nodeInfo1 := &node.Info{IP: ip, Port: port, PubKey: pkb}
ip = "127.0.0.1"
port = "9080"
_, pk2 := utils.GenKey(ip, port)
pkb2, err := pk2.MarshalBinary()
if err != nil {
fmt.Println("problem marshalling binary from public key")
}
_, pk2 := utils.GenKeyBLS(ip, port)
pkb2 := pk2.Serialize()
nodeInfo2 := &node.Info{IP: ip, Port: port, PubKey: pkb2}
leaders := make([]*node.Info, 2)

@ -14,13 +14,13 @@ import (
"bytes"
"encoding/gob"
"fmt"
"github.com/harmony-one/bls/ffi/go/bls"
"log"
"github.com/dedis/kyber"
"github.com/harmony-one/harmony/api/proto"
"github.com/harmony-one/harmony/p2p"
peer "github.com/libp2p/go-libp2p-peer"
"github.com/libp2p/go-libp2p-peer"
)
// RoleType defines the role of the node
@ -84,25 +84,19 @@ func (p PongMessageType) String() string {
func NewPingMessage(peer p2p.Peer) *PingMessageType {
ping := new(PingMessageType)
var err error
ping.Version = ProtocolVersion
ping.Node.IP = peer.IP
ping.Node.Port = peer.Port
ping.Node.PeerID = peer.PeerID
ping.Node.ValidatorID = peer.ValidatorID
ping.Node.PubKey, err = peer.PubKey.MarshalBinary()
ping.Node.PubKey = peer.PubKey.Serialize()
ping.Node.Role = ValidatorRole
if err != nil {
fmt.Printf("Error Marshal PubKey: %v", err)
return nil
}
return ping
}
// NewPongMessage creates a new Pong message based on a list of p2p.Peer and a list of publicKeys
func NewPongMessage(peers []p2p.Peer, pubKeys []kyber.Point) *PongMessageType {
func NewPongMessage(peers []p2p.Peer, pubKeys []*bls.PublicKey) *PongMessageType {
pong := new(PongMessageType)
pong.PubKeys = make([][]byte, 0)
@ -116,7 +110,7 @@ func NewPongMessage(peers []p2p.Peer, pubKeys []kyber.Point) *PongMessageType {
n.Port = p.Port
n.ValidatorID = p.ValidatorID
n.PeerID = p.PeerID
n.PubKey, err = p.PubKey.MarshalBinary()
n.PubKey = p.PubKey.Serialize()
if err != nil {
fmt.Printf("Error Marshal PubKey: %v", err)
continue
@ -125,11 +119,7 @@ func NewPongMessage(peers []p2p.Peer, pubKeys []kyber.Point) *PongMessageType {
}
for _, p := range pubKeys {
key, err := p.MarshalBinary()
if err != nil {
fmt.Printf("Error Marshal PublicKeys: %v", err)
continue
}
key := p.Serialize()
pong.PubKeys = append(pong.PubKeys, key)
}

@ -2,31 +2,28 @@ package node
import (
"fmt"
"github.com/harmony-one/bls/ffi/go/bls"
"reflect"
"strings"
"testing"
"github.com/dedis/kyber"
"github.com/harmony-one/harmony/api/proto"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/crypto/pki"
"github.com/harmony-one/harmony/p2p"
)
var (
priKey1 = crypto.Ed25519Curve.Scalar().SetInt64(int64(333))
pubKey1 = pki.GetPublicKeyFromScalar(priKey1)
pubKey1 = pki.GetBLSPrivateKeyFromInt(333).GetPublicKey()
p1 = p2p.Peer{
IP: "127.0.0.1",
Port: "9999",
ValidatorID: -1,
PubKey: pubKey1,
}
e1 = "ping:Validator/1=>127.0.0.1:9999:-1/[90 217 28 68 64 211 160 232 61 244 159 244 160 36 61 161 237 242 236 45 147 118 237 88 234 122 198 188 157 116 90 228]"
e3 = "ping:Client/1=>127.0.0.1:9999:-1/[90 217 28 68 64 211 160 232 61 244 159 244 160 36 61 161 237 242 236 45 147 118 237 88 234 122 198 188 157 116 90 228]"
e1 = "ping:Validator/1=>127.0.0.1:9999:-1/[120 1 130 197 30 202 78 236 84 249 5 230 132 208 242 242 246 63 100 123 96 11 211 228 4 56 64 94 57 133 3 226 254 222 231 160 178 81 252 205 40 28 45 2 90 74 207 15 68 86 138 68 143 176 221 161 108 105 133 6 64 121 92 25 134 255 9 209 156 209 119 187 13 160 23 147 240 24 196 152 100 20 163 51 118 45 100 26 179 227 184 166 147 113 50 139]"
e3 = "ping:Client/1=>127.0.0.1:9999:-1/[120 1 130 197 30 202 78 236 84 249 5 230 132 208 242 242 246 63 100 123 96 11 211 228 4 56 64 94 57 133 3 226 254 222 231 160 178 81 252 205 40 28 45 2 90 74 207 15 68 86 138 68 143 176 221 161 108 105 133 6 64 121 92 25 134 255 9 209 156 209 119 187 13 160 23 147 240 24 196 152 100 20 163 51 118 45 100 26 179 227 184 166 147 113 50 139]"
priKey2 = crypto.Ed25519Curve.Scalar().SetInt64(int64(999))
pubKey2 = pki.GetPublicKeyFromScalar(priKey2)
pubKey2 = pki.GetBLSPrivateKeyFromInt(999).GetPublicKey()
p2 = []p2p.Peer{
{
@ -46,7 +43,7 @@ var (
}
e2 = "pong:1=>length:2"
pubKeys = []kyber.Point{pubKey1, pubKey2}
pubKeys = []*bls.PublicKey{pubKey1, pubKey2}
buf1 []byte
buf2 []byte

@ -2,20 +2,21 @@
package consensus // consensus
import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/harmony-one/bls/ffi/go/bls"
"reflect"
"strconv"
"sync"
"github.com/dedis/kyber"
"github.com/dedis/kyber/sign/schnorr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/core/state"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/crypto/pki"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p"
"github.com/harmony-one/harmony/p2p/host"
@ -30,20 +31,12 @@ type Consensus struct {
state State
// Commits collected from validators.
commitments *map[uint32]kyber.Point
finalCommitments *map[uint32]kyber.Point
aggregatedCommitment kyber.Point
aggregatedFinalCommitment kyber.Point
bitmap *crypto.Mask
finalBitmap *crypto.Mask
// Challenges for the validators
challenge [32]byte
finalChallenge [32]byte
// Responses collected from validators
responses *map[uint32]kyber.Scalar
finalResponses *map[uint32]kyber.Scalar
prepareSigs *map[uint32]*bls.Sign
commitSigs *map[uint32]*bls.Sign
aggregatedPrepareSig *bls.Sign
aggregatedCommitSig *bls.Sign
prepareBitmap *bls_cosi.Mask
commitBitmap *bls_cosi.Mask
// map of nodeID to validator Peer object
// FIXME: should use PubKey of p2p.Peer as the hashkey
@ -57,12 +50,12 @@ type Consensus struct {
leader p2p.Peer
// Public keys of the committee including leader and validators
PublicKeys []kyber.Point
PublicKeys []*bls.PublicKey
pubKeyLock sync.Mutex
// private/public keys of current node
priKey kyber.Scalar
pubKey kyber.Point
priKey *bls.SecretKey
pubKey *bls.PublicKey
// Whether I am leader. False means I am validator
IsLeader bool
@ -85,8 +78,6 @@ type Consensus struct {
// Validator specific fields
// Blocks received but not done with consensus yet
blocksReceived map[uint32]*BlockConsensusStatus
// Commitment secret
secret map[uint32]kyber.Scalar
// Signal channel for starting a new consensus process
ReadySignal chan struct{}
@ -136,43 +127,43 @@ func New(host p2p.Host, ShardID string, peers []p2p.Peer, leader p2p.Peer) *Cons
consensus.IsLeader = false
}
consensus.commitments = &map[uint32]kyber.Point{}
consensus.finalCommitments = &map[uint32]kyber.Point{}
consensus.responses = &map[uint32]kyber.Scalar{}
consensus.finalResponses = &map[uint32]kyber.Scalar{}
consensus.leader = leader
for _, peer := range peers {
consensus.validators.Store(utils.GetUniqueIDFromPeer(peer), peer)
}
consensus.prepareSigs = &map[uint32]*bls.Sign{}
consensus.commitSigs = &map[uint32]*bls.Sign{}
// Initialize cosign bitmap
allPublicKeys := make([]kyber.Point, 0)
allPublicKeys := make([]*bls.PublicKey, 0)
for _, validatorPeer := range peers {
allPublicKeys = append(allPublicKeys, validatorPeer.PubKey)
}
allPublicKeys = append(allPublicKeys, leader.PubKey)
mask, err := crypto.NewMask(crypto.Ed25519Curve, allPublicKeys, consensus.leader.PubKey)
if err != nil {
panic("Failed to create mask")
}
finalMask, err := crypto.NewMask(crypto.Ed25519Curve, allPublicKeys, consensus.leader.PubKey)
if err != nil {
panic("Failed to create final mask")
}
consensus.PublicKeys = allPublicKeys
consensus.bitmap = mask
consensus.finalBitmap = finalMask
consensus.secret = map[uint32]kyber.Scalar{}
prepareBitmap, _ := bls_cosi.NewMask(consensus.PublicKeys, consensus.leader.PubKey)
commitBitmap, _ := bls_cosi.NewMask(consensus.PublicKeys, consensus.leader.PubKey)
consensus.prepareBitmap = prepareBitmap
consensus.commitBitmap = commitBitmap
consensus.aggregatedPrepareSig = nil
consensus.aggregatedCommitSig = nil
// For now use socket address as ID
// TODO: populate Id derived from address
consensus.nodeID = utils.GetUniqueIDFromPeer(selfPeer)
// Set private key for myself so that I can sign messages.
consensus.priKey = crypto.Ed25519Curve.Scalar().SetInt64(int64(consensus.nodeID))
consensus.pubKey = pki.GetPublicKeyFromScalar(consensus.priKey)
nodeIDBytes := make([]byte, 32)
binary.LittleEndian.PutUint32(nodeIDBytes, consensus.nodeID)
privateKey := bls.SecretKey{}
err := privateKey.SetLittleEndian(nodeIDBytes)
consensus.priKey = &privateKey
consensus.pubKey = privateKey.GetPublicKey()
consensus.consensusID = 0 // or view Id in the original pbft paper
myShardID, err := strconv.Atoi(ShardID)
@ -207,13 +198,11 @@ func (consensus *Consensus) Author(header *types.Header) (common.Address, error)
return common.Address{}, nil
}
// TODO: switch to BLS-based signature
// Sign on the hash of the message
func (consensus *Consensus) signMessage(message []byte) []byte {
signature, err := schnorr.Sign(crypto.Ed25519Curve, consensus.priKey, message)
if err != nil {
panic("Failed to sign message with Schnorr signature.")
}
return signature
hash := sha256.Sum256(message)
signature := consensus.priKey.SignHash(hash[:])
return signature.Serialize()
}
// GetValidatorPeers returns list of validator peers.
@ -231,24 +220,37 @@ func (consensus *Consensus) GetValidatorPeers() []p2p.Peer {
return validatorPeers
}
// GetPrepareSigsArray returns the signatures for prepare as a array
func (consensus *Consensus) GetPrepareSigsArray() []*bls.Sign {
sigs := []*bls.Sign{}
for _, sig := range *consensus.prepareSigs {
sigs = append(sigs, sig)
}
return sigs
}
// GetCommitSigsArray returns the signatures for commit as a array
func (consensus *Consensus) GetCommitSigsArray() []*bls.Sign {
sigs := []*bls.Sign{}
for _, sig := range *consensus.commitSigs {
sigs = append(sigs, sig)
}
return sigs
}
// ResetState resets the state of the consensus
func (consensus *Consensus) ResetState() {
consensus.state = Finished
consensus.commitments = &map[uint32]kyber.Point{}
consensus.finalCommitments = &map[uint32]kyber.Point{}
consensus.responses = &map[uint32]kyber.Scalar{}
consensus.finalResponses = &map[uint32]kyber.Scalar{}
mask, _ := crypto.NewMask(crypto.Ed25519Curve, consensus.PublicKeys, consensus.leader.PubKey)
finalMask, _ := crypto.NewMask(crypto.Ed25519Curve, consensus.PublicKeys, consensus.leader.PubKey)
consensus.bitmap = mask
consensus.finalBitmap = finalMask
consensus.bitmap.SetMask([]byte{})
consensus.finalBitmap.SetMask([]byte{})
consensus.aggregatedCommitment = nil
consensus.aggregatedFinalCommitment = nil
consensus.secret = map[uint32]kyber.Scalar{}
consensus.prepareSigs = &map[uint32]*bls.Sign{}
consensus.commitSigs = &map[uint32]*bls.Sign{}
prepareBitmap, _ := bls_cosi.NewMask(consensus.PublicKeys, consensus.leader.PubKey)
commitBitmap, _ := bls_cosi.NewMask(consensus.PublicKeys, consensus.leader.PubKey)
consensus.prepareBitmap = prepareBitmap
consensus.commitBitmap = commitBitmap
consensus.aggregatedPrepareSig = nil
consensus.aggregatedCommitSig = nil
// Clear the OfflinePeersList again
consensus.OfflinePeerList = make([]p2p.Peer, 0)
@ -262,8 +264,8 @@ func (consensus *Consensus) String() string {
} else {
duty = "VLD" // validator
}
return fmt.Sprintf("[duty:%s, priKey:%s, ShardID:%v, nodeID:%v, state:%s]",
duty, consensus.priKey.String(), consensus.ShardID, consensus.nodeID, consensus.state)
return fmt.Sprintf("[duty:%s, pubKey:%s, ShardID:%v, nodeID:%v, state:%s]",
duty, hex.EncodeToString(consensus.pubKey.Serialize()), consensus.ShardID, consensus.nodeID, consensus.state)
}
// AddPeers adds new peers into the validator map of the consensus
@ -343,7 +345,7 @@ func (consensus *Consensus) RemovePeers(peers []p2p.Peer) int {
// DebugPrintPublicKeys print all the PublicKeys in string format in Consensus
func (consensus *Consensus) DebugPrintPublicKeys() {
for _, k := range consensus.PublicKeys {
str := fmt.Sprintf("%s", k)
str := fmt.Sprintf("%s", hex.EncodeToString(k.Serialize()))
utils.GetLogInstance().Debug("pk:", "string", str)
}
@ -355,7 +357,7 @@ func (consensus *Consensus) DebugPrintValidators() {
count := 0
consensus.validators.Range(func(k, v interface{}) bool {
if p, ok := v.(p2p.Peer); ok {
str2 := fmt.Sprintf("%s", p.PubKey)
str2 := fmt.Sprintf("%s", p.PubKey.Serialize())
utils.GetLogInstance().Debug("validator:", "IP", p.IP, "Port", p.Port, "VID", p.ValidatorID, "Key", str2)
count++
return true
@ -366,7 +368,7 @@ func (consensus *Consensus) DebugPrintValidators() {
}
// UpdatePublicKeys updates the PublicKeys variable, protected by a mutex
func (consensus *Consensus) UpdatePublicKeys(pubKeys []kyber.Point) int {
func (consensus *Consensus) UpdatePublicKeys(pubKeys []*bls.PublicKey) int {
consensus.pubKeyLock.Lock()
// consensus.PublicKeys = make([]kyber.Point, len(pubKeys))
consensus.PublicKeys = append(pubKeys[:0:0], pubKeys...)

@ -3,18 +3,15 @@ package consensus
import (
"bytes"
"encoding/hex"
"errors"
"strconv"
"time"
"github.com/dedis/kyber"
"github.com/dedis/kyber/sign/schnorr"
"github.com/ethereum/go-ethereum/rlp"
protobuf "github.com/golang/protobuf/proto"
"github.com/harmony-one/bls/ffi/go/bls"
consensus_proto "github.com/harmony-one/harmony/api/consensus"
"github.com/harmony-one/harmony/api/services/explorer"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/internal/profiler"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p"
@ -67,14 +64,10 @@ func (consensus *Consensus) ProcessMessageLeader(payload []byte) {
}
switch message.Type {
case consensus_proto.MessageType_PREPARE:
consensus.processPrepareMessage(message)
case consensus_proto.MessageType_COMMIT:
consensus.processCommitMessage(message, ChallengeDone)
case consensus_proto.MessageType_RESPONSE:
consensus.processResponseMessage(message, CollectiveSigDone)
case consensus_proto.MessageType_FINAL_COMMIT:
consensus.processCommitMessage(message, FinalChallengeDone)
case consensus_proto.MessageType_FINAL_RESPONSE:
consensus.processResponseMessage(message, Finished)
consensus.processCommitMessage(message)
default:
utils.GetLogInstance().Error("Unexpected message type", "msgType", message.Type, "consensus", consensus)
}
@ -100,32 +93,16 @@ func (consensus *Consensus) startConsensus(newBlock *types.Block) {
// Set state to AnnounceDone
consensus.state = AnnounceDone
consensus.commitByLeader(true)
// TODO: sign for leader itself
host.BroadcastMessageFromLeader(consensus.host, consensus.GetValidatorPeers(), msgToSend, consensus.OfflinePeers)
}
// commitByLeader commits to the message by leader himself before receiving others commits
func (consensus *Consensus) commitByLeader(firstRound bool) {
// Generate leader's own commitment
secret, commitment := crypto.Commit(crypto.Ed25519Curve)
consensus.secret[consensus.consensusID] = secret
if firstRound {
consensus.mutex.Lock()
defer consensus.mutex.Unlock()
(*consensus.commitments)[consensus.nodeID] = commitment
consensus.bitmap.SetKey(consensus.pubKey, true)
} else {
(*consensus.finalCommitments)[consensus.nodeID] = commitment
consensus.finalBitmap.SetKey(consensus.pubKey, true)
}
}
// processCommitMessage processes the commit message sent from validators
func (consensus *Consensus) processCommitMessage(message consensus_proto.Message, targetState State) {
// processPrepareMessage processes the prepare message sent from validators
func (consensus *Consensus) processPrepareMessage(message consensus_proto.Message) {
consensusID := message.ConsensusId
blockHash := message.BlockHash
validatorID := message.SenderId
commitment := message.Payload
prepareSig := message.Payload
signature := message.Signature
// Verify signature
@ -143,12 +120,15 @@ func (consensus *Consensus) processCommitMessage(message consensus_proto.Message
message.Signature = nil
messageBytes, err := protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Warn("Failed to marshal the announce message", "error", err)
}
if schnorr.Verify(crypto.Ed25519Curve, value.PubKey, messageBytes, signature) != nil {
utils.GetLogInstance().Warn("Received message with invalid signature", "validatorKey", consensus.leader.PubKey, "consensus", consensus)
return
utils.GetLogInstance().Warn("Failed to marshal the prepare message", "error", err)
}
_ = messageBytes
_ = signature
// TODO: verify message signature
//if schnorr.Verify(crypto.Ed25519Curve, value.PubKey, messageBytes, signature) != nil {
// consensus.Log.Warn("Received message with invalid signature", "validatorKey", consensus.leader.PubKey, "consensus", consensus)
// return
//}
// check consensus Id
consensus.mutex.Lock()
@ -163,27 +143,28 @@ func (consensus *Consensus) processCommitMessage(message consensus_proto.Message
return
}
commitments := consensus.commitments // targetState == ChallengeDone
bitmap := consensus.bitmap
if targetState == FinalChallengeDone {
commitments = consensus.finalCommitments
bitmap = consensus.finalBitmap
}
prepareSigs := consensus.prepareSigs
prepareBitmap := consensus.prepareBitmap
// proceed only when the message is not received before
_, ok = (*commitments)[validatorID]
_, ok = (*prepareSigs)[validatorID]
shouldProcess := !ok
if len((*commitments)) >= ((len(consensus.PublicKeys)*2)/3 + 1) {
if len((*prepareSigs)) >= ((len(consensus.PublicKeys)*2)/3 + 1) {
shouldProcess = false
}
if shouldProcess {
point := crypto.Ed25519Curve.Point()
point.UnmarshalBinary(commitment)
(*commitments)[validatorID] = point
utils.GetLogInstance().Debug("Received new commit message", "num", len(*commitments), "validatorID", validatorID, "PublicKeys", len(consensus.PublicKeys))
var sign bls.Sign
err := sign.Deserialize(prepareSig)
if err != nil {
utils.GetLogInstance().Error("Failed to deserialize bls signature", "validatorID", validatorID)
}
// TODO: check bls signature
(*prepareSigs)[validatorID] = &sign
utils.GetLogInstance().Debug("Received new prepare signature", "numReceivedSoFar", len(*prepareSigs), "validatorID", validatorID, "PublicKeys", len(consensus.PublicKeys))
// Set the bitmap indicate this validate signed.
bitmap.SetKey(value.PubKey, true)
prepareBitmap.SetKey(value.PubKey, true)
}
if !shouldProcess {
@ -191,33 +172,15 @@ func (consensus *Consensus) processCommitMessage(message consensus_proto.Message
return
}
if len((*commitments)) >= ((len(consensus.PublicKeys)*2)/3+1) && consensus.state < targetState {
utils.GetLogInstance().Debug("Enough commitments received with signatures", "num", len(*commitments), "state", consensus.state)
// Broadcast challenge
msgTypeToSend := consensus_proto.MessageType_CHALLENGE // targetState == ChallengeDone
if targetState == FinalChallengeDone {
msgTypeToSend = consensus_proto.MessageType_FINAL_CHALLENGE
}
msgToSend, challengeScalar, aggCommitment := consensus.constructChallengeMessage(msgTypeToSend)
bytes, err := challengeScalar.MarshalBinary()
if err != nil {
utils.GetLogInstance().Error("Failed to serialize challenge")
}
if msgTypeToSend == consensus_proto.MessageType_CHALLENGE {
copy(consensus.challenge[:], bytes)
consensus.aggregatedCommitment = aggCommitment
} else if msgTypeToSend == consensus_proto.MessageType_FINAL_CHALLENGE {
copy(consensus.finalChallenge[:], bytes)
consensus.aggregatedFinalCommitment = aggCommitment
}
targetState := PreparedDone
if len((*prepareSigs)) >= ((len(consensus.PublicKeys)*2)/3+1) && consensus.state < targetState {
utils.GetLogInstance().Debug("Enough commitments received with signatures", "num", len(*prepareSigs), "state", consensus.state)
// Add leader's response
consensus.responseByLeader(challengeScalar, targetState == ChallengeDone)
// Construct prepared message
msgToSend, aggSig := consensus.constructPreparedMessage()
consensus.aggregatedPrepareSig = aggSig
// Broadcast challenge message
// Broadcast prepared message
host.BroadcastMessageFromLeader(consensus.host, consensus.GetValidatorPeers(), msgToSend, consensus.OfflinePeers)
// Set state to targetState (ChallengeDone or FinalChallengeDone)
@ -225,29 +188,12 @@ func (consensus *Consensus) processCommitMessage(message consensus_proto.Message
}
}
// Leader commit to the message itself before receiving others commits
func (consensus *Consensus) responseByLeader(challenge kyber.Scalar, firstRound bool) {
// Generate leader's own commitment
response, err := crypto.Response(crypto.Ed25519Curve, consensus.priKey, consensus.secret[consensus.consensusID], challenge)
if err == nil {
if firstRound {
(*consensus.responses)[consensus.nodeID] = response
consensus.bitmap.SetKey(consensus.pubKey, true)
} else {
(*consensus.finalResponses)[consensus.nodeID] = response
consensus.finalBitmap.SetKey(consensus.pubKey, true)
}
} else {
utils.GetLogInstance().Warn("leader failed to generate response", "err", err)
}
}
// Processes the response message sent from validators
func (consensus *Consensus) processResponseMessage(message consensus_proto.Message, targetState State) {
// Processes the commit message sent from validators
func (consensus *Consensus) processCommitMessage(message consensus_proto.Message) {
consensusID := message.ConsensusId
blockHash := message.BlockHash
validatorID := message.SenderId
response := message.Payload
commitSig := message.Payload
signature := message.Signature
shouldProcess := true
@ -279,142 +225,95 @@ func (consensus *Consensus) processResponseMessage(message consensus_proto.Messa
message.Signature = nil
messageBytes, err := protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Warn("Failed to marshal the announce message", "error", err)
}
if schnorr.Verify(crypto.Ed25519Curve, value.PubKey, messageBytes, signature) != nil {
utils.GetLogInstance().Warn("Received message with invalid signature", "validatorKey", consensus.leader.PubKey, "consensus", consensus)
return
utils.GetLogInstance().Warn("Failed to marshal the commit message", "error", err)
}
_ = messageBytes
_ = signature
// TODO: verify message signature
//if schnorr.Verify(crypto.Ed25519Curve, value.PubKey, messageBytes, signature) != nil {
// consensus.Log.Warn("Received message with invalid signature", "validatorKey", consensus.leader.PubKey, "consensus", consensus)
// return
//}
commitments := consensus.commitments // targetState == CollectiveSigDone
responses := consensus.responses
bitmap := consensus.bitmap
if targetState == Finished {
commitments = consensus.finalCommitments
responses = consensus.finalResponses
bitmap = consensus.finalBitmap
}
commitSigs := consensus.commitSigs
commitBitmap := consensus.commitBitmap
// proceed only when the message is not received before
_, ok = (*responses)[validatorID]
_, ok = (*commitSigs)[validatorID]
shouldProcess = shouldProcess && !ok
if len((*responses)) >= ((len(consensus.PublicKeys)*2)/3 + 1) {
if len((*commitSigs)) >= ((len(consensus.PublicKeys)*2)/3 + 1) {
shouldProcess = false
}
if shouldProcess {
// verify the response matches the received commit
responseScalar := crypto.Ed25519Curve.Scalar()
responseScalar.UnmarshalBinary(response)
err := consensus.verifyResponse(commitments, responseScalar, validatorID)
var sign bls.Sign
err := sign.Deserialize(commitSig)
if err != nil {
utils.GetLogInstance().Warn("leader failed to verify the response", "error", err, "VID", strconv.Itoa(int(validatorID)))
shouldProcess = false
} else {
(*responses)[validatorID] = responseScalar
utils.GetLogInstance().Debug("Received new response message", "num", len(*responses), "validatorID", strconv.Itoa(int(validatorID)))
// Set the bitmap indicate this validate signed.
bitmap.SetKey(value.PubKey, true)
utils.GetLogInstance().Debug("Failed to deserialize bls signature", "validatorID", validatorID)
}
// TODO: check bls signature
(*commitSigs)[validatorID] = &sign
utils.GetLogInstance().Debug("Received new commit message", "numReceivedSoFar", len(*commitSigs), "validatorID", strconv.Itoa(int(validatorID)))
// Set the bitmap indicate this validate signed.
commitBitmap.SetKey(value.PubKey, true)
}
if !shouldProcess {
utils.GetLogInstance().Debug("Received new response message", "validatorID", strconv.Itoa(int(validatorID)))
utils.GetLogInstance().Debug("Received additional new commit message", "validatorID", strconv.Itoa(int(validatorID)))
return
}
threshold := 2
if targetState == Finished {
threshold = 1
}
if len(*responses) >= ((len(consensus.PublicKeys)*threshold)/3+1) && consensus.state != targetState {
if len(*responses) >= ((len(consensus.PublicKeys)*threshold)/3+1) && consensus.state != targetState {
utils.GetLogInstance().Debug("Enough responses received with signatures", "num", len(*responses), "state", consensus.state)
// Aggregate responses
responseScalars := []kyber.Scalar{}
for _, val := range *responses {
responseScalars = append(responseScalars, val)
}
aggregatedResponse, err := crypto.AggregateResponses(crypto.Ed25519Curve, responseScalars)
if err != nil {
utils.GetLogInstance().Error("Failed to aggregate responses")
return
}
aggregatedCommitment := consensus.aggregatedCommitment
if targetState == Finished {
aggregatedCommitment = consensus.aggregatedFinalCommitment
}
collectiveSigAndBitmap, err := crypto.Sign(crypto.Ed25519Curve, aggregatedCommitment, aggregatedResponse, bitmap)
if err != nil {
utils.GetLogInstance().Error("Failed to create collective signature")
return
}
utils.GetLogInstance().Info("CollectiveSig and Bitmap created.", "size", len(collectiveSigAndBitmap))
collectiveSig := [64]byte{}
copy(collectiveSig[:], collectiveSigAndBitmap[:64])
bitmap := collectiveSigAndBitmap[64:]
// Set state to CollectiveSigDone or Finished
consensus.state = targetState
if consensus.state != Finished {
// Start the second round of Cosi
msgToSend := consensus.constructCollectiveSigMessage(collectiveSig, bitmap)
host.BroadcastMessageFromLeader(consensus.host, consensus.GetValidatorPeers(), msgToSend, consensus.OfflinePeers)
consensus.commitByLeader(false)
} else {
var blockObj types.Block
err = rlp.DecodeBytes(consensus.block, &blockObj)
if err != nil {
utils.GetLogInstance().Debug("failed to construct the new block after consensus")
}
// Sign the block
copy(blockObj.Header().Signature[:], collectiveSig[:])
copy(blockObj.Header().Bitmap[:], bitmap)
consensus.OnConsensusDone(&blockObj)
select {
case consensus.VerifiedNewBlock <- &blockObj:
default:
utils.GetLogInstance().Info("[SYNC] consensus verified block send to chan failed", "blockHash", blockObj.Hash())
}
consensus.reportMetrics(blockObj)
// Dump new block into level db.
explorer.GetStorageInstance(consensus.leader.IP, consensus.leader.Port, true).Dump(&blockObj, consensus.consensusID)
// Reset state to Finished, and clear other data.
consensus.ResetState()
consensus.consensusID++
utils.GetLogInstance().Debug("HOORAY!!! CONSENSUS REACHED!!!", "consensusID", consensus.consensusID, "numOfSignatures", len(*responses))
// TODO: remove this temporary delay
time.Sleep(500 * time.Millisecond)
// Send signal to Node so the new block can be added and new round of consensus can be triggered
consensus.ReadySignal <- struct{}{}
}
targetState := CommitDone
if len(*commitSigs) >= ((len(consensus.PublicKeys)*threshold)/3+1) && consensus.state != targetState {
utils.GetLogInstance().Info("Enough commits received!", "num", len(*commitSigs), "state", consensus.state)
// Construct committed message
msgToSend, aggSig := consensus.constructCommittedMessage()
consensus.aggregatedPrepareSig = aggSig
// Broadcast committed message
host.BroadcastMessageFromLeader(consensus.host, consensus.GetValidatorPeers(), msgToSend, consensus.OfflinePeers)
// Set state to CollectiveSigDone or Finished
consensus.state = targetState
var blockObj types.Block
err = rlp.DecodeBytes(consensus.block, &blockObj)
if err != nil {
utils.GetLogInstance().Debug("failed to construct the new block after consensus")
}
}
}
func (consensus *Consensus) verifyResponse(commitments *map[uint32]kyber.Point, response kyber.Scalar, validatorID uint32) error {
if response.Equal(crypto.Ed25519Curve.Scalar()) {
return errors.New("response is zero valued")
}
_, ok := (*commitments)[validatorID]
if !ok {
return errors.New("no commit is received for the validator")
// Sign the block
copy(blockObj.Header().Signature[:], aggSig.Serialize()[:])
copy(blockObj.Header().Bitmap[:], commitBitmap.Bitmap)
consensus.OnConsensusDone(&blockObj)
consensus.state = targetState
select {
case consensus.VerifiedNewBlock <- &blockObj:
default:
utils.GetLogInstance().Info("[SYNC] consensus verified block send to chan failed", "blockHash", blockObj.Hash())
}
consensus.reportMetrics(blockObj)
// Dump new block into level db.
explorer.GetStorageInstance(consensus.leader.IP, consensus.leader.Port, true).Dump(&blockObj, consensus.consensusID)
// Reset state to Finished, and clear other data.
consensus.ResetState()
consensus.consensusID++
utils.GetLogInstance().Debug("HOORAY!!! CONSENSUS REACHED!!!", "consensusID", consensus.consensusID, "numOfSignatures", len(*commitSigs))
// TODO: remove this temporary delay
time.Sleep(500 * time.Millisecond)
// Send signal to Node so the new block can be added and new round of consensus can be triggered
consensus.ReadySignal <- struct{}{}
consensus.state = Finished
}
return nil
}
func (consensus *Consensus) reportMetrics(block types.Block) {
@ -442,7 +341,7 @@ func (consensus *Consensus) reportMetrics(block types.Block) {
txHashes = append(txHashes, hex.EncodeToString(txHash[:]))
}
metrics := map[string]interface{}{
"key": consensus.pubKey.String(),
"key": hex.EncodeToString(consensus.pubKey.Serialize()),
"tps": tps,
"txCount": numOfTxs,
"nodeCount": len(consensus.PublicKeys) + 1,

@ -2,13 +2,11 @@ package consensus
import (
"bytes"
"github.com/dedis/kyber"
"github.com/ethereum/go-ethereum/log"
protobuf "github.com/golang/protobuf/proto"
"github.com/harmony-one/bls/ffi/go/bls"
consensus_proto "github.com/harmony-one/harmony/api/consensus"
"github.com/harmony-one/harmony/api/proto"
"github.com/harmony-one/harmony/crypto"
bls_cosi "github.com/harmony-one/harmony/crypto/bls"
"github.com/harmony-one/harmony/internal/utils"
)
@ -41,14 +39,14 @@ func (consensus *Consensus) constructAnnounceMessage() []byte {
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal Announce message", "error", err)
}
utils.GetLogInstance().Info("New Announce", "NodeID", consensus.nodeID, "bitmap", consensus.bitmap)
utils.GetLogInstance().Info("New Announce", "NodeID", consensus.nodeID)
return proto.ConstructConsensusMessage(marshaledMessage)
}
// Construct the challenge message, returning challenge message in bytes, challenge scalar and aggregated commmitment point.
func (consensus *Consensus) constructChallengeMessage(msgType consensus_proto.MessageType) ([]byte, kyber.Scalar, kyber.Point) {
// Construct the prepared message, returning prepared message in bytes.
func (consensus *Consensus) constructPreparedMessage() ([]byte, *bls.Sign) {
message := consensus_proto.Message{}
message.Type = msgType
message.Type = consensus_proto.MessageType_PREPARED
// 4 byte consensus id
message.ConsensusId = consensus.consensusID
@ -62,56 +60,37 @@ func (consensus *Consensus) constructChallengeMessage(msgType consensus_proto.Me
//// Payload
buffer := bytes.NewBuffer([]byte{})
commitmentsMap := consensus.commitments // msgType == Challenge
bitmap := consensus.bitmap
if msgType == consensus_proto.MessageType_FINAL_CHALLENGE {
commitmentsMap = consensus.finalCommitments
bitmap = consensus.finalBitmap
}
// 33 byte aggregated commit
commitments := make([]kyber.Point, 0)
for _, val := range *commitmentsMap {
commitments = append(commitments, val)
}
aggCommitment, aggCommitmentBytes := getAggregatedCommit(commitments)
buffer.Write(aggCommitmentBytes)
// 33 byte aggregated key
buffer.Write(getAggregatedKey(bitmap))
// 48 bytes aggregated signature
aggSig := bls_cosi.AggregateSig(consensus.GetPrepareSigsArray())
buffer.Write(aggSig.Serialize())
// 32 byte challenge
challengeScalar := getChallenge(aggCommitment, bitmap.AggregatePublic, message.BlockHash)
bytes, err := challengeScalar.MarshalBinary()
if err != nil {
log.Error("Failed to serialize challenge")
}
buffer.Write(bytes)
// Bitmap
buffer.Write(consensus.prepareBitmap.Bitmap)
message.Payload = buffer.Bytes()
//// END Payload
// TODO: use custom serialization method rather than protobuf
marshaledMessage, err := protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal Challenge message", "error", err)
utils.GetLogInstance().Debug("Failed to marshal Prepared message", "error", err)
}
// 64 byte of signature on previous data
// 48 byte of signature on previous data
signature := consensus.signMessage(marshaledMessage)
message.Signature = signature
marshaledMessage, err = protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal Challenge message", "error", err)
utils.GetLogInstance().Debug("Failed to marshal Prepared message", "error", err)
}
utils.GetLogInstance().Info("New Challenge", "NodeID", consensus.nodeID, "bitmap", consensus.bitmap)
return proto.ConstructConsensusMessage(marshaledMessage), challengeScalar, aggCommitment
utils.GetLogInstance().Info("New Prepared Message", "NodeID", consensus.nodeID, "bitmap", consensus.prepareBitmap)
return proto.ConstructConsensusMessage(marshaledMessage), aggSig
}
// Construct the collective signature message
func (consensus *Consensus) constructCollectiveSigMessage(collectiveSig [64]byte, bitmap []byte) []byte {
// Construct the committed message, returning committed message in bytes.
func (consensus *Consensus) constructCommittedMessage() ([]byte, *bls.Sign) {
message := consensus_proto.Message{}
message.Type = consensus_proto.MessageType_COLLECTIVE_SIG
message.Type = consensus_proto.MessageType_COMMITTED
// 4 byte consensus id
message.ConsensusId = consensus.consensusID
@ -124,52 +103,29 @@ func (consensus *Consensus) constructCollectiveSigMessage(collectiveSig [64]byte
//// Payload
buffer := bytes.NewBuffer([]byte{})
// 64 byte collective signature
buffer.Write(collectiveSig[:])
// 48 bytes aggregated signature
aggSig := bls_cosi.AggregateSig(consensus.GetCommitSigsArray())
buffer.Write(aggSig.Serialize())
// N byte bitmap
buffer.Write(bitmap)
// Bitmap
buffer.Write(consensus.commitBitmap.Bitmap)
message.Payload = buffer.Bytes()
//// END Payload
// TODO: use custom serialization method rather than protobuf
marshaledMessage, err := protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal Challenge message", "error", err)
utils.GetLogInstance().Debug("Failed to marshal Committed message", "error", err)
}
// 64 byte of signature on previous data
// 48 byte of signature on previous data
signature := consensus.signMessage(marshaledMessage)
message.Signature = signature
marshaledMessage, err = protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal Challenge message", "error", err)
}
utils.GetLogInstance().Info("New CollectiveSig", "NodeID", consensus.nodeID, "bitmap", consensus.bitmap)
return proto.ConstructConsensusMessage(marshaledMessage)
}
func getAggregatedCommit(commitments []kyber.Point) (commitment kyber.Point, bytes []byte) {
aggCommitment := crypto.AggregateCommitmentsOnly(crypto.Ed25519Curve, commitments)
bytes, err := aggCommitment.MarshalBinary()
if err != nil {
panic("Failed to deserialize the aggregated commitment")
}
return aggCommitment, append(bytes[:], byte(0))
}
func getAggregatedKey(bitmap *crypto.Mask) []byte {
bytes, err := bitmap.AggregatePublic.MarshalBinary()
if err != nil {
panic("Failed to deserialize the aggregated key")
}
return append(bytes[:], byte(0))
}
func getChallenge(aggCommitment, aggKey kyber.Point, message []byte) kyber.Scalar {
challenge, err := crypto.Challenge(crypto.Ed25519Curve, aggCommitment, aggKey, message)
if err != nil {
log.Error("Failed to generate challenge")
utils.GetLogInstance().Debug("Failed to marshal Committed message", "error", err)
}
return challenge
utils.GetLogInstance().Info("New Prepared Message", "NodeID", consensus.nodeID, "bitmap", consensus.commitBitmap)
return proto.ConstructConsensusMessage(marshaledMessage), aggSig
}

@ -1,14 +1,11 @@
package consensus
import (
"github.com/harmony-one/harmony/internal/utils"
"testing"
"github.com/harmony-one/harmony/p2p/p2pimpl"
consensus_proto "github.com/harmony-one/harmony/api/consensus"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/crypto/pki"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p"
)
@ -24,22 +21,17 @@ func TestConstructAnnounceMessage(test *testing.T) {
consensus.blockHash = [32]byte{}
msg := consensus.constructAnnounceMessage()
if len(msg) != 109 {
if len(msg) != 93 {
test.Errorf("Annouce message is not constructed in the correct size: %d", len(msg))
}
}
func TestConstructChallengeMessage(test *testing.T) {
leaderPriKey := crypto.Ed25519Curve.Scalar()
priKeyInBytes := crypto.HashSha256("12")
leaderPriKey.UnmarshalBinary(priKeyInBytes[:])
leaderPubKey := pki.GetPublicKeyFromScalar(leaderPriKey)
func TestConstructPreparedMessage(test *testing.T) {
leaderPriKey, leaderPubKey := utils.GenKeyBLS("127.0.0.1", "6000")
leader := p2p.Peer{IP: "127.0.0.1", Port: "6000", PubKey: leaderPubKey}
validatorPriKey := crypto.Ed25519Curve.Scalar()
priKeyInBytes = crypto.HashSha256("12")
validatorPriKey.UnmarshalBinary(priKeyInBytes[:])
validatorPubKey := pki.GetPublicKeyFromScalar(leaderPriKey)
validatorPriKey, validatorPubKey := utils.GenKeyBLS("127.0.0.1", "5555")
validator := p2p.Peer{IP: "127.0.0.1", Port: "5555", PubKey: validatorPubKey}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
host, err := p2pimpl.NewHost(&leader, priKey)
@ -48,14 +40,16 @@ func TestConstructChallengeMessage(test *testing.T) {
}
consensus := New(host, "0", []p2p.Peer{leader, validator}, leader)
consensus.blockHash = [32]byte{}
(*consensus.commitments)[0] = leaderPubKey
(*consensus.commitments)[1] = validatorPubKey
consensus.bitmap.SetKey(leaderPubKey, true)
consensus.bitmap.SetKey(validatorPubKey, true)
msg, _, _ := consensus.constructChallengeMessage(consensus_proto.MessageType_CHALLENGE)
message := "test string"
(*consensus.prepareSigs)[0] = leaderPriKey.Sign(message)
(*consensus.prepareSigs)[1] = validatorPriKey.Sign(message)
consensus.prepareBitmap.SetKey(leaderPubKey, true)
consensus.prepareBitmap.SetKey(validatorPubKey, true)
msg, _ := consensus.constructPreparedMessage()
if len(msg) != 209 {
if len(msg) != 144 {
test.Errorf("Challenge message is not constructed in the correct size: %d", len(msg))
}
}

@ -3,8 +3,10 @@ package consensus
import (
"fmt"
"crypto/sha256"
"github.com/ethereum/go-ethereum/rlp"
"github.com/golang/mock/gomock"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/internal/utils"
mock_host "github.com/harmony-one/harmony/p2p/host/mock"
"github.com/stretchr/testify/assert"
@ -13,20 +15,20 @@ import (
"github.com/harmony-one/harmony/p2p/p2pimpl"
consensus_proto "github.com/harmony-one/harmony/api/consensus"
"github.com/harmony-one/harmony/p2p"
)
var (
ip = "127.0.0.1"
ip = "127.0.0.1"
blockHash = sha256.Sum256([]byte("test"))
)
func TestProcessMessageLeaderCommit(test *testing.T) {
func TestProcessMessageLeaderPrepare(test *testing.T) {
ctrl := gomock.NewController(test)
defer ctrl.Finish()
leader := p2p.Peer{IP: ip, Port: "7777"}
_, leader.PubKey = utils.GenKey(leader.IP, leader.Port)
_, leader.PubKey = utils.GenKeyBLS(leader.IP, leader.Port)
validators := make([]p2p.Peer, 3)
hosts := make([]p2p.Host, 3)
@ -34,7 +36,7 @@ func TestProcessMessageLeaderCommit(test *testing.T) {
for i := 0; i < 3; i++ {
port := fmt.Sprintf("%d", 7788+i)
validators[i] = p2p.Peer{IP: ip, Port: port, ValidatorID: i + 1}
_, validators[i].PubKey = utils.GenKey(validators[i].IP, validators[i].Port)
_, validators[i].PubKey = utils.GenKeyBLS(validators[i].IP, validators[i].Port)
}
m := mock_host.NewMockHost(ctrl)
@ -44,7 +46,7 @@ func TestProcessMessageLeaderCommit(test *testing.T) {
m.EXPECT().SendMessage(gomock.Any(), gomock.Any()).Times(3)
consensusLeader := New(m, "0", validators, leader)
consensusLeader.blockHash = [32]byte{}
consensusLeader.blockHash = blockHash
consensusValidators := make([]*Consensus, 3)
for i := 0; i < 3; i++ {
@ -56,22 +58,22 @@ func TestProcessMessageLeaderCommit(test *testing.T) {
hosts[i] = host
consensusValidators[i] = New(hosts[i], "0", validators, leader)
consensusValidators[i].blockHash = [32]byte{}
_, msg := consensusValidators[i].constructCommitMessage(consensus_proto.MessageType_COMMIT)
consensusValidators[i].blockHash = blockHash
msg := consensusValidators[i].constructPrepareMessage()
consensusLeader.ProcessMessageLeader(msg[1:])
}
assert.Equal(test, ChallengeDone, consensusLeader.state)
assert.Equal(test, PreparedDone, consensusLeader.state)
time.Sleep(1 * time.Second)
}
func TestProcessMessageLeaderResponse(test *testing.T) {
func TestProcessMessageLeaderCommit(test *testing.T) {
ctrl := gomock.NewController(test)
defer ctrl.Finish()
leader := p2p.Peer{IP: ip, Port: "8889"}
_, leader.PubKey = utils.GenKey(leader.IP, leader.Port)
_, leader.PubKey = utils.GenKeyBLS(leader.IP, leader.Port)
validators := make([]p2p.Peer, 3)
hosts := make([]p2p.Host, 3)
@ -79,14 +81,14 @@ func TestProcessMessageLeaderResponse(test *testing.T) {
for i := 0; i < 3; i++ {
port := fmt.Sprintf("%d", 8788+i)
validators[i] = p2p.Peer{IP: ip, Port: port, ValidatorID: i + 1}
_, validators[i].PubKey = utils.GenKey(validators[i].IP, validators[i].Port)
_, validators[i].PubKey = utils.GenKeyBLS(validators[i].IP, validators[i].Port)
}
m := mock_host.NewMockHost(ctrl)
// Asserts that the first and only call to Bar() is passed 99.
// Anything else will fail.
m.EXPECT().GetSelfPeer().Return(leader)
m.EXPECT().SendMessage(gomock.Any(), gomock.Any()).Times(6)
m.EXPECT().SendMessage(gomock.Any(), gomock.Any()).Times(3)
for i := 0; i < 3; i++ {
priKey, _, _ := utils.GenKeyP2P(validators[i].IP, validators[i].Port)
@ -98,22 +100,25 @@ func TestProcessMessageLeaderResponse(test *testing.T) {
}
consensusLeader := New(m, "0", validators, leader)
consensusLeader.blockHash = [32]byte{}
consensusLeader.state = PreparedDone
consensusLeader.blockHash = blockHash
consensusLeader.OnConsensusDone = func(newBlock *types.Block) {}
consensusLeader.block, _ = rlp.EncodeToBytes(types.NewBlock(&types.Header{}, nil, nil))
consensusValidators := make([]*Consensus, 3)
for i := 0; i < 3; i++ {
consensusValidators[i] = New(hosts[i], "0", validators, leader)
consensusValidators[i].blockHash = [32]byte{}
_, msg := consensusValidators[i].constructCommitMessage(consensus_proto.MessageType_COMMIT)
consensusLeader.ProcessMessageLeader(msg[1:])
}
go func() {
<-consensusLeader.ReadySignal
<-consensusLeader.ReadySignal
}()
for i := 0; i < 3; i++ {
msg := consensusValidators[i].constructResponseMessage(consensus_proto.MessageType_RESPONSE, crypto.Ed25519Curve.Scalar().One())
consensusValidators[i] = New(hosts[i], "0", validators, leader)
consensusValidators[i].blockHash = blockHash
msg := consensusValidators[i].constructCommitMessage()
consensusLeader.ProcessMessageLeader(msg[1:])
}
assert.Equal(test, CollectiveSigDone, consensusLeader.state)
assert.Equal(test, Finished, consensusLeader.state)
time.Sleep(1 * time.Second)
}

@ -7,13 +7,10 @@ type State int
const (
Finished State = iota
AnnounceDone
PrepareDone
PreparedDone
CommitDone
ChallengeDone
ResponseDone
CollectiveSigDone
FinalCommitDone
FinalChallengeDone
FinalResponseDone
CommittedDone
)
// Returns string name for the State enum
@ -21,15 +18,12 @@ func (state State) String() string {
names := [...]string{
"Finished",
"AnnounceDone",
"PrepareDone",
"PreparedDone",
"CommitDone",
"ChallengeDone",
"ResponseDone",
"CollectiveSigDone",
"FinalCommitDone",
"FinalChallengeDone",
"FinalResponseDone"}
"CommittedDone"}
if state < Finished || state > FinalResponseDone {
if state < Finished || state > CommittedDone {
return "Unknown"
}
return names[state]

@ -35,11 +35,11 @@ func TestNew(test *testing.T) {
}
func TestRemovePeers(t *testing.T) {
_, pk1 := utils.GenKey("1", "1")
_, pk2 := utils.GenKey("2", "2")
_, pk3 := utils.GenKey("3", "3")
_, pk4 := utils.GenKey("4", "4")
_, pk5 := utils.GenKey("5", "5")
_, pk1 := utils.GenKeyBLS("1", "1")
_, pk2 := utils.GenKeyBLS("2", "2")
_, pk3 := utils.GenKeyBLS("3", "3")
_, pk4 := utils.GenKeyBLS("4", "4")
_, pk5 := utils.GenKeyBLS("5", "5")
p1 := p2p.Peer{IP: "127.0.0.1", Port: "19901", PubKey: pk1}
p2 := p2p.Peer{IP: "127.0.0.1", Port: "19902", PubKey: pk2}

@ -3,13 +3,10 @@ package consensus
import (
"bytes"
"github.com/dedis/kyber/sign/schnorr"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
protobuf "github.com/golang/protobuf/proto"
consensus_proto "github.com/harmony-one/harmony/api/consensus"
"github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/internal/attack"
"github.com/harmony-one/harmony/internal/utils"
)
@ -25,12 +22,10 @@ func (consensus *Consensus) ProcessMessageValidator(payload []byte) {
switch message.Type {
case consensus_proto.MessageType_ANNOUNCE:
consensus.processAnnounceMessage(message)
case consensus_proto.MessageType_CHALLENGE:
consensus.processChallengeMessage(message, ResponseDone)
case consensus_proto.MessageType_FINAL_CHALLENGE:
consensus.processChallengeMessage(message, FinalResponseDone)
case consensus_proto.MessageType_COLLECTIVE_SIG:
consensus.processCollectiveSigMessage(message)
case consensus_proto.MessageType_PREPARED:
consensus.processPreparedMessage(message)
case consensus_proto.MessageType_COMMITTED:
consensus.processCommittedMessage(message)
default:
utils.GetLogInstance().Error("Unexpected message type", "msgType", message.Type, "consensus", consensus)
}
@ -62,10 +57,13 @@ func (consensus *Consensus) processAnnounceMessage(message consensus_proto.Messa
if err != nil {
utils.GetLogInstance().Warn("Failed to marshal the announce message", "error", err)
}
if schnorr.Verify(crypto.Ed25519Curve, consensus.leader.PubKey, messageBytes, signature) != nil {
utils.GetLogInstance().Warn("Received message with invalid signature", "leaderKey", consensus.leader.PubKey, "consensus", consensus)
return
}
_ = signature
_ = messageBytes
// TODO: verify message signature
//if schnorr.Verify(crypto.Ed25519Curve, consensus.leader.PubKey, messageBytes, signature) != nil {
// consensus.Log.Warn("Received message with invalid signature", "leaderKey", consensus.leader.PubKey, "consensus", consensus)
// return
//}
// check block header is valid
var blockObj types.Block
@ -101,20 +99,19 @@ func (consensus *Consensus) processAnnounceMessage(message consensus_proto.Messa
return
}
// Commit and store the commit
secret, msgToSend := consensus.constructCommitMessage(consensus_proto.MessageType_COMMIT)
consensus.secret[consensusID] = secret
// Construct and send prepare message
msgToSend := consensus.constructPrepareMessage()
consensus.SendMessage(consensus.leader, msgToSend)
// utils.GetLogInstance().Warn("Sending Commit to leader", "state", targetState)
// Set state to CommitDone
consensus.state = CommitDone
consensus.state = PrepareDone
}
// Processes the challenge message sent from the leader
func (consensus *Consensus) processChallengeMessage(message consensus_proto.Message, targetState State) {
utils.GetLogInstance().Info("Received Challenge Message", "nodeID", consensus.nodeID)
// Processes the prepared message sent from the leader
func (consensus *Consensus) processPreparedMessage(message consensus_proto.Message) {
utils.GetLogInstance().Info("Received Prepared Message", "nodeID", consensus.nodeID)
consensusID := message.ConsensusId
blockHash := message.BlockHash
@ -123,19 +120,13 @@ func (consensus *Consensus) processChallengeMessage(message consensus_proto.Mess
signature := message.Signature
//#### Read payload data
// TODO: use BLS-based multi-sig
offset := 0
// 33 byte of aggregated commit
aggreCommit := messagePayload[offset : offset+33]
offset += 33
// 33 byte of aggregated key
aggreKey := messagePayload[offset : offset+33]
offset += 33
// 48 byte of multi-sig
multiSig := messagePayload[offset : offset+48]
offset += 48
// 32 byte of challenge
challenge := messagePayload[offset : offset+32]
offset += 32
// bitmap
bitmap := messagePayload[offset:]
// Update readyByConsensus for attack.
attack.GetInstance().UpdateConsensusReady(consensusID)
@ -154,10 +145,13 @@ func (consensus *Consensus) processChallengeMessage(message consensus_proto.Mess
if err != nil {
utils.GetLogInstance().Warn("Failed to marshal the announce message", "error", err)
}
if schnorr.Verify(crypto.Ed25519Curve, consensus.leader.PubKey, messageBytes, signature) != nil {
utils.GetLogInstance().Warn("Received message with invalid signature", "leaderKey", consensus.leader.PubKey, "consensus", consensus)
return
}
_ = signature
_ = messageBytes
// TODO: verify message signature
//if schnorr.Verify(crypto.Ed25519Curve, consensus.leader.PubKey, messageBytes, signature) != nil {
// consensus.Log.Warn("Received message with invalid signature", "leaderKey", consensus.leader.PubKey, "consensus", consensus)
// return
//}
// Add attack model of IncorrectResponse.
if attack.GetInstance().IncorrectResponse() {
@ -174,94 +168,22 @@ func (consensus *Consensus) processChallengeMessage(message consensus_proto.Mess
return
}
aggCommitment := crypto.Ed25519Curve.Point()
aggCommitment.UnmarshalBinary(aggreCommit[:32])
aggKey := crypto.Ed25519Curve.Point()
aggKey.UnmarshalBinary(aggreKey[:32])
reconstructedChallenge, err := crypto.Challenge(crypto.Ed25519Curve, aggCommitment, aggKey, blockHash)
if err != nil {
log.Error("Failed to reconstruct the challenge from commits and keys")
return
}
// For now, simply return the private key of this node.
receivedChallenge := crypto.Ed25519Curve.Scalar()
err = receivedChallenge.UnmarshalBinary(challenge)
if err != nil {
log.Error("Failed to deserialize challenge", "err", err)
return
}
if !reconstructedChallenge.Equal(receivedChallenge) {
log.Error("The challenge doesn't match the commitments and keys")
return
}
response, err := crypto.Response(crypto.Ed25519Curve, consensus.priKey, consensus.secret[consensusID], receivedChallenge)
if err != nil {
log.Warn("validator failed to generate response", "err", err, "priKey", consensus.priKey, "nodeID", consensus.nodeID, "secret", consensus.secret[consensusID])
return
}
_ = multiSig
_ = bitmap
// TODO: verify multi signature
msgTypeToSend := consensus_proto.MessageType_RESPONSE
if targetState == FinalResponseDone {
msgTypeToSend = consensus_proto.MessageType_FINAL_RESPONSE
}
msgToSend := consensus.constructResponseMessage(msgTypeToSend, response)
// Construct and send the commit message
msgToSend := consensus.constructCommitMessage()
consensus.SendMessage(consensus.leader, msgToSend)
// utils.GetLogInstance().Warn("Sending Response to leader", "state", targetState)
// Set state to target state (ResponseDone, FinalResponseDone)
consensus.state = targetState
if consensus.state == FinalResponseDone {
// TODO: the block catch up logic is a temporary workaround for full failure node catchup. Implement the full node catchup logic
// The logic is to roll up to the latest blocks one by one to try catching up with the leader.
for {
val, ok := consensus.blocksReceived[consensus.consensusID]
if ok {
delete(consensus.blocksReceived, consensus.consensusID)
consensus.blockHash = [32]byte{}
delete(consensus.secret, consensusID)
consensus.consensusID = consensusID + 1 // roll up one by one, until the next block is not received yet.
var blockObj types.Block
err := rlp.DecodeBytes(val.block, &blockObj)
if err != nil {
utils.GetLogInstance().Warn("Unparseable block header data", "error", err)
return
}
if err != nil {
utils.GetLogInstance().Debug("failed to construct the new block after consensus")
}
// check block data (transactions
if !consensus.BlockVerifier(&blockObj) {
utils.GetLogInstance().Debug("[WARNING] Block content is not verified successfully", "consensusID", consensus.consensusID)
return
}
utils.GetLogInstance().Info("Finished Response. Adding block to chain", "numTx", len(blockObj.Transactions()))
consensus.OnConsensusDone(&blockObj)
select {
case consensus.VerifiedNewBlock <- &blockObj:
default:
utils.GetLogInstance().Info("[SYNC] consensus verified block send to chan failed", "blockHash", blockObj.Hash())
continue
}
} else {
break
}
}
}
consensus.state = CommitDone
}
// Processes the collective signature message sent from the leader
func (consensus *Consensus) processCollectiveSigMessage(message consensus_proto.Message) {
// Processes the committed message sent from the leader
func (consensus *Consensus) processCommittedMessage(message consensus_proto.Message) {
utils.GetLogInstance().Warn("Received Prepared Message", "nodeID", consensus.nodeID)
consensusID := message.ConsensusId
blockHash := message.BlockHash
leaderID := message.SenderId
@ -269,11 +191,18 @@ func (consensus *Consensus) processCollectiveSigMessage(message consensus_proto.
signature := message.Signature
//#### Read payload data
collectiveSig := messagePayload[0:64]
bitmap := messagePayload[64:]
//#### END: Read payload data
offset := 0
// 48 byte of multi-sig
multiSig := messagePayload[offset : offset+48]
offset += 48
// Verify block data
// bitmap
bitmap := messagePayload[offset:]
// Update readyByConsensus for attack.
attack.GetInstance().UpdateConsensusReady(consensusID)
// Verify block data and the aggregated signatures
// check leader Id
myLeaderID := utils.GetUniqueIDFromPeer(consensus.leader)
if uint32(leaderID) != myLeaderID {
@ -287,17 +216,13 @@ func (consensus *Consensus) processCollectiveSigMessage(message consensus_proto.
if err != nil {
utils.GetLogInstance().Warn("Failed to marshal the announce message", "error", err)
}
if schnorr.Verify(crypto.Ed25519Curve, consensus.leader.PubKey, messageBytes, signature) != nil {
utils.GetLogInstance().Warn("Received message with invalid signature", "leaderKey", consensus.leader.PubKey, "consensus", consensus)
return
}
// Verify collective signature
err = crypto.Verify(crypto.Ed25519Curve, consensus.PublicKeys, blockHash, append(collectiveSig, bitmap...), crypto.NewThresholdPolicy((2*len(consensus.PublicKeys)/3)+1))
if err != nil {
utils.GetLogInstance().Warn("Failed to verify the collective sig message", "consensusID", consensusID, "err", err, "bitmap", bitmap, "NodeID", consensus.nodeID, "#PK", len(consensus.PublicKeys))
return
}
_ = signature
_ = messageBytes
// TODO: verify message signature
//if schnorr.Verify(crypto.Ed25519Curve, consensus.leader.PubKey, messageBytes, signature) != nil {
// consensus.Log.Warn("Received message with invalid signature", "leaderKey", consensus.leader.PubKey, "consensus", consensus)
// return
//}
// Add attack model of IncorrectResponse.
if attack.GetInstance().IncorrectResponse() {
@ -305,6 +230,8 @@ func (consensus *Consensus) processCollectiveSigMessage(message consensus_proto.
return
}
consensus.mutex.Lock()
defer consensus.mutex.Unlock()
// check consensus Id
if consensusID != consensus.consensusID {
// hack for new node state syncing
@ -319,12 +246,54 @@ func (consensus *Consensus) processCollectiveSigMessage(message consensus_proto.
return
}
secret, msgToSend := consensus.constructCommitMessage(consensus_proto.MessageType_FINAL_COMMIT)
// Store the commitment secret
consensus.secret[consensusID] = secret
_ = multiSig
_ = bitmap
// TODO: verify multi signature
// Construct and send the prepare message
msgToSend := consensus.constructPrepareMessage()
consensus.SendMessage(consensus.leader, msgToSend)
// Set state to CommitDone
consensus.state = FinalCommitDone
consensus.state = PrepareDone
// TODO: the block catch up logic is a temporary workaround for full failure node catchup. Implement the full node catchup logic
// The logic is to roll up to the latest blocks one by one to try catching up with the leader.
for {
val, ok := consensus.blocksReceived[consensus.consensusID]
if ok {
delete(consensus.blocksReceived, consensus.consensusID)
consensus.blockHash = [32]byte{}
consensus.consensusID = consensusID + 1 // roll up one by one, until the next block is not received yet.
var blockObj types.Block
err := rlp.DecodeBytes(val.block, &blockObj)
if err != nil {
utils.GetLogInstance().Warn("Unparseable block header data", "error", err)
return
}
if err != nil {
utils.GetLogInstance().Debug("failed to construct the new block after consensus")
}
// check block data (transactions
if !consensus.BlockVerifier(&blockObj) {
utils.GetLogInstance().Debug("[WARNING] Block content is not verified successfully", "consensusID", consensus.consensusID)
return
}
utils.GetLogInstance().Info("Finished Response. Adding block to chain", "numTx", len(blockObj.Transactions()))
consensus.OnConsensusDone(&blockObj)
select {
case consensus.VerifiedNewBlock <- &blockObj:
default:
utils.GetLogInstance().Info("[SYNC] consensus verified block send to chan failed", "blockHash", blockObj.Hash())
continue
}
} else {
break
}
}
}

@ -1,18 +1,16 @@
package consensus
import (
"github.com/dedis/kyber"
protobuf "github.com/golang/protobuf/proto"
consensus_proto "github.com/harmony-one/harmony/api/consensus"
"github.com/harmony-one/harmony/api/proto"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/internal/utils"
)
// Construct the commit message to send to leader (assumption the consensus data is already verified)
func (consensus *Consensus) constructCommitMessage(msgType consensus_proto.MessageType) (secret kyber.Scalar, commitMsg []byte) {
// Construct the prepare message to send to leader (assumption the consensus data is already verified)
func (consensus *Consensus) constructPrepareMessage() []byte {
message := consensus_proto.Message{}
message.Type = msgType
message.Type = consensus_proto.MessageType_PREPARE
// 4 byte consensus id
message.ConsensusId = consensus.consensusID
@ -23,17 +21,15 @@ func (consensus *Consensus) constructCommitMessage(msgType consensus_proto.Messa
// 4 byte sender id
message.SenderId = uint32(consensus.nodeID)
// 32 byte of commit
secret, commitment := crypto.Commit(crypto.Ed25519Curve)
bytes, err := commitment.MarshalBinary()
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal commit", "error", err)
// 48 byte of bls signature
sign := consensus.priKey.SignHash(message.BlockHash)
if sign != nil {
message.Payload = consensus.priKey.SignHash(message.BlockHash).Serialize()
}
message.Payload = bytes
marshaledMessage, err := protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal Announce message", "error", err)
utils.GetLogInstance().Debug("Failed to marshal Prepare message", "error", err)
}
// 64 byte of signature on previous data
signature := consensus.signMessage(marshaledMessage)
@ -41,16 +37,16 @@ func (consensus *Consensus) constructCommitMessage(msgType consensus_proto.Messa
marshaledMessage, err = protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal Announce message", "error", err)
utils.GetLogInstance().Debug("Failed to marshal Prepare message", "error", err)
}
return secret, proto.ConstructConsensusMessage(marshaledMessage)
return proto.ConstructConsensusMessage(marshaledMessage)
}
// Construct the response message to send to leader (assumption the consensus data is already verified)
func (consensus *Consensus) constructResponseMessage(msgType consensus_proto.MessageType, response kyber.Scalar) []byte {
// Construct the commit message to send to leader (assumption the consensus data is already verified)
func (consensus *Consensus) constructCommitMessage() []byte {
message := consensus_proto.Message{}
message.Type = msgType
message.Type = consensus_proto.MessageType_COMMIT
// 4 byte consensus id
message.ConsensusId = consensus.consensusID
@ -61,15 +57,16 @@ func (consensus *Consensus) constructResponseMessage(msgType consensus_proto.Mes
// 4 byte sender id
message.SenderId = uint32(consensus.nodeID)
bytes, err := response.MarshalBinary()
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal response", "error", err)
// 48 byte of bls signature
// TODO: sign on the prepared message hash, rather than the block hash
sign := consensus.priKey.SignHash(message.BlockHash)
if sign != nil {
message.Payload = consensus.priKey.SignHash(message.BlockHash).Serialize()
}
message.Payload = bytes
marshaledMessage, err := protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal Announce message", "error", err)
utils.GetLogInstance().Debug("Failed to marshal Commit message", "error", err)
}
// 64 byte of signature on previous data
signature := consensus.signMessage(marshaledMessage)
@ -77,7 +74,8 @@ func (consensus *Consensus) constructResponseMessage(msgType consensus_proto.Mes
marshaledMessage, err = protobuf.Marshal(&message)
if err != nil {
utils.GetLogInstance().Debug("Failed to marshal Announce message", "error", err)
utils.GetLogInstance().Debug("Failed to marshal Commit message", "error", err)
}
return proto.ConstructConsensusMessage(marshaledMessage)
}

@ -5,13 +5,11 @@ import (
"github.com/harmony-one/harmony/p2p/p2pimpl"
consensus_proto "github.com/harmony-one/harmony/api/consensus"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p"
)
func TestConstructCommitMessage(test *testing.T) {
func TestConstructPrepareMessage(test *testing.T) {
leader := p2p.Peer{IP: "127.0.0.1", Port: "9992"}
validator := p2p.Peer{IP: "127.0.0.1", Port: "9995"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
@ -21,14 +19,14 @@ func TestConstructCommitMessage(test *testing.T) {
}
consensus := New(host, "0", []p2p.Peer{leader, validator}, leader)
consensus.blockHash = [32]byte{}
_, msg := consensus.constructCommitMessage(consensus_proto.MessageType_COMMIT)
msg := consensus.constructPrepareMessage()
if len(msg) != 143 {
test.Errorf("Commit message is not constructed in the correct size: %d", len(msg))
if len(msg) != 93 {
test.Errorf("Prepare message is not constructed in the correct size: %d", len(msg))
}
}
func TestConstructResponseMessage(test *testing.T) {
func TestConstructCommitMessage(test *testing.T) {
leader := p2p.Peer{IP: "127.0.0.1", Port: "9902"}
validator := p2p.Peer{IP: "127.0.0.1", Port: "9905"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
@ -38,9 +36,9 @@ func TestConstructResponseMessage(test *testing.T) {
}
consensus := New(host, "0", []p2p.Peer{leader, validator}, leader)
consensus.blockHash = [32]byte{}
msg := consensus.constructResponseMessage(consensus_proto.MessageType_RESPONSE, crypto.Ed25519Curve.Scalar())
msg := consensus.constructCommitMessage()
if len(msg) != 143 {
test.Errorf("Response message is not constructed in the correct size: %d", len(msg))
if len(msg) != 93 {
test.Errorf("Commit message is not constructed in the correct size: %d", len(msg))
}
}

@ -21,14 +21,14 @@ func TestProcessMessageValidatorAnnounce(test *testing.T) {
defer ctrl.Finish()
leader := p2p.Peer{IP: "127.0.0.1", Port: "9982"}
_, leader.PubKey = utils.GenKey(leader.IP, leader.Port)
_, leader.PubKey = utils.GenKeyBLS(leader.IP, leader.Port)
validator1 := p2p.Peer{IP: "127.0.0.1", Port: "9984", ValidatorID: 1}
_, validator1.PubKey = utils.GenKey(validator1.IP, validator1.Port)
_, validator1.PubKey = utils.GenKeyBLS(validator1.IP, validator1.Port)
validator2 := p2p.Peer{IP: "127.0.0.1", Port: "9986", ValidatorID: 2}
_, validator2.PubKey = utils.GenKey(validator2.IP, validator2.Port)
_, validator2.PubKey = utils.GenKeyBLS(validator2.IP, validator2.Port)
validator3 := p2p.Peer{IP: "127.0.0.1", Port: "9988", ValidatorID: 3}
_, validator3.PubKey = utils.GenKey(validator3.IP, validator3.Port)
_, validator3.PubKey = utils.GenKeyBLS(validator3.IP, validator3.Port)
m := mock_host.NewMockHost(ctrl)
// Asserts that the first and only call to Bar() is passed 99.
@ -65,24 +65,24 @@ func TestProcessMessageValidatorAnnounce(test *testing.T) {
copy(consensusValidator1.blockHash[:], hashBytes[:])
consensusValidator1.processAnnounceMessage(message)
assert.Equal(test, CommitDone, consensusValidator1.state)
assert.Equal(test, PrepareDone, consensusValidator1.state)
time.Sleep(1 * time.Second)
}
func TestProcessMessageValidatorChallenge(test *testing.T) {
func TestProcessMessageValidatorPrepared(test *testing.T) {
ctrl := gomock.NewController(test)
defer ctrl.Finish()
leader := p2p.Peer{IP: "127.0.0.1", Port: "7782"}
_, leader.PubKey = utils.GenKey(leader.IP, leader.Port)
_, leader.PubKey = utils.GenKeyBLS(leader.IP, leader.Port)
validator1 := p2p.Peer{IP: "127.0.0.1", Port: "7784", ValidatorID: 1}
_, validator1.PubKey = utils.GenKey(validator1.IP, validator1.Port)
_, validator1.PubKey = utils.GenKeyBLS(validator1.IP, validator1.Port)
validator2 := p2p.Peer{IP: "127.0.0.1", Port: "7786", ValidatorID: 2}
_, validator2.PubKey = utils.GenKey(validator2.IP, validator2.Port)
_, validator2.PubKey = utils.GenKeyBLS(validator2.IP, validator2.Port)
validator3 := p2p.Peer{IP: "127.0.0.1", Port: "7788", ValidatorID: 3}
_, validator3.PubKey = utils.GenKey(validator3.IP, validator3.Port)
_, validator3.PubKey = utils.GenKeyBLS(validator3.IP, validator3.Port)
m := mock_host.NewMockHost(ctrl)
// Asserts that the first and only call to Bar() is passed 99.
@ -102,8 +102,8 @@ func TestProcessMessageValidatorChallenge(test *testing.T) {
copy(consensusLeader.blockHash[:], hashBytes[:])
commitMsg := consensusLeader.constructAnnounceMessage()
challengeMsg, _, _ := consensusLeader.constructChallengeMessage(consensus_proto.MessageType_CHALLENGE)
announceMsg := consensusLeader.constructAnnounceMessage()
preparedMsg, _ := consensusLeader.constructPreparedMessage()
if err != nil {
test.Errorf("Failed to unmarshal message payload")
@ -115,15 +115,15 @@ func TestProcessMessageValidatorChallenge(test *testing.T) {
}
message := consensus_proto.Message{}
err = message.XXX_Unmarshal(commitMsg[1:])
err = message.XXX_Unmarshal(announceMsg[1:])
copy(consensusValidator1.blockHash[:], hashBytes[:])
consensusValidator1.processAnnounceMessage(message)
message = consensus_proto.Message{}
err = message.XXX_Unmarshal(challengeMsg[1:])
consensusValidator1.processChallengeMessage(message, ResponseDone)
err = message.XXX_Unmarshal(preparedMsg[1:])
consensusValidator1.processPreparedMessage(message)
assert.Equal(test, ResponseDone, consensusValidator1.state)
assert.Equal(test, CommitDone, consensusValidator1.state)
time.Sleep(1 * time.Second)
}

@ -0,0 +1,225 @@
package bls
import (
"errors"
"fmt"
"github.com/harmony-one/bls/ffi/go/bls"
)
// AggregateSig aggregates all the BLS signature into a single multi-signature.
func AggregateSig(sigs []*bls.Sign) *bls.Sign {
var aggregatedSig bls.Sign
for _, sig := range sigs {
aggregatedSig.Add(sig)
}
return &aggregatedSig
}
// Mask represents a cosigning participation bitmask.
type Mask struct {
Bitmap []byte
publics []*bls.PublicKey
AggregatePublic *bls.PublicKey
}
// NewMask returns a new participation bitmask for cosigning where all
// cosigners are disabled by default. If a public key is given it verifies that
// it is present in the list of keys and sets the corresponding index in the
// bitmask to 1 (enabled).
func NewMask(publics []*bls.PublicKey, myKey *bls.PublicKey) (*Mask, error) {
m := &Mask{
publics: publics,
}
m.Bitmap = make([]byte, m.Len())
m.AggregatePublic = &bls.PublicKey{}
if myKey != nil {
found := false
for i, key := range publics {
if key.IsEqual(myKey) {
m.SetBit(i, true)
found = true
break
}
}
if !found {
return nil, errors.New("key not found")
}
}
return m, nil
}
// Mask returns a copy of the participation bitmask.
func (m *Mask) Mask() []byte {
clone := make([]byte, len(m.Bitmap))
copy(clone[:], m.Bitmap)
return clone
}
// Len returns the Bitmap length in bytes.
func (m *Mask) Len() int {
return (len(m.publics) + 7) >> 3
}
// SetMask sets the participation bitmask according to the given byte slice
// interpreted in little-endian order, i.e., bits 0-7 of byte 0 correspond to
// cosigners 0-7, bits 0-7 of byte 1 correspond to cosigners 8-15, etc.
func (m *Mask) SetMask(mask []byte) error {
if m.Len() != len(mask) {
return fmt.Errorf("mismatching Bitmap lengths")
}
for i := range m.publics {
byt := i >> 3
msk := byte(1) << uint(i&7)
if ((m.Bitmap[byt] & msk) == 0) && ((mask[byt] & msk) != 0) {
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 0 to 1
m.AggregatePublic.Add(m.publics[i])
}
if ((m.Bitmap[byt] & msk) != 0) && ((mask[byt] & msk) == 0) {
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 1 to 0
m.AggregatePublic.Sub(m.publics[i])
}
}
return nil
}
// SetBit enables (enable: true) or disables (enable: false) the bit
// in the participation Bitmap of the given cosigner.
func (m *Mask) SetBit(i int, enable bool) error {
if i >= len(m.publics) {
return errors.New("index out of range")
}
byt := i >> 3
msk := byte(1) << uint(i&7)
if ((m.Bitmap[byt] & msk) == 0) && enable {
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 0 to 1
m.AggregatePublic.Add(m.publics[i])
}
if ((m.Bitmap[byt] & msk) != 0) && !enable {
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 1 to 0
m.AggregatePublic.Sub(m.publics[i])
}
return nil
}
// GetPubKeyFromMask will return pubkeys which masked either zero or one depending on the flag
// it is used to show which signers are signed or not in the cosign message
func (m *Mask) GetPubKeyFromMask(flag bool) []*bls.PublicKey {
pubKeys := []*bls.PublicKey{}
for i := range m.publics {
byt := i >> 3
msk := byte(1) << uint(i&7)
if flag == true {
if (m.Bitmap[byt] & msk) != 0 {
pubKeys = append(pubKeys, m.publics[i])
}
} else {
if (m.Bitmap[byt] & msk) == 0 {
pubKeys = append(pubKeys, m.publics[i])
}
}
}
return pubKeys
}
// IndexEnabled checks whether the given index is enabled in the Bitmap or not.
func (m *Mask) IndexEnabled(i int) (bool, error) {
if i >= len(m.publics) {
return false, errors.New("index out of range")
}
byt := i >> 3
msk := byte(1) << uint(i&7)
return ((m.Bitmap[byt] & msk) != 0), nil
}
// KeyEnabled checks whether the index, corresponding to the given key, is
// enabled in the Bitmap or not.
func (m *Mask) KeyEnabled(public *bls.PublicKey) (bool, error) {
for i, key := range m.publics {
if key.IsEqual(public) {
return m.IndexEnabled(i)
}
}
return false, errors.New("key not found")
}
// SetKey set the bit in the Bitmap for the given cosigner
func (m *Mask) SetKey(public *bls.PublicKey, enable bool) error {
for i, key := range m.publics {
if key.IsEqual(public) {
return m.SetBit(i, enable)
}
}
return errors.New("key not found")
}
// CountEnabled returns the number of enabled nodes in the CoSi participation
// Bitmap.
func (m *Mask) CountEnabled() int {
// hw is hamming weight
hw := 0
for i := range m.publics {
byt := i >> 3
msk := byte(1) << uint(i&7)
if (m.Bitmap[byt] & msk) != 0 {
hw++
}
}
return hw
}
// CountTotal returns the total number of nodes this CoSi instance knows.
func (m *Mask) CountTotal() int {
return len(m.publics)
}
// AggregateMasks computes the bitwise OR of the two given participation masks.
func AggregateMasks(a, b []byte) ([]byte, error) {
if len(a) != len(b) {
return nil, errors.New("mismatching Bitmap lengths")
}
m := make([]byte, len(a))
for i := range m {
m[i] = a[i] | b[i]
}
return m, nil
}
// Policy represents a fully customizable cosigning policy deciding what
// cosigner sets are and aren't sufficient for a collective signature to be
// considered acceptable to a verifier. The Check method may inspect the set of
// participants that cosigned by invoking cosi.Mask and/or cosi.MaskBit, and may
// use any other relevant contextual information (e.g., how security-critical
// the operation relying on the collective signature is) in determining whether
// the collective signature was produced by an acceptable set of cosigners.
type Policy interface {
Check(m *Mask) bool
}
// CompletePolicy is the default policy requiring that all participants have
// cosigned to make a collective signature valid.
type CompletePolicy struct {
}
// Check verifies that all participants have contributed to a collective
// signature.
func (p CompletePolicy) Check(m *Mask) bool {
return m.CountEnabled() == m.CountTotal()
}
// ThresholdPolicy allows to specify a simple t-of-n policy requring that at
// least the given threshold number of participants t have cosigned to make a
// collective signature valid.
type ThresholdPolicy struct {
thold int
}
// NewThresholdPolicy returns a new ThresholdPolicy with the given threshold.
func NewThresholdPolicy(thold int) *ThresholdPolicy {
return &ThresholdPolicy{thold: thold}
}
// Check verifies that at least a threshold number of participants have
// contributed to a collective signature.
func (p ThresholdPolicy) Check(m *Mask) bool {
return m.CountEnabled() >= p.thold
}

@ -2,18 +2,16 @@ package pki
import (
"crypto/sha256"
"encoding/binary"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/dedis/kyber"
"github.com/ethereum/go-ethereum/log"
"github.com/harmony-one/harmony/crypto"
)
// GetAddressFromPublicKey returns address given a public key.
func GetAddressFromPublicKey(pubKey kyber.Point) [20]byte {
bytes, err := pubKey.MarshalBinary()
if err != nil {
log.Error("Failed to serialize challenge")
}
func GetAddressFromPublicKey(pubKey *bls.PublicKey) [20]byte {
bytes := pubKey.Serialize()
address := [20]byte{}
hash := sha256.Sum256(bytes)
copy(address[:], hash[12:])
@ -21,18 +19,23 @@ func GetAddressFromPublicKey(pubKey kyber.Point) [20]byte {
}
// GetAddressFromPrivateKey returns address given a private key.
func GetAddressFromPrivateKey(priKey kyber.Scalar) [20]byte {
return GetAddressFromPublicKey(GetPublicKeyFromScalar(priKey))
func GetAddressFromPrivateKey(priKey *bls.SecretKey) [20]byte {
return GetAddressFromPublicKey(priKey.GetPublicKey())
}
// GetAddressFromPrivateKeyBytes returns address from private key in bytes.
func GetAddressFromPrivateKeyBytes(priKey [32]byte) [20]byte {
return GetAddressFromPublicKey(GetPublicKeyFromScalar(crypto.Ed25519Curve.Scalar().SetBytes(priKey[:])))
var privateKey bls.SecretKey
privateKey.SetLittleEndian(priKey[:])
return GetAddressFromPublicKey(privateKey.GetPublicKey())
}
// GetAddressFromInt is the temporary helper function for benchmark use
func GetAddressFromInt(value int) [20]byte {
return GetAddressFromPublicKey(GetPublicKeyFromScalar(GetPrivateKeyScalarFromInt(value)))
priKey := [32]byte{}
binary.LittleEndian.PutUint32(priKey[:], uint32(value))
return GetAddressFromPrivateKeyBytes(priKey)
}
// GetPrivateKeyScalarFromInt return private key scalar.
@ -40,6 +43,15 @@ func GetPrivateKeyScalarFromInt(value int) kyber.Scalar {
return crypto.Ed25519Curve.Scalar().SetInt64(int64(value))
}
// GetBLSPrivateKeyFromInt returns bls private key
func GetBLSPrivateKeyFromInt(value int) *bls.SecretKey {
priKey := [32]byte{}
binary.LittleEndian.PutUint32(priKey[:], uint32(value))
var privateKey bls.SecretKey
privateKey.SetLittleEndian(priKey[:])
return &privateKey
}
// GetPrivateKeyFromInt returns private key in bytes given an interger.
func GetPrivateKeyFromInt(value int) [32]byte {
priKey, err := crypto.Ed25519Curve.Scalar().SetInt64(int64(value)).MarshalBinary()

@ -1,6 +1,8 @@
package pki
import (
"encoding/binary"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/crypto"
"reflect"
"testing"
@ -8,12 +10,13 @@ import (
)
func TestGetAddressFromPublicKey(test *testing.T) {
suite := crypto.Ed25519Curve
t := time.Now().UnixNano()
scalar := suite.Scalar().SetInt64(t)
pubKey := GetPublicKeyFromScalar(scalar)
addr1 := GetAddressFromPublicKey(pubKey)
addr2 := GetAddressFromPrivateKey(scalar)
priKey := [32]byte{}
binary.LittleEndian.PutUint32(priKey[:], uint32(t))
var privateKey bls.SecretKey
privateKey.SetLittleEndian(priKey[:])
addr1 := GetAddressFromPublicKey(privateKey.GetPublicKey())
addr2 := GetAddressFromPrivateKey(&privateKey)
if !reflect.DeepEqual(addr1, addr2) {
test.Error("two public address should be equal")
}

@ -1,13 +1,12 @@
package beaconchain
import (
"github.com/harmony-one/bls/ffi/go/bls"
"math/rand"
"os"
"strconv"
"sync"
"github.com/dedis/kyber"
"github.com/harmony-one/harmony/api/proto/bcconn"
proto_identity "github.com/harmony-one/harmony/api/proto/identity"
"github.com/harmony-one/harmony/api/proto/node"
@ -44,7 +43,7 @@ type BCInfo struct {
type BeaconChain struct {
BCInfo BCInfo
ShardLeaderMap map[int]*node.Info
PubKey kyber.Point
PubKey *bls.PublicKey
host p2p.Host
state BCState
rpcServer *beaconchain.Server
@ -105,10 +104,10 @@ func New(numShards int, ip, port string, key p2p_crypto.PrivKey) *BeaconChain {
return &bc
}
func generateBCKey() kyber.Point {
func generateBCKey() *bls.PublicKey {
r := rand.Intn(1000)
priKey := pki.GetPrivateKeyFromInt(r)
pubkey := pki.GetPublicKeyFromPrivateKey(priKey)
priKey := pki.GetBLSPrivateKeyFromInt(r)
pubkey := priKey.GetPublicKey()
return pubkey
}

@ -3,17 +3,15 @@ package newnode
import (
"errors"
"fmt"
"github.com/harmony-one/bls/ffi/go/bls"
"os"
"strconv"
"time"
"github.com/dedis/kyber"
"github.com/ethereum/go-ethereum/log"
"github.com/harmony-one/harmony/api/proto/bcconn"
proto_identity "github.com/harmony-one/harmony/api/proto/identity"
proto_node "github.com/harmony-one/harmony/api/proto/node"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p"
"github.com/harmony-one/harmony/p2p/host"
@ -32,8 +30,8 @@ type NewNode struct {
isLeader bool
Self p2p.Peer
Leaders map[uint32]p2p.Peer
PubK kyber.Point
priK kyber.Scalar
PubK *bls.PublicKey
priK *bls.SecretKey
log log.Logger
SetInfo chan bool
host p2p.Host
@ -41,7 +39,7 @@ type NewNode struct {
// New candidatenode initialization
func New(ip string, port string, nodePk p2p_crypto.PrivKey) *NewNode {
priKey, pubKey := utils.GenKey(ip, port)
priKey, pubKey := utils.GenKeyBLS(ip, port)
var node NewNode
var err error
node.PubK = pubKey
@ -77,7 +75,7 @@ func (node NewNode) String() string {
// RequestBeaconChain requests beacon chain for identity data
func (node *NewNode) requestBeaconChain(BCPeer p2p.Peer) (err error) {
node.log.Info("connecting to beacon chain now ...")
pubk, err := node.PubK.MarshalBinary()
pubk := node.PubK.Serialize()
if err != nil {
node.log.Error("Could not Marshall public key into binary")
}
@ -130,8 +128,8 @@ func (node *NewNode) processShardInfo(msgPayload []byte) bool {
}
leaderPeer.Addrs = append(leaderPeer.Addrs, targetAddr)
leaderPeer.PubKey = crypto.Ed25519Curve.Point()
err = leaderPeer.PubKey.UnmarshalBinary(v.PubKey[:])
leaderPeer.PubKey = &bls.PublicKey{}
err = leaderPeer.PubKey.Deserialize(v.PubKey[:])
if err != nil {
node.log.Error("Could not unmarshall leaders public key from binary to kyber.point")
}

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/binary"
"encoding/json"
"github.com/harmony-one/bls/ffi/go/bls"
"io"
"log"
mrand "math/rand"
@ -73,6 +74,21 @@ func GenKey(ip, port string) (kyber.Scalar, kyber.Point) {
return priKey, pubKey
}
// GenKeyBLS generates a bls key pair given ip and port.
func GenKeyBLS(ip, port string) (*bls.SecretKey, *bls.PublicKey) {
nodeIDBytes := make([]byte, 32)
binary.LittleEndian.PutUint32(nodeIDBytes, GetUniqueIDFromIPPort(ip, port))
privateKey := bls.SecretKey{}
err := privateKey.SetLittleEndian(nodeIDBytes)
if err != nil {
log.Print("failed to set private key", err)
}
priKey := &privateKey
pubKey := privateKey.GetPublicKey()
return priKey, pubKey
}
// GenKeyP2P generates a pair of RSA keys used in libp2p host
func GenKeyP2P(ip, port string) (p2p_crypto.PrivKey, p2p_crypto.PubKey, error) {
r := mrand.New(mrand.NewSource(int64(GetUniqueIDFromIPPort(ip, port))))

@ -3,18 +3,17 @@ package node
import (
"bytes"
"fmt"
"github.com/harmony-one/bls/ffi/go/bls"
"os"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/dedis/kyber"
"github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/harmony/api/proto"
proto_identity "github.com/harmony-one/harmony/api/proto/identity"
proto_node "github.com/harmony-one/harmony/api/proto/node"
"github.com/harmony-one/harmony/core/types"
hmy_crypto "github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/crypto/pki"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p"
@ -303,8 +302,8 @@ func (node *Node) pingMessageHandler(msgPayload []byte) int {
peer.PeerID = ping.Node.PeerID
peer.ValidatorID = ping.Node.ValidatorID
peer.PubKey = hmy_crypto.Ed25519Curve.Point()
err = peer.PubKey.UnmarshalBinary(ping.Node.PubKey[:])
peer.PubKey = &bls.PublicKey{}
err = peer.PubKey.Deserialize(ping.Node.PubKey[:])
if err != nil {
utils.GetLogInstance().Error("UnmarshalBinary Failed", "error", err)
return -1
@ -358,8 +357,8 @@ func (node *Node) pongMessageHandler(msgPayload []byte) int {
peer.ValidatorID = p.ValidatorID
peer.PeerID = p.PeerID
peer.PubKey = hmy_crypto.Ed25519Curve.Point()
err = peer.PubKey.UnmarshalBinary(p.PubKey[:])
peer.PubKey = &bls.PublicKey{}
err = peer.PubKey.Deserialize(p.PubKey[:])
if err != nil {
utils.GetLogInstance().Error("UnmarshalBinary Failed", "error", err)
continue
@ -374,17 +373,17 @@ func (node *Node) pongMessageHandler(msgPayload []byte) int {
// Reset Validator PublicKeys every time we receive PONG message from Leader
// The PublicKeys has to be idential across the shard on every node
// TODO (lc): we need to handle RemovePeer situation
publicKeys := make([]kyber.Point, 0)
publicKeys := make([]*bls.PublicKey, 0)
// Create the the PubKey from the []byte sent from leader
for _, k := range pong.PubKeys {
key := hmy_crypto.Ed25519Curve.Point()
err = key.UnmarshalBinary(k[:])
key := bls.PublicKey{}
err = key.Deserialize(k[:])
if err != nil {
utils.GetLogInstance().Error("UnmarshalBinary Failed PubKeys", "error", err)
continue
}
publicKeys = append(publicKeys, key)
publicKeys = append(publicKeys, &key)
}
if node.State == NodeWaitToJoin {

@ -10,7 +10,7 @@ import (
)
func TestNodeStreamHandler(t *testing.T) {
_, pubKey := utils.GenKey("1", "2")
_, pubKey := utils.GenKeyBLS("1", "2")
leader := p2p.Peer{IP: "127.0.0.1", Port: "8882", PubKey: pubKey}
validator := p2p.Peer{IP: "127.0.0.1", Port: "8885"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
@ -34,7 +34,7 @@ func TestNodeStreamHandler(t *testing.T) {
}
func TestAddNewBlock(t *testing.T) {
_, pubKey := utils.GenKey("1", "2")
_, pubKey := utils.GenKeyBLS("1", "2")
leader := p2p.Peer{IP: "127.0.0.1", Port: "9882", PubKey: pubKey}
validator := p2p.Peer{IP: "127.0.0.1", Port: "9885"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
@ -57,7 +57,7 @@ func TestAddNewBlock(t *testing.T) {
}
func TestVerifyNewBlock(t *testing.T) {
_, pubKey := utils.GenKey("1", "2")
_, pubKey := utils.GenKeyBLS("1", "2")
leader := p2p.Peer{IP: "127.0.0.1", Port: "8882", PubKey: pubKey}
validator := p2p.Peer{IP: "127.0.0.1", Port: "8885"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")

@ -8,7 +8,6 @@ import (
proto_node "github.com/harmony-one/harmony/api/proto/node"
"github.com/harmony-one/harmony/consensus"
"github.com/harmony-one/harmony/crypto"
"github.com/harmony-one/harmony/crypto/pki"
"github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p"
@ -16,7 +15,7 @@ import (
)
func TestNewNode(t *testing.T) {
_, pubKey := utils.GenKey("1", "2")
_, pubKey := utils.GenKeyBLS("1", "2")
leader := p2p.Peer{IP: "127.0.0.1", Port: "8882", PubKey: pubKey}
validator := p2p.Peer{IP: "127.0.0.1", Port: "8885"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
@ -40,7 +39,7 @@ func TestNewNode(t *testing.T) {
}
func TestGetSyncingPeers(t *testing.T) {
_, pubKey := utils.GenKey("1", "2")
_, pubKey := utils.GenKeyBLS("1", "2")
leader := p2p.Peer{IP: "127.0.0.1", Port: "8882", PubKey: pubKey}
validator := p2p.Peer{IP: "127.0.0.1", Port: "8885"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
@ -66,11 +65,8 @@ func TestGetSyncingPeers(t *testing.T) {
}
func TestAddPeers(t *testing.T) {
priKey1 := crypto.Ed25519Curve.Scalar().SetInt64(int64(333))
pubKey1 := pki.GetPublicKeyFromScalar(priKey1)
priKey2 := crypto.Ed25519Curve.Scalar().SetInt64(int64(999))
pubKey2 := pki.GetPublicKeyFromScalar(priKey2)
pubKey1 := pki.GetBLSPrivateKeyFromInt(333).GetPublicKey()
pubKey2 := pki.GetBLSPrivateKeyFromInt(444).GetPublicKey()
peers1 := []*p2p.Peer{
&p2p.Peer{
@ -88,7 +84,7 @@ func TestAddPeers(t *testing.T) {
ValidatorID: 2,
},
}
_, pubKey := utils.GenKey("1", "2")
_, pubKey := utils.GenKeyBLS("1", "2")
leader := p2p.Peer{IP: "127.0.0.1", Port: "8982", PubKey: pubKey}
validator := p2p.Peer{IP: "127.0.0.1", Port: "8985"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")
@ -112,8 +108,7 @@ func TestAddPeers(t *testing.T) {
}
func sendPingMessage(node *Node, leader p2p.Peer) {
priKey1 := crypto.Ed25519Curve.Scalar().SetInt64(int64(333))
pubKey1 := pki.GetPublicKeyFromScalar(priKey1)
pubKey1 := pki.GetBLSPrivateKeyFromInt(333).GetPublicKey()
p1 := p2p.Peer{
IP: "127.0.0.1",
@ -132,15 +127,13 @@ func sendPingMessage(node *Node, leader p2p.Peer) {
}
func sendPongMessage(node *Node, leader p2p.Peer) {
priKey1 := crypto.Ed25519Curve.Scalar().SetInt64(int64(333))
pubKey1 := pki.GetPublicKeyFromScalar(priKey1)
pubKey1 := pki.GetBLSPrivateKeyFromInt(333).GetPublicKey()
pubKey2 := pki.GetBLSPrivateKeyFromInt(444).GetPublicKey()
p1 := p2p.Peer{
IP: "127.0.0.1",
Port: "9998",
PubKey: pubKey1,
}
priKey2 := crypto.Ed25519Curve.Scalar().SetInt64(int64(999))
pubKey2 := pki.GetPublicKeyFromScalar(priKey2)
p2 := p2p.Peer{
IP: "127.0.0.1",
Port: "9999",
@ -165,7 +158,7 @@ func exitServer() {
}
func TestPingPongHandler(t *testing.T) {
_, pubKey := utils.GenKey("127.0.0.1", "8881")
_, pubKey := utils.GenKeyBLS("127.0.0.1", "8881")
leader := p2p.Peer{IP: "127.0.0.1", Port: "8881", PubKey: pubKey}
// validator := p2p.Peer{IP: "127.0.0.1", Port: "9991"}
priKey, _, _ := utils.GenKeyP2P("127.0.0.1", "9902")

@ -2,11 +2,11 @@ package p2p
import (
"fmt"
"github.com/harmony-one/bls/ffi/go/bls"
"net"
"github.com/dedis/kyber"
peer "github.com/libp2p/go-libp2p-peer"
multiaddr "github.com/multiformats/go-multiaddr"
"github.com/libp2p/go-libp2p-peer"
"github.com/multiformats/go-multiaddr"
)
// StreamHandler handles incoming p2p message.
@ -16,7 +16,7 @@ type StreamHandler func(Stream)
type Peer struct {
IP string // IP address of the peer
Port string // Port number of the peer
PubKey kyber.Point // Public key of the peer, used for consensus signing
PubKey *bls.PublicKey // Public key of the peer, used for consensus signing
Ready bool // Ready is true if the peer is ready to join consensus. (FIXME: deprecated)
ValidatorID int // -1 is the default value, means not assigned any validator ID in the shard
Addrs []multiaddr.Multiaddr // MultiAddress of the peer

@ -13,6 +13,21 @@ GOARCH=amd64
FOLDER=/${WHOAMI:-$USER}
RACE=
HMY_PATH=$GOPATH/src/github.com/harmony-one
export CGO_CFLAGS="-I$HMY_PATH/bls/include -I$HMY_PATH/mcl/include"
export CGO_LDFLAGS="-L$HMY_PATH/bls/lib"
export LD_LIBRARY_PATH=$HMY_PATH/bls/lib:$HMY_PATH/mcl/lib
OS=$(uname -s)
case $OS in
Darwin)
export CGO_CFLAGS="-I$HMY_PATH/bls/include -I$HMY_PATH/mcl/include -I/usr/local/opt/openssl/include"
export CGO_LDFLAGS="-L$HMY_PATH/bls/lib -L/usr/local/opt/openssl/lib"
export LD_LIBRARY_PATH=$HMY_PATH/bls/lib:$HMY_PATH/mcl/lib:/usr/local/opt/openssl/lib
export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
;;
esac
if [ "$(uname -s)" == "Darwin" ]; then
MD5='md5 -r'
GOOS=darwin

@ -36,6 +36,21 @@ dirnames() {
go_dirs="${tmpdir}/go_dirs.txt"
dirnames < "${go_files}" | sort -u -t/ > "${go_dirs}"
HMY_PATH=$GOPATH/src/github.com/harmony-one
export CGO_CFLAGS="-I$HMY_PATH/bls/include -I$HMY_PATH/mcl/include"
export CGO_LDFLAGS="-L$HMY_PATH/bls/lib"
export LD_LIBRARY_PATH=$HMY_PATH/bls/lib:$HMY_PATH/mcl/lib
OS=$(uname -s)
case $OS in
Darwin)
export CGO_CFLAGS="-I$HMY_PATH/bls/include -I$HMY_PATH/mcl/include -I/usr/local/opt/openssl/include"
export CGO_LDFLAGS="-L$HMY_PATH/bls/lib -L/usr/local/opt/openssl/lib"
export LD_LIBRARY_PATH=$HMY_PATH/bls/lib:$HMY_PATH/mcl/lib:/usr/local/opt/openssl/lib
export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
;;
esac
echo "Running go test..."
if go test -v -count=1 ./...
then

@ -1,6 +1,10 @@
package main
import (
"crypto/ecdsa"
"crypto/rand"
"encoding/hex"
"github.com/ethereum/go-ethereum/crypto"
"github.com/harmony-one/bls/ffi/go/bls"
"log"
"time"
@ -13,9 +17,17 @@ func main() {
startTime := time.Now()
for i := 0; i < 1000; i++ {
bls.Init(bls.BLS12_381)
var sec bls.SecretKey
sec.SetByCSPRNG()
if i == 0 {
testECKey, _ := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
log.Printf("Secret Key: 0x%s", sec.GetHexString())
log.Printf("Secret Key: 0x%s", hex.EncodeToString(sec.GetLittleEndian()))
log.Printf("Secret Key Length: %d", len(sec.GetLittleEndian()))
log.Printf("Secret Key: 0x%s", hex.EncodeToString(testECKey.D.Bytes()))
log.Printf("Secret Key Length: %d", len(testECKey.D.Bytes()))
}
if i == 0 {
aggSig = sec.Sign(m)
aggPub = sec.GetPublicKey()
@ -26,8 +38,8 @@ func main() {
}
endTime := time.Now()
log.Printf("Time required to sign 1000 messages and aggregate 1000 pub keys and signatures: %f seconds", endTime.Sub(startTime).Seconds())
log.Printf("Aggregate Signature: 0x%x", aggSig.GetHexString())
log.Printf("Aggregate Public Key: 0x%x", aggPub.GetHexString())
log.Printf("Aggregate Signature: 0x%s, length: %d", aggSig.GetHexString(), len(aggSig.Serialize()))
log.Printf("Aggregate Public Key: 0x%s, length: %d", aggPub.GetHexString(), len(aggPub.Serialize()))
startTime = time.Now()
if !aggSig.Verify(aggPub, m) {

@ -3,6 +3,21 @@
ROOT=$(dirname $0)/..
USER=$(whoami)
HMY_PATH=$GOPATH/src/github.com/harmony-one
export CGO_CFLAGS="-I$HMY_PATH/bls/include -I$HMY_PATH/mcl/include"
export CGO_LDFLAGS="-L$HMY_PATH/bls/lib"
export LD_LIBRARY_PATH=$HMY_PATH/bls/lib:$HMY_PATH/mcl/lib
OS=$(uname -s)
case $OS in
Darwin)
export CGO_CFLAGS="-I$HMY_PATH/bls/include -I$HMY_PATH/mcl/include -I/usr/local/opt/openssl/include"
export CGO_LDFLAGS="-L$HMY_PATH/bls/lib -L/usr/local/opt/openssl/lib"
export LD_LIBRARY_PATH=$HMY_PATH/bls/lib:$HMY_PATH/mcl/lib:/usr/local/opt/openssl/lib
export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
;;
esac
set -x
set -eo pipefail

Loading…
Cancel
Save