Basic multi-sig logic

pull/3374/head
Rongjian Lan 4 years ago
parent cb237f1b42
commit dc24b3ad52
  1. 149
      api/proto/message/message.pb.go
  2. 1
      api/proto/message/message.proto
  3. 18
      consensus/checks.go
  4. 3
      consensus/consensus.go
  5. 20
      consensus/consensus_service.go
  6. 2
      consensus/consensus_service_test.go
  7. 16
      consensus/consensus_v2.go
  8. 81
      consensus/construct.go
  9. 8
      consensus/construct_test.go
  10. 185
      consensus/double_sign.go
  11. 71
      consensus/fbft_log.go
  12. 67
      consensus/leader.go
  13. 9
      consensus/quorum/one-node-one-vote.go
  14. 31
      consensus/quorum/one-node-staked-vote.go
  15. 2
      consensus/quorum/one-node-staked-vote_test.go
  16. 56
      consensus/quorum/quorum.go
  17. 5
      consensus/threshold.go
  18. 77
      consensus/validator.go
  19. 58
      consensus/view_change.go
  20. 15
      consensus/votepower/roster.go
  21. 57
      crypto/bls/mask.go
  22. 6
      crypto/bls/mask_test.go
  23. 20
      crypto/pki/utils.go
  24. 21
      crypto/pki/utils_test.go
  25. 50
      node/node.go
  26. 4
      node/node_explorer.go
  27. 116
      staking/slash/double-sign.go
  28. 25
      staking/slash/double-sign_test.go
  29. 2
      staking/slash/test/copy.go
  30. 4
      staking/slash/test/copy_test.go

@ -650,13 +650,14 @@ type ConsensusRequest struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
ViewId uint64 `protobuf:"varint,1,opt,name=view_id,json=viewId,proto3" json:"view_id,omitempty"` ViewId uint64 `protobuf:"varint,1,opt,name=view_id,json=viewId,proto3" json:"view_id,omitempty"`
BlockNum uint64 `protobuf:"varint,2,opt,name=block_num,json=blockNum,proto3" json:"block_num,omitempty"` BlockNum uint64 `protobuf:"varint,2,opt,name=block_num,json=blockNum,proto3" json:"block_num,omitempty"`
ShardId uint32 `protobuf:"varint,3,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"` ShardId uint32 `protobuf:"varint,3,opt,name=shard_id,json=shardId,proto3" json:"shard_id,omitempty"`
BlockHash []byte `protobuf:"bytes,4,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` BlockHash []byte `protobuf:"bytes,4,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"`
Block []byte `protobuf:"bytes,5,opt,name=block,proto3" json:"block,omitempty"` Block []byte `protobuf:"bytes,5,opt,name=block,proto3" json:"block,omitempty"`
SenderPubkey []byte `protobuf:"bytes,6,opt,name=sender_pubkey,json=senderPubkey,proto3" json:"sender_pubkey,omitempty"` SenderPubkey []byte `protobuf:"bytes,6,opt,name=sender_pubkey,json=senderPubkey,proto3" json:"sender_pubkey,omitempty"`
Payload []byte `protobuf:"bytes,7,opt,name=payload,proto3" json:"payload,omitempty"` Payload []byte `protobuf:"bytes,7,opt,name=payload,proto3" json:"payload,omitempty"`
SenderPubkeyBitmap []byte `protobuf:"bytes,8,opt,name=sender_pubkey_bitmap,json=senderPubkeyBitmap,proto3" json:"sender_pubkey_bitmap,omitempty"`
} }
func (x *ConsensusRequest) Reset() { func (x *ConsensusRequest) Reset() {
@ -740,6 +741,13 @@ func (x *ConsensusRequest) GetPayload() []byte {
return nil return nil
} }
func (x *ConsensusRequest) GetSenderPubkeyBitmap() []byte {
if x != nil {
return x.SenderPubkeyBitmap
}
return nil
}
type DrandRequest struct { type DrandRequest struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -1032,7 +1040,7 @@ var file_message_proto_rawDesc = []byte{
0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64,
0x22, 0xd7, 0x01, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x52, 0x65, 0x22, 0x89, 0x02, 0x0a, 0x10, 0x43, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, 0x1b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x76, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, 0x1b,
0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28,
@ -1045,67 +1053,70 @@ var file_message_proto_rawDesc = []byte{
0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79,
0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x97, 0x01, 0x0a, 0x0c, 0x44, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x65,
0x72, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x08, 0x73, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x69, 0x74, 0x6d,
0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x02, 0x18, 0x61, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72,
0x01, 0x52, 0x07, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0d, 0x73, 0x65, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x22, 0x97, 0x01, 0x0a,
0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x44, 0x72, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a,
0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x75, 0x62, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x42,
0x6b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x02, 0x18, 0x01, 0x52, 0x07, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x0d,
0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20,
0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x01, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50,
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x70, 0x61, 0x79, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68,
0x6c, 0x6f, 0x61, 0x64, 0x22, 0xad, 0x03, 0x0a, 0x11, 0x56, 0x69, 0x65, 0x77, 0x43, 0x68, 0x61, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x62,
0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x76, 0x69, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c,
0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x76, 0x69, 0x65, 0x6f, 0x61, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x70,
0x77, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xad, 0x03, 0x0a, 0x11, 0x56, 0x69, 0x65, 0x77, 0x43,
0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e, 0x75, 0x6d, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07,
0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x76, 0x69, 0x65, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x76,
0x28, 0x0d, 0x52, 0x07, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x69, 0x65, 0x77, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e,
0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x75, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4e,
0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x75, 0x6d, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, 0x61, 0x72, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x03,
0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x73, 0x68, 0x61, 0x72, 0x64, 0x49, 0x64, 0x12, 0x23, 0x0a,
0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x0d, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x04,
0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6b,
0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x75, 0x62,
0x25, 0x0a, 0x0e, 0x76, 0x69, 0x65, 0x77, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f, 0x73, 0x69, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x64, 0x65,
0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x76, 0x69, 0x65, 0x77, 0x63, 0x68, 0x61, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,
0x6e, 0x67, 0x65, 0x53, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x69, 0x65, 0x77, 0x69, 0x64, 0x61, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,
0x5f, 0x73, 0x69, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x76, 0x69, 0x65, 0x77, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x76, 0x69, 0x65, 0x77, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x5f,
0x69, 0x64, 0x53, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x32, 0x5f, 0x61, 0x67, 0x67, 0x73, 0x73, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x76, 0x69, 0x65, 0x77, 0x63,
0x69, 0x67, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x32, 0x41, 0x67, 0x67, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x69, 0x65, 0x77,
0x73, 0x69, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x32, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x69, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x76, 0x69,
0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x32, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x65, 0x77, 0x69, 0x64, 0x53, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x32, 0x5f, 0x61, 0x67,
0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x33, 0x5f, 0x61, 0x67, 0x67, 0x73, 0x69, 0x67, 0x73, 0x18, 0x67, 0x73, 0x69, 0x67, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x32, 0x41,
0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x33, 0x41, 0x67, 0x67, 0x73, 0x69, 0x67, 0x73, 0x67, 0x67, 0x73, 0x69, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x32, 0x5f, 0x62, 0x69, 0x74,
0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x33, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x18, 0x0c, 0x20, 0x6d, 0x61, 0x70, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x32, 0x42, 0x69, 0x74,
0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x33, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x12, 0x25, 0x0a, 0x6d, 0x61, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x33, 0x5f, 0x61, 0x67, 0x67, 0x73, 0x69, 0x67,
0x0e, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6d, 0x33, 0x41, 0x67, 0x67, 0x73, 0x69,
0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x42, 0x67, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x33, 0x5f, 0x62, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x18,
0x6c, 0x6f, 0x63, 0x6b, 0x2a, 0x50, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x0c, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x6d, 0x33, 0x42, 0x69, 0x74, 0x6d, 0x61, 0x70, 0x12,
0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4e, 0x53, 0x45, 0x4e, 0x53, 0x55, 0x53, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63,
0x10, 0x00, 0x12, 0x0f, 0x0a, 0x07, 0x53, 0x54, 0x41, 0x4b, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x1a, 0x6b, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65,
0x02, 0x08, 0x01, 0x12, 0x0d, 0x0a, 0x05, 0x44, 0x52, 0x41, 0x4e, 0x44, 0x10, 0x02, 0x1a, 0x02, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x2a, 0x50, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x08, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x53, 0x55, 0x50, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4e, 0x53, 0x45, 0x4e, 0x53,
0x50, 0x4f, 0x52, 0x54, 0x10, 0x03, 0x2a, 0xd1, 0x01, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x55, 0x53, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x07, 0x53, 0x54, 0x41, 0x4b, 0x49, 0x4e, 0x47, 0x10,
0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x16, 0x4e, 0x45, 0x57, 0x4e, 0x4f, 0x44, 0x01, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0d, 0x0a, 0x05, 0x44, 0x52, 0x41, 0x4e, 0x44, 0x10, 0x02,
0x45, 0x5f, 0x42, 0x45, 0x41, 0x43, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4b, 0x49, 0x4e, 0x47, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x5f, 0x53,
0x10, 0x00, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x4e, 0x4e, 0x4f, 0x55, 0x4e, 0x55, 0x50, 0x50, 0x4f, 0x52, 0x54, 0x10, 0x03, 0x2a, 0xd1, 0x01, 0x0a, 0x0b, 0x4d, 0x65, 0x73,
0x43, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x45, 0x10, 0x73, 0x61, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x16, 0x4e, 0x45, 0x57, 0x4e,
0x02, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x45, 0x44, 0x10, 0x03, 0x12, 0x4f, 0x44, 0x45, 0x5f, 0x42, 0x45, 0x41, 0x43, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x4b, 0x49,
0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4e, 0x47, 0x10, 0x00, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x41, 0x4e, 0x4e, 0x4f,
0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x0e, 0x0a, 0x0a, 0x56, 0x49, 0x55, 0x4e, 0x43, 0x45, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52,
0x45, 0x57, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x45, 0x45, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x52, 0x45, 0x50, 0x41, 0x52, 0x45, 0x44, 0x10,
0x57, 0x56, 0x49, 0x45, 0x57, 0x10, 0x07, 0x12, 0x12, 0x0a, 0x0a, 0x44, 0x52, 0x41, 0x4e, 0x44, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x04, 0x12, 0x0d, 0x0a,
0x5f, 0x49, 0x4e, 0x49, 0x54, 0x10, 0x0a, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x14, 0x0a, 0x0c, 0x44, 0x09, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x54, 0x45, 0x44, 0x10, 0x05, 0x12, 0x0e, 0x0a, 0x0a,
0x52, 0x41, 0x4e, 0x44, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x0b, 0x1a, 0x02, 0x08, 0x56, 0x49, 0x45, 0x57, 0x43, 0x48, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07,
0x01, 0x12, 0x17, 0x0a, 0x0f, 0x4c, 0x4f, 0x54, 0x54, 0x45, 0x52, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x4e, 0x45, 0x57, 0x56, 0x49, 0x45, 0x57, 0x10, 0x07, 0x12, 0x12, 0x0a, 0x0a, 0x44, 0x52, 0x41,
0x55, 0x45, 0x53, 0x54, 0x10, 0x0c, 0x1a, 0x02, 0x08, 0x01, 0x32, 0x41, 0x0a, 0x0d, 0x43, 0x6c, 0x4e, 0x44, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x10, 0x0a, 0x1a, 0x02, 0x08, 0x01, 0x12, 0x14, 0x0a,
0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x30, 0x0a, 0x07, 0x50, 0x0c, 0x44, 0x52, 0x41, 0x4e, 0x44, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x49, 0x54, 0x10, 0x0b, 0x1a,
0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x10, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x02, 0x08, 0x01, 0x12, 0x17, 0x0a, 0x0f, 0x4c, 0x4f, 0x54, 0x54, 0x45, 0x52, 0x59, 0x5f, 0x52,
0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x10, 0x0c, 0x1a, 0x02, 0x08, 0x01, 0x32, 0x41, 0x0a, 0x0d,
0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62, 0x06, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x30, 0x0a,
0x72, 0x6f, 0x74, 0x6f, 0x33, 0x07, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x12, 0x10, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x11, 0x2e, 0x6d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

@ -87,6 +87,7 @@ message ConsensusRequest {
bytes block = 5; bytes block = 5;
bytes sender_pubkey = 6; bytes sender_pubkey = 6;
bytes payload = 7; bytes payload = 7;
bytes sender_pubkey_bitmap = 8;
} }
message DrandRequest { message DrandRequest {

@ -62,7 +62,7 @@ func (consensus *Consensus) isRightBlockNumAndViewID(recvMsg *FBFTMessage,
Uint64("MsgViewID", recvMsg.ViewID). Uint64("MsgViewID", recvMsg.ViewID).
Uint64("MsgBlockNum", recvMsg.BlockNum). Uint64("MsgBlockNum", recvMsg.BlockNum).
Uint64("blockNum", consensus.blockNum). Uint64("blockNum", consensus.blockNum).
Str("ValidatorPubKey", recvMsg.SenderPubkey.Bytes.Hex()). Interface("ValidatorPubKey", recvMsg.SenderPubkeys).
Msg("BlockNum/viewID not match") Msg("BlockNum/viewID not match")
return false return false
} }
@ -74,12 +74,18 @@ func (consensus *Consensus) onAnnounceSanityChecks(recvMsg *FBFTMessage) bool {
msg_pb.MessageType_ANNOUNCE, recvMsg.BlockNum, recvMsg.ViewID, msg_pb.MessageType_ANNOUNCE, recvMsg.BlockNum, recvMsg.ViewID,
) )
if len(logMsgs) > 0 { if len(logMsgs) > 0 {
if len(logMsgs[0].SenderPubkeys) != 1 || len(recvMsg.SenderPubkeys) != 1 {
consensus.getLogger().Debug().
Interface("signers", recvMsg.SenderPubkeys).
Msg("[OnAnnounce] Announce message have 0 or more than 1 signers")
return false
}
if logMsgs[0].BlockHash != recvMsg.BlockHash && if logMsgs[0].BlockHash != recvMsg.BlockHash &&
bytes.Equal(logMsgs[0].SenderPubkey.Bytes[:], recvMsg.SenderPubkey.Bytes[:]) { bytes.Equal(logMsgs[0].SenderPubkeys[0].Bytes[:], recvMsg.SenderPubkeys[0].Bytes[:]) {
consensus.getLogger().Debug(). consensus.getLogger().Debug().
Str("logMsgSenderKey", logMsgs[0].SenderPubkey.Bytes.Hex()). Str("logMsgSenderKey", logMsgs[0].SenderPubkeys[0].Bytes.Hex()).
Str("logMsgBlockHash", logMsgs[0].BlockHash.Hex()). Str("logMsgBlockHash", logMsgs[0].BlockHash.Hex()).
Str("recvMsg.SenderPubkey", recvMsg.SenderPubkey.Bytes.Hex()). Str("recvMsg.SenderPubkeys", recvMsg.SenderPubkeys[0].Bytes.Hex()).
Uint64("recvMsg.BlockNum", recvMsg.BlockNum). Uint64("recvMsg.BlockNum", recvMsg.BlockNum).
Uint64("recvMsg.ViewID", recvMsg.ViewID). Uint64("recvMsg.ViewID", recvMsg.ViewID).
Str("recvMsgBlockHash", recvMsg.BlockHash.Hex()). Str("recvMsgBlockHash", recvMsg.BlockHash.Hex()).
@ -188,6 +194,10 @@ func (consensus *Consensus) onViewChangeSanityCheck(recvMsg *FBFTMessage) bool {
Msg("Received viewID that is MaxViewIDDiff (100) further from the current viewID!") Msg("Received viewID that is MaxViewIDDiff (100) further from the current viewID!")
return false return false
} }
if len(recvMsg.SenderPubkeys) != 1 {
consensus.getLogger().Error().Msg("[onViewChange] multiple signers in view change message.")
return false
}
return true return true
} }

@ -47,6 +47,8 @@ type Consensus struct {
aggregatedCommitSig *bls_core.Sign aggregatedCommitSig *bls_core.Sign
prepareBitmap *bls_cosi.Mask prepareBitmap *bls_cosi.Mask
commitBitmap *bls_cosi.Mask commitBitmap *bls_cosi.Mask
multiSigBitmap *bls_cosi.Mask // Bitmap for parsing multisig bitmap from validators
multiSigMutex sync.RWMutex
// Commits collected from view change // Commits collected from view change
// for each viewID, we need keep track of corresponding sigs and bitmap // for each viewID, we need keep track of corresponding sigs and bitmap
// until one of the viewID has enough votes (>=2f+1) // until one of the viewID has enough votes (>=2f+1)
@ -186,7 +188,6 @@ func New(
// FBFT related // FBFT related
consensus.FBFTLog = NewFBFTLog() consensus.FBFTLog = NewFBFTLog()
consensus.phase = FBFTAnnounce consensus.phase = FBFTAnnounce
// TODO Refactor consensus.block* into State?
consensus.current = State{mode: Normal} consensus.current = State{mode: Normal}
// FBFT timeout // FBFT timeout
consensus.consensusTimeout = createTimeout() consensus.consensusTimeout = createTimeout()

@ -5,6 +5,8 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/harmony-one/harmony/internal/params"
"github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/crypto/bls"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -92,6 +94,7 @@ func (consensus *Consensus) UpdatePublicKeys(pubKeys []bls_cosi.PublicKeyWrapper
} }
consensus.pubKeyLock.Unlock() consensus.pubKeyLock.Unlock()
// reset states after update public keys // reset states after update public keys
// TODO: incorporate bitmaps in the decider, so their state can't be inconsistent.
consensus.UpdateBitmaps() consensus.UpdateBitmaps()
consensus.ResetState() consensus.ResetState()
@ -106,7 +109,8 @@ func NewFaker() *Consensus {
return &Consensus{} return &Consensus{}
} }
// Sign on the hash of the message // Sign on the hash of the message with the private keys and return the signature.
// If multiple keys are provided, the aggregated signature will be returned.
func (consensus *Consensus) signMessage(message []byte, priKey *bls_core.SecretKey) []byte { func (consensus *Consensus) signMessage(message []byte, priKey *bls_core.SecretKey) []byte {
hash := hash.Keccak256(message) hash := hash.Keccak256(message)
signature := priKey.SignHash(hash[:]) signature := priKey.SignHash(hash[:])
@ -153,8 +157,12 @@ func (consensus *Consensus) UpdateBitmaps() {
members := consensus.Decider.Participants() members := consensus.Decider.Participants()
prepareBitmap, _ := bls_cosi.NewMask(members, nil) prepareBitmap, _ := bls_cosi.NewMask(members, nil)
commitBitmap, _ := bls_cosi.NewMask(members, nil) commitBitmap, _ := bls_cosi.NewMask(members, nil)
multiSigBitmap, _ := bls_cosi.NewMask(members, nil)
consensus.prepareBitmap = prepareBitmap consensus.prepareBitmap = prepareBitmap
consensus.commitBitmap = commitBitmap consensus.commitBitmap = commitBitmap
consensus.multiSigMutex.Lock()
consensus.multiSigBitmap = multiSigBitmap
consensus.multiSigMutex.Unlock()
} }
// ResetState resets the state of the consensus // ResetState resets the state of the consensus
@ -214,7 +222,10 @@ func (consensus *Consensus) checkViewID(msg *FBFTMessage) error {
//so only set mode to normal when new node enters consensus and need checking viewID //so only set mode to normal when new node enters consensus and need checking viewID
consensus.current.SetMode(Normal) consensus.current.SetMode(Normal)
consensus.SetViewIDs(msg.ViewID) consensus.SetViewIDs(msg.ViewID)
consensus.LeaderPubKey = msg.SenderPubkey if len(msg.SenderPubkeys) != 1 {
return errors.New("Leader message can not have multiple sender keys")
}
consensus.LeaderPubKey = msg.SenderPubkeys[0]
consensus.IgnoreViewIDCheck.UnSet() consensus.IgnoreViewIDCheck.UnSet()
consensus.consensusTimeout[timeoutConsensus].Start() consensus.consensusTimeout[timeoutConsensus].Start()
consensus.getLogger().Debug(). consensus.getLogger().Debug().
@ -332,6 +343,11 @@ func (consensus *Consensus) UpdateConsensusInformation() Mode {
consensus.BlockPeriod = 5 * time.Second consensus.BlockPeriod = 5 * time.Second
// TODO: remove once multisig is fully upgraded in the network
if consensus.ChainReader.Config().ChainID != params.MainnetChainID || curEpoch.Cmp(big.NewInt(1000)) > 0 {
consensus.MultiSig = true
}
isFirstTimeStaking := consensus.ChainReader.Config().IsStaking(nextEpoch) && isFirstTimeStaking := consensus.ChainReader.Config().IsStaking(nextEpoch) &&
len(curHeader.ShardState()) > 0 && len(curHeader.ShardState()) > 0 &&
!consensus.ChainReader.Config().IsStaking(curEpoch) !consensus.ChainReader.Config().IsStaking(curEpoch)

@ -43,7 +43,7 @@ func TestPopulateMessageFields(t *testing.T) {
keyBytes := bls.SerializedPublicKey{} keyBytes := bls.SerializedPublicKey{}
keyBytes.FromLibBLSPublicKey(blsPriKey.GetPublicKey()) keyBytes.FromLibBLSPublicKey(blsPriKey.GetPublicKey())
consensusMsg := consensus.populateMessageFields(msg.GetConsensus(), consensus.blockHash[:], consensusMsg := consensus.populateMessageFieldsAndSender(msg.GetConsensus(), consensus.blockHash[:],
keyBytes) keyBytes)
if consensusMsg.ViewId != 2 { if consensusMsg.ViewId != 2 {

@ -110,7 +110,7 @@ func (consensus *Consensus) finalizeCommits() {
return return
} }
// Construct committed message // Construct committed message
network, err := consensus.construct(msg_pb.MessageType_COMMITTED, nil, leaderPriKey) network, err := consensus.construct(msg_pb.MessageType_COMMITTED, nil, []*bls.PrivateKeyWrapper{leaderPriKey})
if err != nil { if err != nil {
consensus.getLogger().Warn().Err(err). consensus.getLogger().Warn().Err(err).
Msg("[FinalizeCommits] Unable to construct Committed message") Msg("[FinalizeCommits] Unable to construct Committed message")
@ -272,20 +272,18 @@ func (consensus *Consensus) tryCatchup() {
consensus.getLogger().Debug().Msg("[TryCatchup] parent block hash not match") consensus.getLogger().Debug().Msg("[TryCatchup] parent block hash not match")
break break
} }
consensus.getLogger().Info().Msg("[TryCatchup] block found to commit")
preparedMsgs := consensus.FBFTLog.GetMessagesByTypeSeqHash( if len(committedMsg.SenderPubkeys) != 1 {
msg_pb.MessageType_PREPARED, committedMsg.BlockNum, committedMsg.BlockHash, consensus.getLogger().Error().Msg("[TryCatchup] Leader message can not have multiple sender keys")
)
msg := consensus.FBFTLog.FindMessageByMaxViewID(preparedMsgs)
if msg == nil {
break break
} }
consensus.getLogger().Info().Msg("[TryCatchup] prepared message found to commit")
consensus.getLogger().Info().Msg("[TryCatchup] block found to commit")
atomic.AddUint64(&consensus.blockNum, 1) atomic.AddUint64(&consensus.blockNum, 1)
consensus.SetCurBlockViewID(committedMsg.ViewID + 1) consensus.SetCurBlockViewID(committedMsg.ViewID + 1)
consensus.LeaderPubKey = committedMsg.SenderPubkey consensus.LeaderPubKey = committedMsg.SenderPubkeys[0]
consensus.getLogger().Info().Msg("[TryCatchup] Adding block to chain") consensus.getLogger().Info().Msg("[TryCatchup] Adding block to chain")

@ -2,6 +2,9 @@ package consensus
import ( import (
"bytes" "bytes"
"errors"
protobuf "github.com/golang/protobuf/proto"
"github.com/harmony-one/harmony/crypto/bls" "github.com/harmony-one/harmony/crypto/bls"
@ -24,13 +27,31 @@ type NetworkMessage struct {
// Populates the common basic fields for all consensus message. // Populates the common basic fields for all consensus message.
func (consensus *Consensus) populateMessageFields( func (consensus *Consensus) populateMessageFields(
request *msg_pb.ConsensusRequest, blockHash []byte, pubKey bls.SerializedPublicKey, request *msg_pb.ConsensusRequest, blockHash []byte,
) *msg_pb.ConsensusRequest { ) *msg_pb.ConsensusRequest {
request.ViewId = consensus.GetCurBlockViewID() request.ViewId = consensus.GetCurBlockViewID()
request.BlockNum = consensus.blockNum request.BlockNum = consensus.blockNum
request.ShardId = consensus.ShardID request.ShardId = consensus.ShardID
// 32 byte block hash // 32 byte block hash
request.BlockHash = blockHash request.BlockHash = blockHash
return request
}
// Populates the common basic fields for the consensus message and senders bitmap.
func (consensus *Consensus) populateMessageFieldsAndSendersBitmap(
request *msg_pb.ConsensusRequest, blockHash []byte, bitmap []byte,
) *msg_pb.ConsensusRequest {
consensus.populateMessageFields(request, blockHash)
// sender address
request.SenderPubkeyBitmap = bitmap
return request
}
// Populates the common basic fields for the consensus message and single sender.
func (consensus *Consensus) populateMessageFieldsAndSender(
request *msg_pb.ConsensusRequest, blockHash []byte, pubKey bls.SerializedPublicKey,
) *msg_pb.ConsensusRequest {
consensus.populateMessageFields(request, blockHash)
// sender address // sender address
request.SenderPubkey = pubKey[:] request.SenderPubkey = pubKey[:]
return request return request
@ -38,8 +59,11 @@ func (consensus *Consensus) populateMessageFields(
// construct is the single creation point of messages intended for the wire. // construct is the single creation point of messages intended for the wire.
func (consensus *Consensus) construct( func (consensus *Consensus) construct(
p msg_pb.MessageType, payloadForSign []byte, priKey *bls.PrivateKeyWrapper, p msg_pb.MessageType, payloadForSign []byte, priKeys []*bls.PrivateKeyWrapper,
) (*NetworkMessage, error) { ) (*NetworkMessage, error) {
if len(priKeys) == 0 {
return nil, errors.New("No private keys provided")
}
message := &msg_pb.Message{ message := &msg_pb.Message{
ServiceType: msg_pb.ServiceType_CONSENSUS, ServiceType: msg_pb.ServiceType_CONSENSUS,
Type: p, Type: p,
@ -52,11 +76,27 @@ func (consensus *Consensus) construct(
aggSig *bls_core.Sign aggSig *bls_core.Sign
) )
consensusMsg = consensus.populateMessageFields( if len(priKeys) == 1 {
message.GetConsensus(), consensus.blockHash[:], priKey.Pub.Bytes, consensusMsg = consensus.populateMessageFieldsAndSender(
) message.GetConsensus(), consensus.blockHash[:], priKeys[0].Pub.Bytes,
)
} else {
// TODO: use a persistent bitmap to report bitmap
mask, err := bls.NewMask(consensus.Decider.Participants(), nil)
if err != nil {
utils.Logger().Warn().Err(err).Msg("unable to setup mask for multi-sig message")
return nil, err
}
for _, key := range priKeys {
mask.SetKey(key.Pub.Bytes, true)
}
consensusMsg = consensus.populateMessageFieldsAndSendersBitmap(
message.GetConsensus(), consensus.blockHash[:], mask.Bitmap,
)
}
// Do the signing, 96 byte of bls signature // Do the signing, 96 byte of bls signature
needMsgSig := true
switch p { switch p {
case msg_pb.MessageType_PREPARED: case msg_pb.MessageType_PREPARED:
consensusMsg.Block = consensus.block consensusMsg.Block = consensus.block
@ -69,13 +109,23 @@ func (consensus *Consensus) construct(
buffer.Write(consensus.prepareBitmap.Bitmap) buffer.Write(consensus.prepareBitmap.Bitmap)
consensusMsg.Payload = buffer.Bytes() consensusMsg.Payload = buffer.Bytes()
case msg_pb.MessageType_PREPARE: case msg_pb.MessageType_PREPARE:
if s := priKey.Pri.SignHash(consensusMsg.BlockHash); s != nil { needMsgSig = false
consensusMsg.Payload = s.Serialize() sig := bls_core.Sign{}
for _, priKey := range priKeys {
if s := priKey.Pri.SignHash(consensusMsg.BlockHash); s != nil {
sig.Add(s)
}
} }
consensusMsg.Payload = sig.Serialize()
case msg_pb.MessageType_COMMIT: case msg_pb.MessageType_COMMIT:
if s := priKey.Pri.SignHash(payloadForSign); s != nil { needMsgSig = false
consensusMsg.Payload = s.Serialize() sig := bls_core.Sign{}
for _, priKey := range priKeys {
if s := priKey.Pri.SignHash(payloadForSign); s != nil {
sig.Add(s)
}
} }
consensusMsg.Payload = sig.Serialize()
case msg_pb.MessageType_COMMITTED: case msg_pb.MessageType_COMMITTED:
buffer := bytes.Buffer{} buffer := bytes.Buffer{}
// 96 bytes aggregated signature // 96 bytes aggregated signature
@ -88,7 +138,16 @@ func (consensus *Consensus) construct(
consensusMsg.Payload = consensus.blockHash[:] consensusMsg.Payload = consensus.blockHash[:]
} }
marshaledMessage, err := consensus.signAndMarshalConsensusMessage(message, priKey.Pri) var marshaledMessage []byte
var err error
if needMsgSig {
// The message that needs signing only needs to be signed with a single key
marshaledMessage, err = consensus.signAndMarshalConsensusMessage(message, priKeys[0].Pri)
} else {
// Skip message (potentially multi-sig) signing for validator consensus messages (prepare and commit)
// as signature is already signed on the block data.
marshaledMessage, err = protobuf.Marshal(message)
}
if err != nil { if err != nil {
utils.Logger().Error().Err(err). utils.Logger().Error().Err(err).
Str("phase", p.String()). Str("phase", p.String()).
@ -96,7 +155,7 @@ func (consensus *Consensus) construct(
return nil, err return nil, err
} }
FBFTMsg, err2 := ParseFBFTMessage(message) FBFTMsg, err2 := consensus.ParseFBFTMessage(message)
if err2 != nil { if err2 != nil {
utils.Logger().Error().Err(err). utils.Logger().Error().Err(err).

@ -35,7 +35,7 @@ func TestConstructAnnounceMessage(test *testing.T) {
pubKeyWrapper := bls.PublicKeyWrapper{Object: blsPriKey.GetPublicKey()} pubKeyWrapper := bls.PublicKeyWrapper{Object: blsPriKey.GetPublicKey()}
pubKeyWrapper.Bytes.FromLibBLSPublicKey(pubKeyWrapper.Object) pubKeyWrapper.Bytes.FromLibBLSPublicKey(pubKeyWrapper.Object)
priKeyWrapper := bls.PrivateKeyWrapper{blsPriKey, &pubKeyWrapper} priKeyWrapper := bls.PrivateKeyWrapper{blsPriKey, &pubKeyWrapper}
if _, err = consensus.construct(msg_pb.MessageType_ANNOUNCE, nil, &priKeyWrapper); err != nil { if _, err = consensus.construct(msg_pb.MessageType_ANNOUNCE, nil, []*bls.PrivateKeyWrapper{&priKeyWrapper}); err != nil {
test.Fatalf("could not construct announce: %v", err) test.Fatalf("could not construct announce: %v", err)
} }
} }
@ -73,7 +73,7 @@ func TestConstructPreparedMessage(test *testing.T) {
validatorKey.FromLibBLSPublicKey(validatorPubKey) validatorKey.FromLibBLSPublicKey(validatorPubKey)
consensus.Decider.SubmitVote( consensus.Decider.SubmitVote(
quorum.Prepare, quorum.Prepare,
leaderKey, []bls.SerializedPublicKey{leaderKey},
leaderPriKey.Sign(message), leaderPriKey.Sign(message),
common.BytesToHash(consensus.blockHash[:]), common.BytesToHash(consensus.blockHash[:]),
consensus.blockNum, consensus.blockNum,
@ -81,7 +81,7 @@ func TestConstructPreparedMessage(test *testing.T) {
) )
if _, err := consensus.Decider.SubmitVote( if _, err := consensus.Decider.SubmitVote(
quorum.Prepare, quorum.Prepare,
validatorKey, []bls.SerializedPublicKey{validatorKey},
validatorPriKey.Sign(message), validatorPriKey.Sign(message),
common.BytesToHash(consensus.blockHash[:]), common.BytesToHash(consensus.blockHash[:]),
consensus.blockNum, consensus.blockNum,
@ -101,7 +101,7 @@ func TestConstructPreparedMessage(test *testing.T) {
pubKeyWrapper := bls.PublicKeyWrapper{Object: blsPriKey.GetPublicKey()} pubKeyWrapper := bls.PublicKeyWrapper{Object: blsPriKey.GetPublicKey()}
pubKeyWrapper.Bytes.FromLibBLSPublicKey(pubKeyWrapper.Object) pubKeyWrapper.Bytes.FromLibBLSPublicKey(pubKeyWrapper.Object)
priKeyWrapper := bls.PrivateKeyWrapper{blsPriKey, &pubKeyWrapper} priKeyWrapper := bls.PrivateKeyWrapper{blsPriKey, &pubKeyWrapper}
network, err := consensus.construct(msg_pb.MessageType_PREPARED, nil, &priKeyWrapper) network, err := consensus.construct(msg_pb.MessageType_PREPARED, nil, []*bls.PrivateKeyWrapper{&priKeyWrapper})
if err != nil { if err != nil {
test.Errorf("Error when creating prepared message") test.Errorf("Error when creating prepared message")
} }

@ -1,6 +1,9 @@
package consensus package consensus
import ( import (
"bytes"
"sort"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
bls_core "github.com/harmony-one/bls/ffi/go/bls" bls_core "github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/consensus/quorum" "github.com/harmony-one/harmony/consensus/quorum"
@ -12,96 +15,120 @@ import (
// Returns true when it is a double-sign or there is error, otherwise, false. // Returns true when it is a double-sign or there is error, otherwise, false.
func (consensus *Consensus) checkDoubleSign(recvMsg *FBFTMessage) bool { func (consensus *Consensus) checkDoubleSign(recvMsg *FBFTMessage) bool {
if consensus.couldThisBeADoubleSigner(recvMsg) { if consensus.couldThisBeADoubleSigner(recvMsg) {
if alreadyCastBallot := consensus.Decider.ReadBallot( addrSet := map[common.Address]struct{}{}
quorum.Commit, recvMsg.SenderPubkey.Bytes, for _, pubKey2 := range recvMsg.SenderPubkeys {
); alreadyCastBallot != nil { if alreadyCastBallot := consensus.Decider.ReadBallot(
firstPubKey, err := bls.BytesToBLSPublicKey(alreadyCastBallot.SignerPubKey[:]) quorum.Commit, pubKey2.Bytes,
if err != nil { ); alreadyCastBallot != nil {
return false for _, pubKey1 := range alreadyCastBallot.SignerPubKeys {
} if bytes.Compare(pubKey2.Bytes[:], pubKey1[:]) == 0 {
if recvMsg.SenderPubkey.Object.IsEqual(firstPubKey) { for _, blk := range consensus.FBFTLog.GetBlocksByNumber(recvMsg.BlockNum) {
for _, blk := range consensus.FBFTLog.GetBlocksByNumber(recvMsg.BlockNum) { firstSignedHeader := blk.Header()
firstSignedBlock := blk.Header() areHeightsEqual := firstSignedHeader.Number().Uint64() == recvMsg.BlockNum
areHeightsEqual := firstSignedBlock.Number().Uint64() == recvMsg.BlockNum areViewIDsEqual := firstSignedHeader.ViewID().Uint64() == recvMsg.ViewID
areViewIDsEqual := firstSignedBlock.ViewID().Uint64() == recvMsg.ViewID areHeadersEqual := firstSignedHeader.Hash() == recvMsg.BlockHash
areHeadersEqual := firstSignedBlock.Hash() == recvMsg.BlockHash
// If signer already firstSignedBlock, and the block height is the same // If signer already firstSignedHeader, and the block height is the same
// and the viewID is the same, then we need to verify the block // and the viewID is the same, then we need to verify the block
// hash, and if block hash is different, then that is a clear // hash, and if block hash is different, then that is a clear
// case of double signing // case of double signing
if areHeightsEqual && areViewIDsEqual && !areHeadersEqual { if areHeightsEqual && areViewIDsEqual && !areHeadersEqual {
var doubleSign bls_core.Sign var doubleSign bls_core.Sign
if err := doubleSign.Deserialize(recvMsg.Payload); err != nil { if err := doubleSign.Deserialize(recvMsg.Payload); err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()). consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not deserialize potential double signer") Msg("could not deserialize potential double signer")
return true return true
} }
curHeader := consensus.ChainReader.CurrentHeader() curHeader := consensus.ChainReader.CurrentHeader()
committee, err := consensus.ChainReader.ReadShardState(curHeader.Epoch()) committee, err := consensus.ChainReader.ReadShardState(curHeader.Epoch())
if err != nil { if err != nil {
consensus.getLogger().Err(err). consensus.getLogger().Err(err).
Uint32("shard", consensus.ShardID). Uint32("shard", consensus.ShardID).
Uint64("epoch", curHeader.Epoch().Uint64()). Uint64("epoch", curHeader.Epoch().Uint64()).
Msg("could not read shard state") Msg("could not read shard state")
return true return true
} }
subComm, err := committee.FindCommitteeByID( subComm, err := committee.FindCommitteeByID(
consensus.ShardID, consensus.ShardID,
) )
if err != nil { if err != nil {
consensus.getLogger().Err(err). consensus.getLogger().Err(err).
Str("msg", recvMsg.String()). Str("msg", recvMsg.String()).
Msg("could not find subcommittee for bls key") Msg("could not find subcommittee for bls key")
return true return true
} }
addr, err := subComm.AddressForBLSKey(recvMsg.SenderPubkey.Bytes) addr, err := subComm.AddressForBLSKey(pubKey2.Bytes)
if err != nil { if err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()). consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not find address for bls key") Msg("could not find address for bls key")
return true return true
} }
if _, ok := addrSet[*addr]; ok {
// Address already slashed
break
}
leaderAddr, err := subComm.AddressForBLSKey(consensus.LeaderPubKey.Bytes) leaderAddr, err := subComm.AddressForBLSKey(consensus.LeaderPubKey.Bytes)
if err != nil { if err != nil {
consensus.getLogger().Err(err).Str("msg", recvMsg.String()). consensus.getLogger().Err(err).Str("msg", recvMsg.String()).
Msg("could not find address for leader bls key") Msg("could not find address for leader bls key")
return true return true
} }
go func(reporter common.Address) { go func(reporter common.Address) {
evid := slash.Evidence{ secondKeys := make([]bls.SerializedPublicKey, len(recvMsg.SenderPubkeys))
ConflictingVotes: slash.ConflictingVotes{ for i, pubKey := range recvMsg.SenderPubkeys {
FirstVote: slash.Vote{ secondKeys[i] = pubKey.Bytes
alreadyCastBallot.SignerPubKey, }
alreadyCastBallot.BlockHeaderHash, evid := slash.Evidence{
alreadyCastBallot.Signature, ConflictingVotes: slash.ConflictingVotes{
}, FirstVote: slash.Vote{
SecondVote: slash.Vote{ alreadyCastBallot.SignerPubKeys,
recvMsg.SenderPubkey.Bytes, alreadyCastBallot.BlockHeaderHash,
recvMsg.BlockHash, alreadyCastBallot.Signature,
common.Hex2Bytes(doubleSign.SerializeToHexStr()), },
}}, SecondVote: slash.Vote{
Moment: slash.Moment{ secondKeys,
Epoch: curHeader.Epoch(), recvMsg.BlockHash,
ShardID: consensus.ShardID, common.Hex2Bytes(doubleSign.SerializeToHexStr()),
}, }},
Offender: *addr, Moment: slash.Moment{
} Epoch: curHeader.Epoch(),
proof := slash.Record{ ShardID: consensus.ShardID,
Evidence: evid, Height: recvMsg.BlockNum,
Reporter: reporter, ViewID: recvMsg.ViewID,
},
Offender: *addr,
}
sort.SliceStable(evid.ConflictingVotes.FirstVote.SignerPubKeys, func(i, j int) bool {
return bytes.Compare(
evid.ConflictingVotes.FirstVote.SignerPubKeys[i][:],
evid.ConflictingVotes.FirstVote.SignerPubKeys[j][:]) < 0
})
sort.SliceStable(evid.ConflictingVotes.SecondVote.SignerPubKeys, func(i, j int) bool {
return bytes.Compare(
evid.ConflictingVotes.SecondVote.SignerPubKeys[i][:],
evid.ConflictingVotes.SecondVote.SignerPubKeys[j][:]) < 0
})
proof := slash.Record{
Evidence: evid,
Reporter: reporter,
}
consensus.SlashChan <- proof
}(*leaderAddr)
addrSet[*addr] = struct{}{}
break
} }
consensus.SlashChan <- proof }
}(*leaderAddr)
return true
} }
} }
} }
} }
return true return true
} }
return false return false

@ -23,27 +23,32 @@ type FBFTLog struct {
// FBFTMessage is the record of pbft messages received by a node during FBFT process // FBFTMessage is the record of pbft messages received by a node during FBFT process
type FBFTMessage struct { type FBFTMessage struct {
MessageType msg_pb.MessageType MessageType msg_pb.MessageType
ViewID uint64 ViewID uint64
BlockNum uint64 BlockNum uint64
BlockHash common.Hash BlockHash common.Hash
Block []byte Block []byte
SenderPubkey *bls.PublicKeyWrapper SenderPubkeys []*bls.PublicKeyWrapper
LeaderPubkey *bls.PublicKeyWrapper SenderPubkeyBitmap []byte
Payload []byte LeaderPubkey *bls.PublicKeyWrapper
ViewchangeSig *bls_core.Sign Payload []byte
ViewidSig *bls_core.Sign ViewchangeSig *bls_core.Sign
M2AggSig *bls_core.Sign ViewidSig *bls_core.Sign
M2Bitmap *bls_cosi.Mask M2AggSig *bls_core.Sign
M3AggSig *bls_core.Sign M2Bitmap *bls_cosi.Mask
M3Bitmap *bls_cosi.Mask M3AggSig *bls_core.Sign
M3Bitmap *bls_cosi.Mask
} }
// String .. // String ..
func (m *FBFTMessage) String() string { func (m *FBFTMessage) String() string {
sender := "" sender := ""
if m.SenderPubkey != nil { for _, key := range m.SenderPubkeys {
sender = m.SenderPubkey.Bytes.Hex() if sender == "" {
sender = key.Bytes.Hex()
} else {
sender = sender + ";" + key.Bytes.Hex()
}
} }
leader := "" leader := ""
if m.LeaderPubkey != nil { if m.LeaderPubkey != nil {
@ -240,7 +245,7 @@ func (log *FBFTLog) FindMessageByMaxViewID(msgs []*FBFTMessage) *FBFTMessage {
} }
// ParseFBFTMessage parses FBFT message into FBFTMessage structure // ParseFBFTMessage parses FBFT message into FBFTMessage structure
func ParseFBFTMessage(msg *msg_pb.Message) (*FBFTMessage, error) { func (consensus *Consensus) ParseFBFTMessage(msg *msg_pb.Message) (*FBFTMessage, error) {
// TODO Have this do sanity checks on the message please // TODO Have this do sanity checks on the message please
pbftMsg := FBFTMessage{} pbftMsg := FBFTMessage{}
pbftMsg.MessageType = msg.GetType() pbftMsg.MessageType = msg.GetType()
@ -252,13 +257,27 @@ func ParseFBFTMessage(msg *msg_pb.Message) (*FBFTMessage, error) {
copy(pbftMsg.Payload[:], consensusMsg.Payload[:]) copy(pbftMsg.Payload[:], consensusMsg.Payload[:])
pbftMsg.Block = make([]byte, len(consensusMsg.Block)) pbftMsg.Block = make([]byte, len(consensusMsg.Block))
copy(pbftMsg.Block[:], consensusMsg.Block[:]) copy(pbftMsg.Block[:], consensusMsg.Block[:])
pbftMsg.SenderPubkeyBitmap = make([]byte, len(consensusMsg.SenderPubkeyBitmap))
copy(pbftMsg.SenderPubkeyBitmap[:], consensusMsg.SenderPubkeyBitmap[:])
pubKey, err := bls_cosi.BytesToBLSPublicKey(consensusMsg.SenderPubkey) if len(consensusMsg.SenderPubkey) != 0 {
if err != nil { // If SenderPubKey is populated, treat it as a single key message
return nil, err pubKey, err := bls_cosi.BytesToBLSPublicKey(consensusMsg.SenderPubkey)
if err != nil {
return nil, err
}
pbftMsg.SenderPubkeys = []*bls.PublicKeyWrapper{{Object: pubKey}}
copy(pbftMsg.SenderPubkeys[0].Bytes[:], consensusMsg.SenderPubkey[:])
} else {
// else, it should be a multi-key message where the bitmap is populated
consensus.multiSigMutex.RLock()
pubKeys, err := consensus.multiSigBitmap.GetSignedPubKeysFromBitmap(pbftMsg.SenderPubkeyBitmap)
consensus.multiSigMutex.RUnlock()
if err != nil {
return nil, err
}
pbftMsg.SenderPubkeys = pubKeys
} }
pbftMsg.SenderPubkey = &bls.PublicKeyWrapper{Object: pubKey}
copy(pbftMsg.SenderPubkey.Bytes[:], consensusMsg.SenderPubkey[:])
return &pbftMsg, nil return &pbftMsg, nil
} }
@ -304,8 +323,8 @@ func ParseViewChangeMessage(msg *msg_pb.Message) (*FBFTMessage, error) {
return nil, err return nil, err
} }
pbftMsg.SenderPubkey = &bls.PublicKeyWrapper{Object: pubKey} pbftMsg.SenderPubkeys = []*bls.PublicKeyWrapper{{Object: pubKey}}
copy(pbftMsg.SenderPubkey.Bytes[:], vcMsg.SenderPubkey[:]) copy(pbftMsg.SenderPubkeys[0].Bytes[:], vcMsg.SenderPubkey[:])
pbftMsg.LeaderPubkey = &bls.PublicKeyWrapper{Object: leaderKey} pbftMsg.LeaderPubkey = &bls.PublicKeyWrapper{Object: leaderKey}
copy(pbftMsg.LeaderPubkey.Bytes[:], vcMsg.LeaderPubkey[:]) copy(pbftMsg.LeaderPubkey.Bytes[:], vcMsg.LeaderPubkey[:])
pbftMsg.ViewchangeSig = &vcSig pbftMsg.ViewchangeSig = &vcSig
@ -336,8 +355,8 @@ func (consensus *Consensus) ParseNewViewMessage(msg *msg_pb.Message) (*FBFTMessa
return nil, err return nil, err
} }
FBFTMsg.SenderPubkey = &bls.PublicKeyWrapper{Object: pubKey} FBFTMsg.SenderPubkeys = []*bls.PublicKeyWrapper{{Object: pubKey}}
copy(FBFTMsg.SenderPubkey.Bytes[:], vcMsg.SenderPubkey[:]) copy(FBFTMsg.SenderPubkeys[0].Bytes[:], vcMsg.SenderPubkey[:])
members := consensus.Decider.Participants() members := consensus.Decider.Participants()
if len(vcMsg.M3Aggsigs) > 0 { if len(vcMsg.M3Aggsigs) > 0 {

@ -3,11 +3,13 @@ package consensus
import ( import (
"time" "time"
"github.com/harmony-one/harmony/consensus/signature" "github.com/harmony-one/harmony/crypto/bls"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/consensus/signature"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/harmony-one/bls/ffi/go/bls" bls_core "github.com/harmony-one/bls/ffi/go/bls"
msg_pb "github.com/harmony-one/harmony/api/proto/message" msg_pb "github.com/harmony-one/harmony/api/proto/message"
"github.com/harmony-one/harmony/consensus/quorum" "github.com/harmony-one/harmony/consensus/quorum"
"github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/types"
@ -37,7 +39,7 @@ func (consensus *Consensus) announce(block *types.Block) {
return return
} }
networkMessage, err := consensus.construct(msg_pb.MessageType_ANNOUNCE, nil, key) networkMessage, err := consensus.construct(msg_pb.MessageType_ANNOUNCE, nil, []*bls.PrivateKeyWrapper{key})
if err != nil { if err != nil {
consensus.getLogger().Err(err). consensus.getLogger().Err(err).
Str("message-type", msg_pb.MessageType_ANNOUNCE.String()). Str("message-type", msg_pb.MessageType_ANNOUNCE.String()).
@ -66,7 +68,7 @@ func (consensus *Consensus) announce(block *types.Block) {
if _, err := consensus.Decider.AddNewVote( if _, err := consensus.Decider.AddNewVote(
quorum.Prepare, quorum.Prepare,
key.Pub.Bytes, []*bls.PublicKeyWrapper{key.Pub},
key.Pri.SignHash(consensus.blockHash[:]), key.Pri.SignHash(consensus.blockHash[:]),
block.Hash(), block.Hash(),
block.NumberU64(), block.NumberU64(),
@ -100,7 +102,7 @@ func (consensus *Consensus) announce(block *types.Block) {
} }
func (consensus *Consensus) onPrepare(msg *msg_pb.Message) { func (consensus *Consensus) onPrepare(msg *msg_pb.Message) {
recvMsg, err := ParseFBFTMessage(msg) recvMsg, err := consensus.ParseFBFTMessage(msg)
if err != nil { if err != nil {
consensus.getLogger().Error().Err(err).Msg("[OnPrepare] Unparseable validator message") consensus.getLogger().Error().Err(err).Msg("[OnPrepare] Unparseable validator message")
return return
@ -127,18 +129,20 @@ func (consensus *Consensus) onPrepare(msg *msg_pb.Message) {
blockHash := consensus.blockHash[:] blockHash := consensus.blockHash[:]
prepareBitmap := consensus.prepareBitmap prepareBitmap := consensus.prepareBitmap
// proceed only when the message is not received before // proceed only when the message is not received before
signed := consensus.Decider.ReadBallot(quorum.Prepare, recvMsg.SenderPubkey.Bytes) for _, signer := range recvMsg.SenderPubkeys {
if signed != nil { signed := consensus.Decider.ReadBallot(quorum.Prepare, signer.Bytes)
consensus.getLogger().Debug(). if signed != nil {
Str("validatorPubKey", recvMsg.SenderPubkey.Bytes.Hex()). consensus.getLogger().Debug().
Msg("[OnPrepare] Already Received prepare message from the validator") Str("validatorPubKey", signer.Bytes.Hex()).
return Msg("[OnPrepare] Already Received prepare message from the validator")
return
}
} }
if consensus.Decider.IsQuorumAchieved(quorum.Prepare) { if consensus.Decider.IsQuorumAchieved(quorum.Prepare) {
// already have enough signatures // already have enough signatures
consensus.getLogger().Debug(). consensus.getLogger().Debug().
Str("validatorPubKey", recvMsg.SenderPubkey.Bytes.Hex()). Interface("validatorPubKeys", recvMsg.SenderPubkeys).
Msg("[OnPrepare] Received Additional Prepare Message") Msg("[OnPrepare] Received Additional Prepare Message")
return return
} }
@ -147,14 +151,22 @@ func (consensus *Consensus) onPrepare(msg *msg_pb.Message) {
// Check BLS signature for the multi-sig // Check BLS signature for the multi-sig
prepareSig := recvMsg.Payload prepareSig := recvMsg.Payload
var sign bls.Sign var sign bls_core.Sign
err = sign.Deserialize(prepareSig) err = sign.Deserialize(prepareSig)
if err != nil { if err != nil {
consensus.getLogger().Error().Err(err). consensus.getLogger().Error().Err(err).
Msg("[OnPrepare] Failed to deserialize bls signature") Msg("[OnPrepare] Failed to deserialize bls signature")
return return
} }
if !sign.VerifyHash(recvMsg.SenderPubkey.Object, blockHash) { signerPubKey := &bls_core.PublicKey{}
if len(recvMsg.SenderPubkeys) == 1 {
signerPubKey = recvMsg.SenderPubkeys[0].Object
} else {
for _, pubKey := range recvMsg.SenderPubkeys {
signerPubKey.Add(pubKey.Object)
}
}
if !sign.VerifyHash(signerPubKey, blockHash) {
consensus.getLogger().Error().Msg("[OnPrepare] Received invalid BLS signature") consensus.getLogger().Error().Msg("[OnPrepare] Received invalid BLS signature")
return return
} }
@ -166,7 +178,7 @@ func (consensus *Consensus) onPrepare(msg *msg_pb.Message) {
//// Write - Start //// Write - Start
if _, err := consensus.Decider.AddNewVote( if _, err := consensus.Decider.AddNewVote(
quorum.Prepare, recvMsg.SenderPubkey.Bytes, quorum.Prepare, recvMsg.SenderPubkeys,
&sign, recvMsg.BlockHash, &sign, recvMsg.BlockHash,
recvMsg.BlockNum, recvMsg.ViewID, recvMsg.BlockNum, recvMsg.ViewID,
); err != nil { ); err != nil {
@ -174,7 +186,7 @@ func (consensus *Consensus) onPrepare(msg *msg_pb.Message) {
return return
} }
// Set the bitmap indicating that this validator signed. // Set the bitmap indicating that this validator signed.
if err := prepareBitmap.SetKey(recvMsg.SenderPubkey.Bytes, true); err != nil { if err := prepareBitmap.SetKeysAtomic(recvMsg.SenderPubkeys, true); err != nil {
consensus.getLogger().Warn().Err(err).Msg("[OnPrepare] prepareBitmap.SetKey failed") consensus.getLogger().Warn().Err(err).Msg("[OnPrepare] prepareBitmap.SetKey failed")
return return
} }
@ -192,7 +204,7 @@ func (consensus *Consensus) onPrepare(msg *msg_pb.Message) {
} }
func (consensus *Consensus) onCommit(msg *msg_pb.Message) { func (consensus *Consensus) onCommit(msg *msg_pb.Message) {
recvMsg, err := ParseFBFTMessage(msg) recvMsg, err := consensus.ParseFBFTMessage(msg)
if err != nil { if err != nil {
consensus.getLogger().Debug().Err(err).Msg("[OnCommit] Parse pbft message failed") consensus.getLogger().Debug().Err(err).Msg("[OnCommit] Parse pbft message failed")
return return
@ -213,14 +225,13 @@ func (consensus *Consensus) onCommit(msg *msg_pb.Message) {
//// Read - End //// Read - End
// Verify the signature on commitPayload is correct // Verify the signature on commitPayload is correct
validatorPubKey, commitSig := recvMsg.SenderPubkey, recvMsg.Payload
logger := consensus.getLogger().With(). logger := consensus.getLogger().With().
Str("validatorPubKey", validatorPubKey.Bytes.Hex()). Interface("validatorPubKey", recvMsg.SenderPubkeys).
Int64("numReceivedSoFar", signerCount).Logger() Int64("numReceivedSoFar", signerCount).Logger()
logger.Debug().Msg("[OnCommit] Received new commit message") logger.Debug().Msg("[OnCommit] Received new commit message")
var sign bls.Sign var sign bls_core.Sign
if err := sign.Deserialize(commitSig); err != nil { if err := sign.Deserialize(recvMsg.Payload); err != nil {
logger.Debug().Msg("[OnCommit] Failed to deserialize bls signature") logger.Debug().Msg("[OnCommit] Failed to deserialize bls signature")
return return
} }
@ -242,7 +253,15 @@ func (consensus *Consensus) onCommit(msg *msg_pb.Message) {
Uint64("MsgBlockNum", recvMsg.BlockNum). Uint64("MsgBlockNum", recvMsg.BlockNum).
Logger() Logger()
if !sign.VerifyHash(recvMsg.SenderPubkey.Object, commitPayload) { signerPubKey := &bls_core.PublicKey{}
if len(recvMsg.SenderPubkeys) == 1 {
signerPubKey = recvMsg.SenderPubkeys[0].Object
} else {
for _, pubKey := range recvMsg.SenderPubkeys {
signerPubKey.Add(pubKey.Object)
}
}
if !sign.VerifyHash(signerPubKey, commitPayload) {
logger.Error().Msg("[OnCommit] Cannot verify commit message") logger.Error().Msg("[OnCommit] Cannot verify commit message")
return return
} }
@ -253,14 +272,14 @@ func (consensus *Consensus) onCommit(msg *msg_pb.Message) {
return return
} }
if _, err := consensus.Decider.AddNewVote( if _, err := consensus.Decider.AddNewVote(
quorum.Commit, recvMsg.SenderPubkey.Bytes, quorum.Commit, recvMsg.SenderPubkeys,
&sign, recvMsg.BlockHash, &sign, recvMsg.BlockHash,
recvMsg.BlockNum, recvMsg.ViewID, recvMsg.BlockNum, recvMsg.ViewID,
); err != nil { ); err != nil {
return return
} }
// Set the bitmap indicating that this validator signed. // Set the bitmap indicating that this validator signed.
if err := commitBitmap.SetKey(recvMsg.SenderPubkey.Bytes, true); err != nil { if err := commitBitmap.SetKeysAtomic(recvMsg.SenderPubkeys, true); err != nil {
consensus.getLogger().Warn().Err(err). consensus.getLogger().Warn().Err(err).
Msg("[OnCommit] commitBitmap.SetKey failed") Msg("[OnCommit] commitBitmap.SetKey failed")
return return

@ -29,11 +29,14 @@ func (v *uniformVoteWeight) Policy() Policy {
// AddNewVote .. // AddNewVote ..
func (v *uniformVoteWeight) AddNewVote( func (v *uniformVoteWeight) AddNewVote(
p Phase, pubKeyBytes bls.SerializedPublicKey, p Phase, pubKeys []*bls_cosi.PublicKeyWrapper,
sig *bls_core.Sign, headerHash common.Hash, sig *bls_core.Sign, headerHash common.Hash,
height, viewID uint64) (*votepower.Ballot, error) { height, viewID uint64) (*votepower.Ballot, error) {
pubKeysBytes := make([]bls.SerializedPublicKey, len(pubKeys))
return v.SubmitVote(p, pubKeyBytes, sig, headerHash, height, viewID) for i, pubKey := range pubKeys {
pubKeysBytes[i] = pubKey.Bytes
}
return v.SubmitVote(p, pubKeysBytes, sig, headerHash, height, viewID)
} }
// IsQuorumAchieved .. // IsQuorumAchieved ..

@ -1,6 +1,7 @@
package quorum package quorum
import ( import (
"bytes"
"encoding/json" "encoding/json"
"math/big" "math/big"
@ -57,19 +58,40 @@ func (v *stakedVoteWeight) Policy() Policy {
// AddNewVote .. // AddNewVote ..
func (v *stakedVoteWeight) AddNewVote( func (v *stakedVoteWeight) AddNewVote(
p Phase, pubKeyBytes bls.SerializedPublicKey, p Phase, pubKeys []*bls_cosi.PublicKeyWrapper,
sig *bls_core.Sign, headerHash common.Hash, sig *bls_core.Sign, headerHash common.Hash,
height, viewID uint64) (*votepower.Ballot, error) { height, viewID uint64) (*votepower.Ballot, error) {
// TODO(audit): pass in sig as byte[] too, so no need to serialize pubKeysBytes := make([]bls.SerializedPublicKey, len(pubKeys))
ballet, err := v.SubmitVote(p, pubKeyBytes, sig, headerHash, height, viewID) signerAddr := common.Address{}
for i, pubKey := range pubKeys {
voter, ok := v.roster.Voters[pubKey.Bytes]
if !ok {
return nil, errors.Errorf("Signer not in committee: %x", pubKey.Bytes)
}
if i == 0 {
signerAddr = voter.EarningAccount
} else {
if bytes.Compare(signerAddr.Bytes(), voter.EarningAccount[:]) != 0 && !voter.IsHarmonyNode {
return nil, errors.Errorf("Multiple signer accounts used in multi-sig: %x, %x", signerAddr.Bytes(), voter.EarningAccount)
}
}
pubKeysBytes[i] = pubKey.Bytes
}
ballet, err := v.SubmitVote(p, pubKeysBytes, sig, headerHash, height, viewID)
if err != nil { if err != nil {
return ballet, err return ballet, err
} }
// Accumulate total voting power // Accumulate total voting power
additionalVotePower := v.roster.Voters[pubKeyBytes].OverallPercent additionalVotePower := numeric.NewDec(0)
for _, pubKeyBytes := range pubKeysBytes {
additionalVotePower = additionalVotePower.Add(v.roster.Voters[pubKeyBytes].OverallPercent)
}
tallyQuorum := func() *tallyAndQuorum { tallyQuorum := func() *tallyAndQuorum {
switch p { switch p {
case Prepare: case Prepare:
@ -83,7 +105,6 @@ func (v *stakedVoteWeight) AddNewVote(
return nil return nil
} }
}() }()
tallyQuorum.tally = tallyQuorum.tally.Add(additionalVotePower) tallyQuorum.tally = tallyQuorum.tally.Add(additionalVotePower)
t := v.QuorumThreshold() t := v.QuorumThreshold()

@ -114,7 +114,7 @@ func sign(d Decider, k secretKeyMap, p Phase) {
for k, v := range k { for k, v := range k {
sig := v.Sign(msg) sig := v.Sign(msg)
// TODO Make upstream test provide meaningful test values // TODO Make upstream test provide meaningful test values
d.AddNewVote(p, k, sig, common.Hash{}, 0, 0) d.AddNewVote(p, []*bls.PublicKeyWrapper{{Bytes: k}}, sig, common.Hash{}, 0, 0)
} }
} }

@ -80,7 +80,7 @@ type ParticipantTracker interface {
type SignatoryTracker interface { type SignatoryTracker interface {
ParticipantTracker ParticipantTracker
SubmitVote( SubmitVote(
p Phase, pubkey bls.SerializedPublicKey, p Phase, pubkeys []bls.SerializedPublicKey,
sig *bls_core.Sign, headerHash common.Hash, sig *bls_core.Sign, headerHash common.Hash,
height, viewID uint64, height, viewID uint64,
) (*votepower.Ballot, error) ) (*votepower.Ballot, error)
@ -117,7 +117,7 @@ type Decider interface {
SetVoters(subCommittee *shard.Committee, epoch *big.Int) (*TallyResult, error) SetVoters(subCommittee *shard.Committee, epoch *big.Int) (*TallyResult, error)
Policy() Policy Policy() Policy
AddNewVote( AddNewVote(
p Phase, pubkey bls.SerializedPublicKey, p Phase, pubkeys []*bls_cosi.PublicKeyWrapper,
sig *bls_core.Sign, headerHash common.Hash, sig *bls_core.Sign, headerHash common.Hash,
height, viewID uint64, height, viewID uint64,
) (*votepower.Ballot, error) ) (*votepower.Ballot, error)
@ -168,10 +168,29 @@ type depInject struct {
func (s *cIdentities) AggregateVotes(p Phase) *bls_core.Sign { func (s *cIdentities) AggregateVotes(p Phase) *bls_core.Sign {
ballots := s.ReadAllBallots(p) ballots := s.ReadAllBallots(p)
sigs := make([]*bls_core.Sign, 0, len(ballots)) sigs := make([]*bls_core.Sign, 0, len(ballots))
collectedKeys := map[bls_cosi.SerializedPublicKey]struct{}{}
for _, ballot := range ballots { for _, ballot := range ballots {
sig := &bls_core.Sign{} sig := &bls_core.Sign{}
// NOTE invariant that shouldn't happen by now // NOTE invariant that shouldn't happen by now
// but pointers are pointers // but pointers are pointers
// If the multisig from any of the signers in this ballot are already collected,
// we need to skip this ballot as its multisig is a duplicate.
alreadyCollected := false
for _, key := range ballot.SignerPubKeys {
if _, ok := collectedKeys[key]; ok {
alreadyCollected = true
break
}
}
if alreadyCollected {
continue
}
for _, key := range ballot.SignerPubKeys {
collectedKeys[key] = struct{}{}
}
if ballot != nil { if ballot != nil {
sig.DeserializeHexStr(common.Bytes2Hex(ballot.Signature)) sig.DeserializeHexStr(common.Bytes2Hex(ballot.Signature))
sigs = append(sigs, sig) sigs = append(sigs, sig)
@ -230,30 +249,37 @@ func (s *cIdentities) SignersCount(p Phase) int64 {
} }
func (s *cIdentities) SubmitVote( func (s *cIdentities) SubmitVote(
p Phase, pubkey bls.SerializedPublicKey, p Phase, pubkeys []bls.SerializedPublicKey,
sig *bls_core.Sign, headerHash common.Hash, sig *bls_core.Sign, headerHash common.Hash,
height, viewID uint64, height, viewID uint64,
) (*votepower.Ballot, error) { ) (*votepower.Ballot, error) {
if ballet := s.ReadBallot(p, pubkey); ballet != nil { for _, pubKey := range pubkeys {
return nil, errors.Errorf("vote is already submitted %x", pubkey) if ballet := s.ReadBallot(p, pubKey); ballet != nil {
return nil, errors.Errorf("vote is already submitted %x", pubKey)
}
} }
ballot := &votepower.Ballot{ ballot := &votepower.Ballot{
SignerPubKey: pubkey, SignerPubKeys: pubkeys,
BlockHeaderHash: headerHash, BlockHeaderHash: headerHash,
Signature: common.Hex2Bytes(sig.SerializeToHexStr()), Signature: common.Hex2Bytes(sig.SerializeToHexStr()),
Height: height, Height: height,
ViewID: viewID, ViewID: viewID,
} }
switch p {
case Prepare: // For each of the keys signed in the multi-sig, a separate ballot with the same multisig is recorded
s.prepare.BallotBox[pubkey] = ballot // This way it's easier to check if a specific key already signed or not.
case Commit: for _, pubKey := range pubkeys {
s.commit.BallotBox[pubkey] = ballot switch p {
case ViewChange: case Prepare:
s.viewChange.BallotBox[pubkey] = ballot s.prepare.BallotBox[pubKey] = ballot
default: case Commit:
return nil, errors.Wrapf(errPhaseUnknown, "given: %s", p.String()) s.commit.BallotBox[pubKey] = ballot
case ViewChange:
s.viewChange.BallotBox[pubKey] = ballot
default:
return nil, errors.Wrapf(errPhaseUnknown, "given: %s", p.String())
}
} }
return ballot, nil return ballot, nil
} }

@ -6,6 +6,7 @@ import (
"github.com/harmony-one/harmony/consensus/quorum" "github.com/harmony-one/harmony/consensus/quorum"
"github.com/harmony-one/harmony/consensus/signature" "github.com/harmony-one/harmony/consensus/signature"
"github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/types"
"github.com/harmony-one/harmony/crypto/bls"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node" nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/internal/utils"
"github.com/harmony-one/harmony/p2p" "github.com/harmony-one/harmony/p2p"
@ -21,7 +22,7 @@ func (consensus *Consensus) didReachPrepareQuorum() error {
} }
// Construct and broadcast prepared message // Construct and broadcast prepared message
networkMessage, err := consensus.construct( networkMessage, err := consensus.construct(
msg_pb.MessageType_PREPARED, nil, leaderPriKey, msg_pb.MessageType_PREPARED, nil, []*bls.PrivateKeyWrapper{leaderPriKey},
) )
if err != nil { if err != nil {
consensus.getLogger().Err(err). consensus.getLogger().Err(err).
@ -58,7 +59,7 @@ func (consensus *Consensus) didReachPrepareQuorum() error {
if _, err := consensus.Decider.AddNewVote( if _, err := consensus.Decider.AddNewVote(
quorum.Commit, quorum.Commit,
key.Pub.Bytes, []*bls.PublicKeyWrapper{key.Pub},
key.Pri.SignHash(commitPayload), key.Pri.SignHash(commitPayload),
blockObj.Hash(), blockObj.Hash(),
blockObj.NumberU64(), blockObj.NumberU64(),

@ -5,17 +5,19 @@ import (
"encoding/hex" "encoding/hex"
"time" "time"
"github.com/harmony-one/harmony/crypto/bls"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
msg_pb "github.com/harmony-one/harmony/api/proto/message" msg_pb "github.com/harmony-one/harmony/api/proto/message"
"github.com/harmony-one/harmony/consensus/signature" "github.com/harmony-one/harmony/consensus/signature"
"github.com/harmony-one/harmony/core/types" "github.com/harmony-one/harmony/core/types"
nodeconfig "github.com/harmony-one/harmony/internal/configs/node"
"github.com/harmony-one/harmony/p2p" "github.com/harmony-one/harmony/p2p"
) )
func (consensus *Consensus) onAnnounce(msg *msg_pb.Message) { func (consensus *Consensus) onAnnounce(msg *msg_pb.Message) {
recvMsg, err := ParseFBFTMessage(msg) recvMsg, err := consensus.ParseFBFTMessage(msg)
if err != nil { if err != nil {
consensus.getLogger().Error(). consensus.getLogger().Error().
Err(err). Err(err).
@ -60,11 +62,27 @@ func (consensus *Consensus) onAnnounce(msg *msg_pb.Message) {
func (consensus *Consensus) prepare() { func (consensus *Consensus) prepare() {
groupID := []nodeconfig.GroupID{nodeconfig.NewGroupIDByShardID(nodeconfig.ShardID(consensus.ShardID))} groupID := []nodeconfig.GroupID{nodeconfig.NewGroupIDByShardID(nodeconfig.ShardID(consensus.ShardID))}
for _, key := range consensus.priKey { priKeys := []*bls.PrivateKeyWrapper{}
p2pMsgs := []*NetworkMessage{}
for i, key := range consensus.priKey {
if !consensus.IsValidatorInCommittee(key.Pub.Bytes) { if !consensus.IsValidatorInCommittee(key.Pub.Bytes) {
continue continue
} }
networkMessage, err := consensus.construct(msg_pb.MessageType_PREPARE, nil, &key) priKeys = append(priKeys, &consensus.priKey[i])
if !consensus.MultiSig {
networkMessage, err := consensus.construct(msg_pb.MessageType_PREPARE, nil, []*bls.PrivateKeyWrapper{&key})
if err != nil {
consensus.getLogger().Err(err).
Str("message-type", msg_pb.MessageType_PREPARE.String()).
Msg("could not construct message")
return
}
p2pMsgs = append(p2pMsgs, networkMessage)
}
}
if consensus.MultiSig {
networkMessage, err := consensus.construct(msg_pb.MessageType_PREPARE, nil, priKeys)
if err != nil { if err != nil {
consensus.getLogger().Err(err). consensus.getLogger().Err(err).
Str("message-type", msg_pb.MessageType_PREPARE.String()). Str("message-type", msg_pb.MessageType_PREPARE.String()).
@ -72,11 +90,15 @@ func (consensus *Consensus) prepare() {
return return
} }
p2pMsgs = append(p2pMsgs, networkMessage)
}
for _, p2pMsg := range p2pMsgs {
// TODO: this will not return immediately, may block // TODO: this will not return immediately, may block
if consensus.current.Mode() != Listening { if consensus.current.Mode() != Listening {
if err := consensus.msgSender.SendWithoutRetry( if err := consensus.msgSender.SendWithoutRetry(
groupID, groupID,
p2p.ConstructMessage(networkMessage.Bytes), p2p.ConstructMessage(p2pMsg.Bytes),
); err != nil { ); err != nil {
consensus.getLogger().Warn().Err(err).Msg("[OnAnnounce] Cannot send prepare message") consensus.getLogger().Warn().Err(err).Msg("[OnAnnounce] Cannot send prepare message")
} else { } else {
@ -96,7 +118,7 @@ func (consensus *Consensus) prepare() {
// if onPrepared accepts the prepared message from the leader, then // if onPrepared accepts the prepared message from the leader, then
// it will send a COMMIT message for the leader to receive on the network. // it will send a COMMIT message for the leader to receive on the network.
func (consensus *Consensus) onPrepared(msg *msg_pb.Message) { func (consensus *Consensus) onPrepared(msg *msg_pb.Message) {
recvMsg, err := ParseFBFTMessage(msg) recvMsg, err := consensus.ParseFBFTMessage(msg)
if err != nil { if err != nil {
consensus.getLogger().Debug().Err(err).Msg("[OnPrepared] Unparseable validator message") consensus.getLogger().Debug().Err(err).Msg("[OnPrepared] Unparseable validator message")
return return
@ -191,7 +213,6 @@ func (consensus *Consensus) onPrepared(msg *msg_pb.Message) {
return return
} }
// TODO: genesis account node delay for 1 second,
// this is a temp fix for allows FN nodes to earning reward // this is a temp fix for allows FN nodes to earning reward
if consensus.delayCommit > 0 { if consensus.delayCommit > 0 {
time.Sleep(consensus.delayCommit) time.Sleep(consensus.delayCommit)
@ -213,21 +234,47 @@ func (consensus *Consensus) onPrepared(msg *msg_pb.Message) {
groupID := []nodeconfig.GroupID{ groupID := []nodeconfig.GroupID{
nodeconfig.NewGroupIDByShardID(nodeconfig.ShardID(consensus.ShardID)), nodeconfig.NewGroupIDByShardID(nodeconfig.ShardID(consensus.ShardID)),
} }
for _, key := range consensus.priKey {
priKeys := []*bls.PrivateKeyWrapper{}
p2pMsgs := []*NetworkMessage{}
for i, key := range consensus.priKey {
if !consensus.IsValidatorInCommittee(key.Pub.Bytes) { if !consensus.IsValidatorInCommittee(key.Pub.Bytes) {
continue continue
} }
priKeys = append(priKeys, &consensus.priKey[i])
if !consensus.MultiSig {
networkMessage, err := consensus.construct(msg_pb.MessageType_COMMIT,
commitPayload, []*bls.PrivateKeyWrapper{&key})
if err != nil {
consensus.getLogger().Err(err).
Str("message-type", msg_pb.MessageType_COMMIT.String()).
Msg("could not construct message")
return
}
p2pMsgs = append(p2pMsgs, networkMessage)
}
}
networkMessage, _ := consensus.construct( if consensus.MultiSig {
msg_pb.MessageType_COMMIT, networkMessage, err := consensus.construct(msg_pb.MessageType_COMMIT,
commitPayload, commitPayload, priKeys)
&key, if err != nil {
) consensus.getLogger().Err(err).
Str("message-type", msg_pb.MessageType_COMMIT.String()).
Msg("could not construct message")
return
}
p2pMsgs = append(p2pMsgs, networkMessage)
}
for _, p2pMsg := range p2pMsgs {
// TODO: this will not return immediately, may block
if consensus.current.Mode() != Listening { if consensus.current.Mode() != Listening {
if err := consensus.msgSender.SendWithoutRetry( if err := consensus.msgSender.SendWithoutRetry(
groupID, groupID,
p2p.ConstructMessage(networkMessage.Bytes), p2p.ConstructMessage(p2pMsg.Bytes),
); err != nil { ); err != nil {
consensus.getLogger().Warn().Msg("[OnPrepared] Cannot send commit message!!") consensus.getLogger().Warn().Msg("[OnPrepared] Cannot send commit message!!")
} else { } else {
@ -246,7 +293,7 @@ func (consensus *Consensus) onPrepared(msg *msg_pb.Message) {
} }
func (consensus *Consensus) onCommitted(msg *msg_pb.Message) { func (consensus *Consensus) onCommitted(msg *msg_pb.Message) {
recvMsg, err := ParseFBFTMessage(msg) recvMsg, err := consensus.ParseFBFTMessage(msg)
if err != nil { if err != nil {
consensus.getLogger().Warn().Msg("[OnCommitted] unable to parse msg") consensus.getLogger().Warn().Msg("[OnCommitted] unable to parse msg")
return return

@ -205,7 +205,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
consensus.getLogger().Info(). consensus.getLogger().Info().
Int64("have", consensus.Decider.SignersCount(quorum.ViewChange)). Int64("have", consensus.Decider.SignersCount(quorum.ViewChange)).
Int64("need", consensus.Decider.TwoThirdsSignersCount()). Int64("need", consensus.Decider.TwoThirdsSignersCount()).
Str("validatorPubKey", recvMsg.SenderPubkey.Bytes.Hex()). Interface("validatorPubKeys", recvMsg.SenderPubkeys).
Msg("[onViewChange] Received Enough View Change Messages") Msg("[onViewChange] Received Enough View Change Messages")
return return
} }
@ -214,7 +214,8 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
return return
} }
senderKey := recvMsg.SenderPubkey // already checked the length of SenderPubkeys in onViewChangeSanityCheck
senderKey := recvMsg.SenderPubkeys[0]
consensus.vcLock.Lock() consensus.vcLock.Lock()
defer consensus.vcLock.Unlock() defer consensus.vcLock.Unlock()
@ -315,7 +316,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
Str("validatorPubKey", senderKey.Bytes.Hex()). Str("validatorPubKey", senderKey.Bytes.Hex()).
Msg("[onViewChange] Add M2 (NIL) type message") Msg("[onViewChange] Add M2 (NIL) type message")
consensus.nilSigs[recvMsg.ViewID][senderKey.Bytes.Hex()] = recvMsg.ViewchangeSig consensus.nilSigs[recvMsg.ViewID][senderKey.Bytes.Hex()] = recvMsg.ViewchangeSig
consensus.nilBitmap[recvMsg.ViewID].SetKey(recvMsg.SenderPubkey.Bytes, true) // Set the bitmap indicating that this validator signed. consensus.nilBitmap[recvMsg.ViewID].SetKey(senderKey.Bytes, true) // Set the bitmap indicating that this validator signed.
} else { // m1 type message } else { // m1 type message
if consensus.BlockVerifier(preparedBlock); err != nil { if consensus.BlockVerifier(preparedBlock); err != nil {
consensus.getLogger().Error().Err(err).Msg("[onViewChange] Prepared block verification failed") consensus.getLogger().Error().Err(err).Msg("[onViewChange] Prepared block verification failed")
@ -328,7 +329,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
Msg("[onViewChange] Already Received M1 Message From the Validator") Msg("[onViewChange] Already Received M1 Message From the Validator")
return return
} }
if !recvMsg.ViewchangeSig.VerifyHash(recvMsg.SenderPubkey.Object, recvMsg.Payload) { if !recvMsg.ViewchangeSig.VerifyHash(senderKey.Object, recvMsg.Payload) {
consensus.getLogger().Warn().Msg("[onViewChange] Failed to Verify Signature for M1 Type Viewchange Message") consensus.getLogger().Warn().Msg("[onViewChange] Failed to Verify Signature for M1 Type Viewchange Message")
return return
} }
@ -375,7 +376,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
copy(preparedMsg.BlockHash[:], recvMsg.Payload[:32]) copy(preparedMsg.BlockHash[:], recvMsg.Payload[:32])
preparedMsg.Payload = make([]byte, len(recvMsg.Payload)-32) preparedMsg.Payload = make([]byte, len(recvMsg.Payload)-32)
copy(preparedMsg.Payload[:], recvMsg.Payload[32:]) copy(preparedMsg.Payload[:], recvMsg.Payload[32:])
preparedMsg.SenderPubkey = newLeaderKey preparedMsg.SenderPubkeys = []*bls.PublicKeyWrapper{newLeaderKey}
consensus.getLogger().Info().Msg("[onViewChange] New Leader Prepared Message Added") consensus.getLogger().Info().Msg("[onViewChange] New Leader Prepared Message Added")
consensus.FBFTLog.AddMessage(&preparedMsg) consensus.FBFTLog.AddMessage(&preparedMsg)
@ -386,7 +387,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
Str("validatorPubKey", senderKey.Bytes.Hex()). Str("validatorPubKey", senderKey.Bytes.Hex()).
Msg("[onViewChange] Add M1 (prepared) type message") Msg("[onViewChange] Add M1 (prepared) type message")
consensus.bhpSigs[recvMsg.ViewID][senderKey.Bytes.Hex()] = recvMsg.ViewchangeSig consensus.bhpSigs[recvMsg.ViewID][senderKey.Bytes.Hex()] = recvMsg.ViewchangeSig
consensus.bhpBitmap[recvMsg.ViewID].SetKey(recvMsg.SenderPubkey.Bytes, true) // Set the bitmap indicating that this validator signed. consensus.bhpBitmap[recvMsg.ViewID].SetKey(senderKey.Bytes, true) // Set the bitmap indicating that this validator signed.
} }
// check and add viewID (m3 type) message signature // check and add viewID (m3 type) message signature
@ -398,7 +399,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
} }
viewIDHash := make([]byte, 8) viewIDHash := make([]byte, 8)
binary.LittleEndian.PutUint64(viewIDHash, recvMsg.ViewID) binary.LittleEndian.PutUint64(viewIDHash, recvMsg.ViewID)
if !recvMsg.ViewidSig.VerifyHash(recvMsg.SenderPubkey.Object, viewIDHash) { if !recvMsg.ViewidSig.VerifyHash(senderKey.Object, viewIDHash) {
consensus.getLogger().Warn(). consensus.getLogger().Warn().
Uint64("MsgViewID", recvMsg.ViewID). Uint64("MsgViewID", recvMsg.ViewID).
Msg("[onViewChange] Failed to Verify M3 Message Signature") Msg("[onViewChange] Failed to Verify M3 Message Signature")
@ -410,7 +411,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
consensus.viewIDSigs[recvMsg.ViewID][senderKey.Bytes.Hex()] = recvMsg.ViewidSig consensus.viewIDSigs[recvMsg.ViewID][senderKey.Bytes.Hex()] = recvMsg.ViewidSig
// Set the bitmap indicating that this validator signed. // Set the bitmap indicating that this validator signed.
consensus.viewIDBitmap[recvMsg.ViewID].SetKey(recvMsg.SenderPubkey.Bytes, true) consensus.viewIDBitmap[recvMsg.ViewID].SetKey(senderKey.Bytes, true)
consensus.getLogger().Info(). consensus.getLogger().Info().
Int("have", len(consensus.viewIDSigs[recvMsg.ViewID])). Int("have", len(consensus.viewIDSigs[recvMsg.ViewID])).
Int64("total", consensus.Decider.ParticipantsCount()). Int64("total", consensus.Decider.ParticipantsCount()).
@ -462,7 +463,7 @@ func (consensus *Consensus) onViewChange(msg *msg_pb.Message) {
if _, err := consensus.Decider.SubmitVote( if _, err := consensus.Decider.SubmitVote(
quorum.Commit, quorum.Commit,
key.Pub.Bytes, []bls.SerializedPublicKey{key.Pub.Bytes},
key.Pri.SignHash(commitPayload), key.Pri.SignHash(commitPayload),
common.BytesToHash(consensus.blockHash[:]), common.BytesToHash(consensus.blockHash[:]),
block.NumberU64(), block.NumberU64(),
@ -516,7 +517,12 @@ func (consensus *Consensus) onNewView(msg *msg_pb.Message) {
return return
} }
senderKey := recvMsg.SenderPubkey if len(recvMsg.SenderPubkeys) != 1 {
consensus.getLogger().Error().Msg("[onNewView] multiple signers in view change message.")
return
}
senderKey := recvMsg.SenderPubkeys[0]
consensus.vcLock.Lock() consensus.vcLock.Lock()
defer consensus.vcLock.Unlock() defer consensus.vcLock.Unlock()
@ -619,7 +625,7 @@ func (consensus *Consensus) onNewView(msg *msg_pb.Message) {
copy(preparedMsg.BlockHash[:], blockHash[:]) copy(preparedMsg.BlockHash[:], blockHash[:])
preparedMsg.Payload = make([]byte, len(recvMsg.Payload)-32) preparedMsg.Payload = make([]byte, len(recvMsg.Payload)-32)
copy(preparedMsg.Payload[:], recvMsg.Payload[32:]) copy(preparedMsg.Payload[:], recvMsg.Payload[32:])
preparedMsg.SenderPubkey = senderKey preparedMsg.SenderPubkeys = []*bls.PublicKeyWrapper{senderKey}
consensus.FBFTLog.AddMessage(&preparedMsg) consensus.FBFTLog.AddMessage(&preparedMsg)
if hasBlock { if hasBlock {
@ -648,24 +654,46 @@ func (consensus *Consensus) onNewView(msg *msg_pb.Message) {
preparedBlock.Epoch(), preparedBlock.Hash(), preparedBlock.NumberU64(), preparedBlock.Header().ViewID().Uint64()) preparedBlock.Epoch(), preparedBlock.Hash(), preparedBlock.NumberU64(), preparedBlock.Header().ViewID().Uint64())
groupID := []nodeconfig.GroupID{ groupID := []nodeconfig.GroupID{
nodeconfig.NewGroupIDByShardID(nodeconfig.ShardID(consensus.ShardID))} nodeconfig.NewGroupIDByShardID(nodeconfig.ShardID(consensus.ShardID))}
for _, key := range consensus.priKey {
priKeys := []*bls.PrivateKeyWrapper{}
p2pMsgs := []*NetworkMessage{}
for i, key := range consensus.priKey {
if !consensus.IsValidatorInCommittee(key.Pub.Bytes) { if !consensus.IsValidatorInCommittee(key.Pub.Bytes) {
continue continue
} }
priKeys = append(priKeys, &consensus.priKey[i])
if !consensus.MultiSig {
network, err := consensus.construct(
msg_pb.MessageType_COMMIT,
commitPayload,
[]*bls.PrivateKeyWrapper{&key},
)
if err != nil {
consensus.getLogger().Err(err).Msg("could not create commit message")
return
}
p2pMsgs = append(p2pMsgs, network)
}
}
if consensus.MultiSig {
network, err := consensus.construct( network, err := consensus.construct(
msg_pb.MessageType_COMMIT, msg_pb.MessageType_COMMIT,
commitPayload, commitPayload,
&key, priKeys,
) )
if err != nil { if err != nil {
consensus.getLogger().Err(err).Msg("could not create commit message") consensus.getLogger().Err(err).Msg("could not create commit message")
return return
} }
msgToSend := network.Bytes p2pMsgs = append(p2pMsgs, network)
}
for _, p2pMsg := range p2pMsgs {
consensus.getLogger().Info().Msg("onNewView === commit") consensus.getLogger().Info().Msg("onNewView === commit")
consensus.host.SendMessageToGroups( consensus.host.SendMessageToGroups(
groupID, groupID,
p2p.ConstructMessage(msgToSend), p2p.ConstructMessage(p2pMsg.Bytes),
) )
} }
consensus.getLogger().Info(). consensus.getLogger().Info().

@ -3,6 +3,7 @@ package votepower
import ( import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt"
"math/big" "math/big"
"sort" "sort"
@ -24,23 +25,23 @@ var (
// Ballot is a vote cast by a validator // Ballot is a vote cast by a validator
type Ballot struct { type Ballot struct {
SignerPubKey bls.SerializedPublicKey `json:"bls-public-key"` SignerPubKeys []bls.SerializedPublicKey `json:"bls-public-keys"`
BlockHeaderHash common.Hash `json:"block-header-hash"` BlockHeaderHash common.Hash `json:"block-header-hash"`
Signature []byte `json:"bls-signature"` Signature []byte `json:"bls-signature"`
Height uint64 `json:"block-height"` Height uint64 `json:"block-height"`
ViewID uint64 `json:"view-id"` ViewID uint64 `json:"view-id"`
} }
// MarshalJSON .. // MarshalJSON ..
func (b Ballot) MarshalJSON() ([]byte, error) { func (b Ballot) MarshalJSON() ([]byte, error) {
return json.Marshal(struct { return json.Marshal(struct {
A string `json:"bls-public-key"` A string `json:"bls-public-keys"`
B string `json:"block-header-hash"` B string `json:"block-header-hash"`
C string `json:"bls-signature"` C string `json:"bls-signature"`
E uint64 `json:"block-height"` E uint64 `json:"block-height"`
F uint64 `json:"view-id"` F uint64 `json:"view-id"`
}{ }{
b.SignerPubKey.Hex(), fmt.Sprint(b.SignerPubKeys),
b.BlockHeaderHash.Hex(), b.BlockHeaderHash.Hex(),
hex.EncodeToString(b.Signature), hex.EncodeToString(b.Signature),
b.Height, b.Height,

@ -66,7 +66,7 @@ func AggregateSig(sigs []*bls.Sign) *bls.Sign {
// Mask represents a cosigning participation bitmask. // Mask represents a cosigning participation bitmask.
type Mask struct { type Mask struct {
Bitmap []byte Bitmap []byte
Publics []*bls.PublicKey Publics []*PublicKeyWrapper
PublicsIndex map[SerializedPublicKey]int PublicsIndex map[SerializedPublicKey]int
AggregatePublic *bls.PublicKey AggregatePublic *bls.PublicKey
} }
@ -77,9 +77,9 @@ type Mask struct {
// bitmask to 1 (enabled). // bitmask to 1 (enabled).
func NewMask(publics []PublicKeyWrapper, myKey *PublicKeyWrapper) (*Mask, error) { func NewMask(publics []PublicKeyWrapper, myKey *PublicKeyWrapper) (*Mask, error) {
index := map[SerializedPublicKey]int{} index := map[SerializedPublicKey]int{}
publicKeys := make([]*bls.PublicKey, len(publics)) publicKeys := make([]*PublicKeyWrapper, len(publics))
for i, key := range publics { for i, key := range publics {
publicKeys[i] = key.Object publicKeys[i] = &publics[i]
index[key.Bytes] = i index[key.Bytes] = i
} }
m := &Mask{ m := &Mask{
@ -135,11 +135,11 @@ func (m *Mask) SetMask(mask []byte) error {
msk := byte(1) << uint(i&7) msk := byte(1) << uint(i&7)
if ((m.Bitmap[byt] & msk) == 0) && ((mask[byt] & msk) != 0) { if ((m.Bitmap[byt] & msk) == 0) && ((mask[byt] & msk) != 0) {
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 0 to 1 m.Bitmap[byt] ^= msk // flip bit in Bitmap from 0 to 1
m.AggregatePublic.Add(m.Publics[i]) m.AggregatePublic.Add(m.Publics[i].Object)
} }
if ((m.Bitmap[byt] & msk) != 0) && ((mask[byt] & msk) == 0) { if ((m.Bitmap[byt] & msk) != 0) && ((mask[byt] & msk) == 0) {
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 1 to 0 m.Bitmap[byt] ^= msk // flip bit in Bitmap from 1 to 0
m.AggregatePublic.Sub(m.Publics[i]) m.AggregatePublic.Sub(m.Publics[i].Object)
} }
} }
return nil return nil
@ -155,11 +155,11 @@ func (m *Mask) SetBit(i int, enable bool) error {
msk := byte(1) << uint(i&7) msk := byte(1) << uint(i&7)
if ((m.Bitmap[byt] & msk) == 0) && enable { if ((m.Bitmap[byt] & msk) == 0) && enable {
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 0 to 1 m.Bitmap[byt] ^= msk // flip bit in Bitmap from 0 to 1
m.AggregatePublic.Add(m.Publics[i]) m.AggregatePublic.Add(m.Publics[i].Object)
} }
if ((m.Bitmap[byt] & msk) != 0) && !enable { if ((m.Bitmap[byt] & msk) != 0) && !enable {
m.Bitmap[byt] ^= msk // flip bit in Bitmap from 1 to 0 m.Bitmap[byt] ^= msk // flip bit in Bitmap from 1 to 0
m.AggregatePublic.Sub(m.Publics[i]) m.AggregatePublic.Sub(m.Publics[i].Object)
} }
return nil return nil
} }
@ -173,17 +173,37 @@ func (m *Mask) GetPubKeyFromMask(flag bool) []*bls.PublicKey {
msk := byte(1) << uint(i&7) msk := byte(1) << uint(i&7)
if flag { if flag {
if (m.Bitmap[byt] & msk) != 0 { if (m.Bitmap[byt] & msk) != 0 {
pubKeys = append(pubKeys, m.Publics[i]) pubKeys = append(pubKeys, m.Publics[i].Object)
} }
} else { } else {
if (m.Bitmap[byt] & msk) == 0 { if (m.Bitmap[byt] & msk) == 0 {
pubKeys = append(pubKeys, m.Publics[i]) pubKeys = append(pubKeys, m.Publics[i].Object)
} }
} }
} }
return pubKeys return pubKeys
} }
// GetSignedPubKeysFromBitmap will return pubkeys that are signed based on the specified bitmap.
func (m *Mask) GetSignedPubKeysFromBitmap(bitmap []byte) ([]*PublicKeyWrapper, error) {
if m.Len() != len(bitmap) {
return nil, errors.Errorf(
"mismatching bitmap lengths expectedBitmapLength %d providedBitmapLength %d",
m.Len(),
len(bitmap),
)
}
pubKeys := []*PublicKeyWrapper{}
for i := range m.Publics {
byt := i >> 3
msk := byte(1) << uint(i&7)
if (bitmap[byt] & msk) != 0 {
pubKeys = append(pubKeys, m.Publics[i])
}
}
return pubKeys, nil
}
// IndexEnabled checks whether the given index is enabled in the Bitmap or not. // IndexEnabled checks whether the given index is enabled in the Bitmap or not.
func (m *Mask) IndexEnabled(i int) (bool, error) { func (m *Mask) IndexEnabled(i int) (bool, error) {
if i >= len(m.Publics) { if i >= len(m.Publics) {
@ -213,6 +233,25 @@ func (m *Mask) SetKey(public SerializedPublicKey, enable bool) error {
return errors.New("key not found") return errors.New("key not found")
} }
// SetKeysAtomic set the bit in the Bitmap for the given cosigners only when all the cosigners are present in the map.
func (m *Mask) SetKeysAtomic(publics []*PublicKeyWrapper, enable bool) error {
indexes := make([]int, len(publics))
for i, key := range publics {
index, found := m.PublicsIndex[key.Bytes]
if !found {
return errors.New("key not found")
}
indexes[i] = index
}
for _, index := range indexes {
err := m.SetBit(index, enable)
if err != nil {
return err
}
}
return nil
}
// CountEnabled returns the number of enabled nodes in the CoSi participation // CountEnabled returns the number of enabled nodes in the CoSi participation
// Bitmap. // Bitmap.
func (m *Mask) CountEnabled() int { func (m *Mask) CountEnabled() int {

@ -226,6 +226,12 @@ func TestEnableKeyFunctions(test *testing.T) {
if err := mask.SetKey(pubKey4.Bytes, true); err == nil { if err := mask.SetKey(pubKey4.Bytes, true); err == nil {
test.Error("Expected key nout found error") test.Error("Expected key nout found error")
} }
enabledKeysFromBitmap, _ := mask.GetSignedPubKeysFromBitmap(mask.Bitmap)
if len(enabledKeysFromBitmap) != 1 {
test.Error("Count of enabled keys from bitmap doesn't match")
}
} }
func TestCopyParticipatingMask(test *testing.T) { func TestCopyParticipatingMask(test *testing.T) {

@ -1,20 +0,0 @@
package pki
import (
"encoding/binary"
"github.com/harmony-one/bls/ffi/go/bls"
)
func init() {
bls.Init(bls.BLS12_381)
}
// 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
}

@ -1,21 +0,0 @@
package pki
import (
"encoding/binary"
"testing"
"time"
"github.com/harmony-one/bls/ffi/go/bls"
)
func TestGetBLSPrivateKeyFromInt(test *testing.T) {
t := time.Now().UnixNano()
privateKey1 := GetBLSPrivateKeyFromInt(int(t))
priKey := [32]byte{}
binary.LittleEndian.PutUint32(priKey[:], uint32(t))
var privateKey2 bls.SecretKey
privateKey2.SetLittleEndian(priKey[:])
if !privateKey1.IsEqual(&privateKey2) {
test.Error("two public address should be equal")
}
}

@ -355,11 +355,12 @@ type withError struct {
} }
var ( var (
errNotRightKeySize = errors.New("key received over wire is wrong size") errNotRightKeySize = errors.New("key received over wire is wrong size")
errNoSenderPubKey = errors.New("no sender public BLS key in message") errNoSenderPubKey = errors.New("no sender public BLS key in message")
errWrongShardID = errors.New("wrong shard id") errWrongSizeOfBitmap = errors.New("wrong size of sender bitmap")
errInvalidNodeMsg = errors.New("invalid node message") errWrongShardID = errors.New("wrong shard id")
errIgnoreBeaconMsg = errors.New("ignore beacon sync block") errInvalidNodeMsg = errors.New("invalid node message")
errIgnoreBeaconMsg = errors.New("ignore beacon sync block")
) )
// validateNodeMessage validate node message // validateNodeMessage validate node message
@ -478,29 +479,30 @@ func (node *Node) validateShardBoundMessage(
} }
maybeCon, maybeVC := m.GetConsensus(), m.GetViewchange() maybeCon, maybeVC := m.GetConsensus(), m.GetViewchange()
senderKey := bls.SerializedPublicKey{} senderKey := []byte{}
senderBitmap := []byte{}
if maybeCon != nil { if maybeCon != nil {
if maybeCon.ShardId != node.Consensus.ShardID { if maybeCon.ShardId != node.Consensus.ShardID {
atomic.AddUint32(&node.NumInvalidMessages, 1) atomic.AddUint32(&node.NumInvalidMessages, 1)
return nil, nil, true, errors.WithStack(errWrongShardID) return nil, nil, true, errors.WithStack(errWrongShardID)
} }
copy(senderKey[:], maybeCon.SenderPubkey[:]) senderKey = maybeCon.SenderPubkey
if len(maybeCon.SenderPubkeyBitmap) > 0 {
senderBitmap = maybeCon.SenderPubkeyBitmap
}
} else if maybeVC != nil { } else if maybeVC != nil {
if maybeVC.ShardId != node.Consensus.ShardID { if maybeVC.ShardId != node.Consensus.ShardID {
atomic.AddUint32(&node.NumInvalidMessages, 1) atomic.AddUint32(&node.NumInvalidMessages, 1)
return nil, nil, true, errors.WithStack(errWrongShardID) return nil, nil, true, errors.WithStack(errWrongShardID)
} }
copy(senderKey[:], maybeVC.SenderPubkey) senderKey = maybeVC.SenderPubkey
} else { } else {
atomic.AddUint32(&node.NumInvalidMessages, 1) atomic.AddUint32(&node.NumInvalidMessages, 1)
return nil, nil, true, errors.WithStack(errNoSenderPubKey) return nil, nil, true, errors.WithStack(errNoSenderPubKey)
} }
if len(senderKey) != bls.PublicKeySizeInBytes {
atomic.AddUint32(&node.NumInvalidMessages, 1)
return nil, nil, true, errors.WithStack(errNotRightKeySize)
}
// ignore mesage not intended for validator // ignore mesage not intended for validator
// but still forward them to the network // but still forward them to the network
if !node.Consensus.IsLeader() { if !node.Consensus.IsLeader() {
@ -511,13 +513,29 @@ func (node *Node) validateShardBoundMessage(
} }
} }
if !node.Consensus.IsValidatorInCommittee(senderKey) { serializedKey := bls.SerializedPublicKey{}
atomic.AddUint32(&node.NumSlotMessages, 1) if len(senderKey) > 0 {
return nil, nil, true, errors.WithStack(shard.ErrValidNotInCommittee) if len(senderKey) != bls.PublicKeySizeInBytes {
atomic.AddUint32(&node.NumInvalidMessages, 1)
return nil, nil, true, errors.WithStack(errNotRightKeySize)
}
copy(serializedKey[:], senderKey)
if !node.Consensus.IsValidatorInCommittee(serializedKey) {
atomic.AddUint32(&node.NumSlotMessages, 1)
return nil, nil, true, errors.WithStack(shard.ErrValidNotInCommittee)
}
} else {
count := node.Consensus.Decider.ParticipantsCount()
if (count+7)>>3 != int64(len(senderBitmap)) {
return nil, nil, true, errors.WithStack(errWrongSizeOfBitmap)
}
} }
atomic.AddUint32(&node.NumValidMessages, 1) atomic.AddUint32(&node.NumValidMessages, 1)
return &m, &senderKey, false, nil
// serializedKey will be empty for multiSig sender
return &m, &serializedKey, false, nil
} }
var ( var (

@ -33,7 +33,7 @@ var (
// explorerMessageHandler passes received message in node_handler to explorer service // explorerMessageHandler passes received message in node_handler to explorer service
func (node *Node) explorerMessageHandler(ctx context.Context, msg *msg_pb.Message) error { func (node *Node) explorerMessageHandler(ctx context.Context, msg *msg_pb.Message) error {
if msg.Type == msg_pb.MessageType_COMMITTED { if msg.Type == msg_pb.MessageType_COMMITTED {
recvMsg, err := consensus.ParseFBFTMessage(msg) recvMsg, err := node.Consensus.ParseFBFTMessage(msg)
if err != nil { if err != nil {
utils.Logger().Error().Err(err). utils.Logger().Error().Err(err).
Msg("[Explorer] onCommitted unable to parse msg") Msg("[Explorer] onCommitted unable to parse msg")
@ -79,7 +79,7 @@ func (node *Node) explorerMessageHandler(ctx context.Context, msg *msg_pb.Messag
node.commitBlockForExplorer(block) node.commitBlockForExplorer(block)
} else if msg.Type == msg_pb.MessageType_PREPARED { } else if msg.Type == msg_pb.MessageType_PREPARED {
recvMsg, err := consensus.ParseFBFTMessage(msg) recvMsg, err := node.Consensus.ParseFBFTMessage(msg)
if err != nil { if err != nil {
utils.Logger().Error().Err(err).Msg("[Explorer] Unable to parse Prepared msg") utils.Logger().Error().Err(err).Msg("[Explorer] Unable to parse Prepared msg")
return err return err

@ -1,6 +1,7 @@
package slash package slash
import ( import (
"bytes"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"math/big" "math/big"
@ -73,9 +74,9 @@ type ConflictingVotes struct {
// Vote is the vote of the double signer // Vote is the vote of the double signer
type Vote struct { type Vote struct {
SignerPubKey bls.SerializedPublicKey `json:"bls-public-key"` SignerPubKeys []bls.SerializedPublicKey `json:"bls-public-keys"`
BlockHeaderHash common.Hash `json:"block-header-hash"` BlockHeaderHash common.Hash `json:"block-header-hash"`
Signature []byte `json:"bls-signature"` Signature []byte `json:"bls-signature"`
} }
// Record is an proof of a slashing made by a witness of a double-signing event // Record is an proof of a slashing made by a witness of a double-signing event
@ -113,13 +114,13 @@ func (r Records) String() string {
} }
var ( var (
errBallotSignerKeysNotSame = errors.New("conflicting ballots must have same signer key") errNoMatchingDoubleSignKeys = errors.New("no matching double sign keys")
errReporterAndOffenderSame = errors.New("reporter and offender cannot be same") errReporterAndOffenderSame = errors.New("reporter and offender cannot be same")
errAlreadyBannedValidator = errors.New("cannot slash on already banned validator") errAlreadyBannedValidator = errors.New("cannot slash on already banned validator")
errSignerKeyNotRightSize = errors.New("bls keys from slash candidate not right side") errSignerKeyNotRightSize = errors.New("bls keys from slash candidate not right side")
errSlashFromFutureEpoch = errors.New("cannot have slash from future epoch") errSlashFromFutureEpoch = errors.New("cannot have slash from future epoch")
errSlashBeforeStakingEpoch = errors.New("cannot have slash before staking epoch") errSlashBeforeStakingEpoch = errors.New("cannot have slash before staking epoch")
errSlashBlockNoConflict = errors.New("cannot slash for signing on non-conflicting blocks") errSlashBlockNoConflict = errors.New("cannot slash for signing on non-conflicting blocks")
) )
// MarshalJSON .. // MarshalJSON ..
@ -170,23 +171,31 @@ func Verify(
first, second := first, second :=
candidate.Evidence.FirstVote, candidate.Evidence.FirstVote,
candidate.Evidence.SecondVote candidate.Evidence.SecondVote
k1, k2 := len(first.SignerPubKey), len(second.SignerPubKey)
if k1 != bls.PublicKeySizeInBytes || for _, pubKey := range append(first.SignerPubKeys, second.SignerPubKeys...) {
k2 != bls.PublicKeySizeInBytes { if len(pubKey) != bls.PublicKeySizeInBytes {
return errors.Wrapf( return errors.Wrapf(
errSignerKeyNotRightSize, "cast key %d double-signed key %d", k1, k2, errSignerKeyNotRightSize, "double-signed key %x", pubKey,
) )
}
} }
if first.BlockHeaderHash == second.BlockHeaderHash { if first.BlockHeaderHash == second.BlockHeaderHash {
return errors.Wrapf(errSlashBlockNoConflict, "first %v+ second %v+", first, second) return errors.Wrapf(errSlashBlockNoConflict, "first %v+ second %v+", first, second)
} }
if shard.CompareBLSPublicKey(first.SignerPubKey, second.SignerPubKey) != 0 { doubleSignKeys := []bls.SerializedPublicKey{}
k1, k2 := first.SignerPubKey.Hex(), second.SignerPubKey.Hex() for _, pubKey1 := range first.SignerPubKeys {
return errors.Wrapf( for _, pubKey2 := range second.SignerPubKeys {
errBallotSignerKeysNotSame, "%s %s", k1, k2, if shard.CompareBLSPublicKey(pubKey1, pubKey2) == 0 {
) doubleSignKeys = append(doubleSignKeys, pubKey1)
break
}
}
}
if len(doubleSignKeys) == 0 {
return errNoMatchingDoubleSignKeys
} }
currentEpoch := chain.CurrentBlock().Epoch() currentEpoch := chain.CurrentBlock().Epoch()
// the slash can't come from the future (shard chain's epoch can't be larger than beacon chain's) // the slash can't come from the future (shard chain's epoch can't be larger than beacon chain's)
@ -212,14 +221,25 @@ func Verify(
) )
} }
if addr, err := subCommittee.AddressForBLSKey( signerFound := false
second.SignerPubKey, addrs := []common.Address{}
); err != nil { for _, pubKey := range doubleSignKeys {
return err addr, err := subCommittee.AddressForBLSKey(
} else if *addr != candidate.Evidence.Offender { pubKey,
return errors.Errorf("offender address (%x) does not match the signer's address (%x)", candidate.Evidence.Offender, addr) )
if err != nil {
return err
}
if *addr == candidate.Evidence.Offender {
signerFound = true
}
addrs = append(addrs, *addr)
} }
if !signerFound {
return errors.Errorf("offender address (%x) does not match the signer's address (%x)", candidate.Evidence.Offender, addrs)
}
// last ditch check // last ditch check
if hash.FromRLPNew256( if hash.FromRLPNew256(
candidate.Evidence.FirstVote, candidate.Evidence.FirstVote,
@ -229,8 +249,8 @@ func Verify(
return errors.Wrapf( return errors.Wrapf(
errBallotsNotDiff, errBallotsNotDiff,
"%s %s", "%s %s",
candidate.Evidence.FirstVote.SignerPubKey.Hex(), candidate.Evidence.FirstVote.SignerPubKeys,
candidate.Evidence.SecondVote.SignerPubKey.Hex(), candidate.Evidence.SecondVote.SignerPubKeys,
) )
} }
@ -246,8 +266,13 @@ func Verify(
return err return err
} }
if publicKey, err = bls.BytesToBLSPublicKey(ballot.SignerPubKey[:]); err != nil { for _, pubKey := range ballot.SignerPubKeys {
return err publicKeyObj, err := bls.BytesToBLSPublicKey(pubKey[:])
if err != nil {
return err
}
publicKey.Add(publicKeyObj)
} }
// slash verification only happens in staking era, therefore want commit payload for staking epoch // slash verification only happens in staking era, therefore want commit payload for staking epoch
commitPayload := consensus_sig.ConstructCommitPayload(chain, commitPayload := consensus_sig.ConstructCommitPayload(chain,
@ -504,15 +529,28 @@ func Rate(votingPower *votepower.Roster, records Records) numeric.Dec {
rate := numeric.ZeroDec() rate := numeric.ZeroDec()
for i := range records { for i := range records {
key := records[i].Evidence.SecondVote.SignerPubKey doubleSignKeys := []bls.SerializedPublicKey{}
if card, exists := votingPower.Voters[key]; exists { for _, pubKey1 := range records[i].Evidence.FirstVote.SignerPubKeys {
rate = rate.Add(card.GroupPercent) for _, pubKey2 := range records[i].Evidence.SecondVote.SignerPubKeys {
} else { if shard.CompareBLSPublicKey(pubKey1, pubKey2) == 0 {
utils.Logger().Debug(). doubleSignKeys = append(doubleSignKeys, pubKey1)
RawJSON("roster", []byte(votingPower.String())). break
RawJSON("double-sign-record", []byte(records[i].String())). }
Msg("did not have offenders voter card in roster as expected") }
} }
for _, key := range doubleSignKeys {
if card, exists := votingPower.Voters[key]; exists &&
bytes.Equal(card.EarningAccount[:], records[i].Evidence.Offender[:]) {
rate = rate.Add(card.GroupPercent)
} else {
utils.Logger().Debug().
RawJSON("roster", []byte(votingPower.String())).
RawJSON("double-sign-record", []byte(records[i].String())).
Msg("did not have offenders voter card in roster as expected")
}
}
} }
if rate.LT(oneDoubleSignerRate) { if rate.LT(oneDoubleSignerRate) {

@ -146,7 +146,7 @@ func TestVerify(t *testing.T) {
sdb: defaultTestStateDB(), sdb: defaultTestStateDB(),
chain: defaultFakeBlockChain(), chain: defaultFakeBlockChain(),
expErr: errBallotSignerKeysNotSame, expErr: errNoMatchingDoubleSignKeys,
}, },
{ {
// block is in the future // block is in the future
@ -693,9 +693,9 @@ func TestRate(t *testing.T) {
keyPairs[2].Pub(): numeric.NewDecWithPrec(3, 2), keyPairs[2].Pub(): numeric.NewDecWithPrec(3, 2),
}), }),
records: Records{ records: Records{
makeEmptyRecordWithSecondSignerKey(keyPairs[0].Pub()), makeEmptyRecordWithSignerKey(keyPairs[0].Pub()),
makeEmptyRecordWithSecondSignerKey(keyPairs[1].Pub()), makeEmptyRecordWithSignerKey(keyPairs[1].Pub()),
makeEmptyRecordWithSecondSignerKey(keyPairs[2].Pub()), makeEmptyRecordWithSignerKey(keyPairs[2].Pub()),
}, },
expRate: numeric.NewDecWithPrec(6, 2), expRate: numeric.NewDecWithPrec(6, 2),
}, },
@ -704,7 +704,7 @@ func TestRate(t *testing.T) {
keyPairs[0].Pub(): numeric.NewDecWithPrec(1, 2), keyPairs[0].Pub(): numeric.NewDecWithPrec(1, 2),
}), }),
records: Records{ records: Records{
makeEmptyRecordWithSecondSignerKey(keyPairs[0].Pub()), makeEmptyRecordWithSignerKey(keyPairs[0].Pub()),
}, },
expRate: oneDoubleSignerRate, expRate: oneDoubleSignerRate,
}, },
@ -720,9 +720,9 @@ func TestRate(t *testing.T) {
keyPairs[3].Pub(): numeric.NewDecWithPrec(3, 2), keyPairs[3].Pub(): numeric.NewDecWithPrec(3, 2),
}), }),
records: Records{ records: Records{
makeEmptyRecordWithSecondSignerKey(keyPairs[0].Pub()), makeEmptyRecordWithSignerKey(keyPairs[0].Pub()),
makeEmptyRecordWithSecondSignerKey(keyPairs[1].Pub()), makeEmptyRecordWithSignerKey(keyPairs[1].Pub()),
makeEmptyRecordWithSecondSignerKey(keyPairs[2].Pub()), makeEmptyRecordWithSignerKey(keyPairs[2].Pub()),
}, },
expRate: numeric.NewDecWithPrec(3, 2), expRate: numeric.NewDecWithPrec(3, 2),
}, },
@ -736,9 +736,10 @@ func TestRate(t *testing.T) {
} }
func makeEmptyRecordWithSecondSignerKey(pub bls.SerializedPublicKey) Record { func makeEmptyRecordWithSignerKey(pub bls.SerializedPublicKey) Record {
var r Record var r Record
r.Evidence.SecondVote.SignerPubKey = pub r.Evidence.SecondVote.SignerPubKeys = []bls.SerializedPublicKey{pub}
r.Evidence.FirstVote.SignerPubKeys = []bls.SerializedPublicKey{pub}
return r return r
} }
@ -775,7 +776,7 @@ func defaultSlashRecord() Record {
func makeVoteData(kp blsKeyPair, block *types.Block) Vote { func makeVoteData(kp blsKeyPair, block *types.Block) Vote {
return Vote{ return Vote{
SignerPubKey: kp.Pub(), SignerPubKeys: []bls.SerializedPublicKey{kp.Pub()},
BlockHeaderHash: block.Hash(), BlockHeaderHash: block.Hash(),
Signature: kp.Sign(block), Signature: kp.Sign(block),
} }
@ -1062,7 +1063,7 @@ func copyConflictingVotes(cv ConflictingVotes) ConflictingVotes {
// copyVote makes a deep copy of slash.Vote // copyVote makes a deep copy of slash.Vote
func copyVote(v Vote) Vote { func copyVote(v Vote) Vote {
cp := Vote{ cp := Vote{
SignerPubKey: v.SignerPubKey, SignerPubKeys: v.SignerPubKeys,
BlockHeaderHash: v.BlockHeaderHash, BlockHeaderHash: v.BlockHeaderHash,
} }
if v.Signature != nil { if v.Signature != nil {

@ -47,7 +47,7 @@ func CopyConflictingVotes(cv slash.ConflictingVotes) slash.ConflictingVotes {
// CopyVote makes a deep copy of slash.Vote // CopyVote makes a deep copy of slash.Vote
func CopyVote(v slash.Vote) slash.Vote { func CopyVote(v slash.Vote) slash.Vote {
cp := slash.Vote{ cp := slash.Vote{
SignerPubKey: v.SignerPubKey, SignerPubKeys: v.SignerPubKeys,
BlockHeaderHash: v.BlockHeaderHash, BlockHeaderHash: v.BlockHeaderHash,
} }
if v.Signature != nil { if v.Signature != nil {

@ -89,13 +89,13 @@ var (
} }
nonZeroVote1 = slash.Vote{ nonZeroVote1 = slash.Vote{
SignerPubKey: bls.SerializedPublicKey{1}, SignerPubKeys: []bls.SerializedPublicKey{{1}},
BlockHeaderHash: common.Hash{2}, BlockHeaderHash: common.Hash{2},
Signature: []byte{1, 2, 3}, Signature: []byte{1, 2, 3},
} }
nonZeroVote2 = slash.Vote{ nonZeroVote2 = slash.Vote{
SignerPubKey: bls.SerializedPublicKey{3}, SignerPubKeys: []bls.SerializedPublicKey{{3}},
BlockHeaderHash: common.Hash{4}, BlockHeaderHash: common.Hash{4},
Signature: []byte{4, 5, 6}, Signature: []byte{4, 5, 6},
} }

Loading…
Cancel
Save