pull/53/head
Richard Liu 6 years ago
commit 9e822c04bd
  1. 2
      README.md
  2. 6
      benchmark.go
  3. 7
      client/btctxgen/main.go
  4. 7
      client/client.go
  5. 7
      client/txgen/main.go
  6. 70
      common/message.go
  7. 62
      consensus/consensus.go
  8. 80
      consensus/consensus_leader.go
  9. 20
      consensus/consensus_leader_test.go
  10. 9
      consensus/consensus_test.go
  11. 39
      consensus/consensus_validator.go
  12. 4
      consensus/consensus_validator_test.go
  13. 419
      crypto/cosi.go
  14. 5
      crypto/curve.go
  15. 10
      crypto/suite.go
  16. 21
      crypto/utils.go
  17. 45
      node/node_handler.go
  18. 0
      p2p/message_reader.go
  19. 3
      p2p/peer.go
  20. 11
      proto/client/client.go
  21. 62
      proto/common.go
  22. 21
      proto/consensus/consensus.go
  23. 15
      proto/node/node.go

@ -21,7 +21,7 @@ git clone git@github.com:simple-rules/harmony-benchmark.git
cd harmony-benchmark cd harmony-benchmark
go get github.com/go-stack/stack go get ./...
``` ```
## Usage ## Usage
``` ```

@ -14,6 +14,7 @@ import (
"time" "time"
"github.com/shirou/gopsutil/process" "github.com/shirou/gopsutil/process"
"harmony-benchmark/crypto"
) )
const ( const (
@ -37,6 +38,8 @@ func getLeader(myShardId string, config *[][]string) p2p.Peer {
if status == "leader" && myShardId == shardId { if status == "leader" && myShardId == shardId {
leaderPeer.Ip = ip leaderPeer.Ip = ip
leaderPeer.Port = port leaderPeer.Port = port
priKey := crypto.Hash(ip + ":" + port) // use ip:port as unique private key for now. TODO: use real private key
leaderPeer.PubKey = crypto.GetPublicKeyFromPrivateKey(crypto.Curve, priKey)
} }
} }
return leaderPeer return leaderPeer
@ -49,7 +52,8 @@ func getPeers(myIp, myPort, myShardId string, config *[][]string) []p2p.Peer {
if status != "validator" || ip == myIp && port == myPort || myShardId != shardId { if status != "validator" || ip == myIp && port == myPort || myShardId != shardId {
continue continue
} }
peer := p2p.Peer{Port: port, Ip: ip} priKey := crypto.Hash(ip + ":" + port) // use ip:port as unique private key for now. TODO: use real private key
peer := p2p.Peer{Port: port, Ip: ip, PubKey: crypto.GetPublicKeyFromPrivateKey(crypto.Curve, priKey)}
peerList = append(peerList, peer) peerList = append(peerList, peer)
} }
return peerList return peerList

@ -12,6 +12,7 @@ import (
"harmony-benchmark/log" "harmony-benchmark/log"
"harmony-benchmark/node" "harmony-benchmark/node"
"harmony-benchmark/p2p" "harmony-benchmark/p2p"
proto_node "harmony-benchmark/proto/node"
"sync" "sync"
"time" "time"
@ -219,14 +220,14 @@ func main() {
allCrossTxs = append(allCrossTxs, crossTxs...) allCrossTxs = append(allCrossTxs, crossTxs...)
log.Debug("[Generator] Sending single-shard txs ...", "leader", leader, "numTxs", len(txs), "numCrossTxs", len(crossTxs)) log.Debug("[Generator] Sending single-shard txs ...", "leader", leader, "numTxs", len(txs), "numCrossTxs", len(crossTxs))
msg := node.ConstructTransactionListMessage(txs) msg := proto_node.ConstructTransactionListMessage(txs)
p2p.SendMessage(leader, msg) p2p.SendMessage(leader, msg)
// Note cross shard txs are later sent in batch // Note cross shard txs are later sent in batch
} }
if len(allCrossTxs) > 0 { if len(allCrossTxs) > 0 {
log.Debug("[Generator] Broadcasting cross-shard txs ...", "allCrossTxs", len(allCrossTxs)) log.Debug("[Generator] Broadcasting cross-shard txs ...", "allCrossTxs", len(allCrossTxs))
msg := node.ConstructTransactionListMessage(allCrossTxs) msg := proto_node.ConstructTransactionListMessage(allCrossTxs)
p2p.BroadcastMessage(leaders, msg) p2p.BroadcastMessage(leaders, msg)
// Put cross shard tx into a pending list waiting for proofs from leaders // Put cross shard tx into a pending list waiting for proofs from leaders
@ -243,7 +244,7 @@ func main() {
} }
// Send a stop message to stop the nodes at the end // Send a stop message to stop the nodes at the end
msg := node.ConstructStopMessage() msg := proto_node.ConstructStopMessage()
peers := append(configr.GetValidators(*configFile), leaders...) peers := append(configr.GetValidators(*configFile), leaders...)
p2p.BroadcastMessage(peers, msg) p2p.BroadcastMessage(peers, msg)
} }

@ -6,6 +6,7 @@ import (
"harmony-benchmark/blockchain" "harmony-benchmark/blockchain"
"harmony-benchmark/log" "harmony-benchmark/log"
"harmony-benchmark/p2p" "harmony-benchmark/p2p"
proto_client "harmony-benchmark/proto/client"
"sync" "sync"
) )
@ -21,9 +22,9 @@ type Client struct {
// The message handler for CLIENT/TRANSACTION messages. // The message handler for CLIENT/TRANSACTION messages.
func (client *Client) TransactionMessageHandler(msgPayload []byte) { func (client *Client) TransactionMessageHandler(msgPayload []byte) {
messageType := TransactionMessageType(msgPayload[0]) messageType := proto_client.TransactionMessageType(msgPayload[0])
switch messageType { switch messageType {
case PROOF_OF_LOCK: case proto_client.PROOF_OF_LOCK:
// Decode the list of blockchain.CrossShardTxProof // Decode the list of blockchain.CrossShardTxProof
txDecoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the PROOF_OF_LOCK messge type txDecoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the PROOF_OF_LOCK messge type
proofs := new([]blockchain.CrossShardTxProof) proofs := new([]blockchain.CrossShardTxProof)
@ -90,7 +91,7 @@ func (client *Client) handleProofOfLockMessage(proofs *[]blockchain.CrossShardTx
} }
func (client *Client) broadcastCrossShardTxUnlockMessage(txsToSend *[]blockchain.Transaction) { func (client *Client) broadcastCrossShardTxUnlockMessage(txsToSend *[]blockchain.Transaction) {
p2p.BroadcastMessage(*client.leaders, ConstructUnlockToCommitOrAbortMessage(*txsToSend)) p2p.BroadcastMessage(*client.leaders, proto_client.ConstructUnlockToCommitOrAbortMessage(*txsToSend))
} }
// Create a new Client // Create a new Client

@ -11,6 +11,7 @@ import (
"harmony-benchmark/log" "harmony-benchmark/log"
"harmony-benchmark/node" "harmony-benchmark/node"
"harmony-benchmark/p2p" "harmony-benchmark/p2p"
proto_node "harmony-benchmark/proto/node"
"math/rand" "math/rand"
"strconv" "strconv"
"sync" "sync"
@ -302,14 +303,14 @@ func main() {
allCrossTxs = append(allCrossTxs, crossTxs...) allCrossTxs = append(allCrossTxs, crossTxs...)
log.Debug("[Generator] Sending single-shard txs ...", "leader", leader, "numTxs", len(txs), "numCrossTxs", len(crossTxs)) log.Debug("[Generator] Sending single-shard txs ...", "leader", leader, "numTxs", len(txs), "numCrossTxs", len(crossTxs))
msg := node.ConstructTransactionListMessage(txs) msg := proto_node.ConstructTransactionListMessage(txs)
p2p.SendMessage(leader, msg) p2p.SendMessage(leader, msg)
// Note cross shard txs are later sent in batch // Note cross shard txs are later sent in batch
} }
if len(allCrossTxs) > 0 { if len(allCrossTxs) > 0 {
log.Debug("[Generator] Broadcasting cross-shard txs ...", "allCrossTxs", len(allCrossTxs)) log.Debug("[Generator] Broadcasting cross-shard txs ...", "allCrossTxs", len(allCrossTxs))
msg := node.ConstructTransactionListMessage(allCrossTxs) msg := proto_node.ConstructTransactionListMessage(allCrossTxs)
p2p.BroadcastMessage(leaders, msg) p2p.BroadcastMessage(leaders, msg)
// Put cross shard tx into a pending list waiting for proofs from leaders // Put cross shard tx into a pending list waiting for proofs from leaders
@ -326,7 +327,7 @@ func main() {
} }
// Send a stop message to stop the nodes at the end // Send a stop message to stop the nodes at the end
msg := node.ConstructStopMessage() msg := proto_node.ConstructStopMessage()
peers := append(configr.GetValidators(*configFile), leaders...) peers := append(configr.GetValidators(*configFile), leaders...)
p2p.BroadcastMessage(peers, msg) p2p.BroadcastMessage(peers, msg)
} }

@ -1,70 +0,0 @@
package common
import (
"errors"
)
// TODO: Fix the comments below.
/*
Node will process the content of the p2p message
---- content start -----
1 byte - message category
0x00: COMMITTEE
0x01: NODE...
1 byte - message type
- for COMMITTEE category
0x00: consensus
0x01: sharding ...
- for NODE category
0x00: transaction ...
n - 2 bytes - actual message payload
---- content end -----
*/
// NODE_TYPE_BYTES is # of bytes message category
const NODE_TYPE_BYTES = 1
// ACTION_TYPE_BYTES is # of bytes for message type which can be
// - for COMMITTEE category
// 0x00: consensus
// 0x01: sharding ...
// - for NODE category
// 0x00: transaction ...
const ACTION_TYPE_BYTES = 1
// The CATEGORY of messages
type MessageCategory byte
const (
COMMITTEE MessageCategory = iota
NODE
CLIENT
// TODO: add more types
)
// Get the message category from the p2p message content
func GetMessageCategory(message []byte) (MessageCategory, error) {
if len(message) < NODE_TYPE_BYTES {
return 0, errors.New("Failed to get node type: no data available.")
}
return MessageCategory(message[NODE_TYPE_BYTES-1]), nil
}
// Get the message type from the p2p message content
func GetMessageType(message []byte) (byte, error) {
if len(message) < NODE_TYPE_BYTES+ACTION_TYPE_BYTES {
return 0, errors.New("Failed to get action type: no data available.")
}
return byte(message[NODE_TYPE_BYTES+ACTION_TYPE_BYTES-1]), nil
}
// Get the node message payload from the p2p message content
func GetMessagePayload(message []byte) ([]byte, error) {
if len(message) < NODE_TYPE_BYTES+ACTION_TYPE_BYTES {
return []byte{}, errors.New("Failed to get message payload: no data available.")
}
return message[NODE_TYPE_BYTES+ACTION_TYPE_BYTES:], nil
}

@ -3,8 +3,9 @@ package consensus // consensus
import ( import (
"fmt" "fmt"
"github.com/dedis/kyber"
"harmony-benchmark/blockchain" "harmony-benchmark/blockchain"
"harmony-benchmark/common" "harmony-benchmark/crypto"
"harmony-benchmark/log" "harmony-benchmark/log"
"harmony-benchmark/p2p" "harmony-benchmark/p2p"
"regexp" "regexp"
@ -15,16 +16,20 @@ import (
// Consensus data containing all info related to one round of consensus process // Consensus data containing all info related to one round of consensus process
type Consensus struct { type Consensus struct {
state ConsensusState state ConsensusState
// Signatures collected from validators // Commits collected from validators. A map from node Id to its commitment
commits map[string]string commitments map[uint16]kyber.Point
// Commits collected from validators.
bitmap *crypto.Mask
// Signatures collected from validators // Signatures collected from validators
responses map[string]string responses map[string]string
// List of validators // List of validators
validators []p2p.Peer validators []p2p.Peer
// Leader // Leader
leader p2p.Peer leader p2p.Peer
// private key of current node // private/public keys of current node
priKey string priKey kyber.Scalar
pubKey kyber.Point
// Whether I am leader. False means I am validator // Whether I am leader. False means I am validator
IsLeader bool IsLeader bool
// Leader or validator Id - 2 byte // Leader or validator Id - 2 byte
@ -44,6 +49,8 @@ type Consensus struct {
// Validator specific fields // Validator specific fields
// Blocks received but not done with consensus yet // Blocks received but not done with consensus yet
blocksReceived map[uint32]*BlockConsensusStatus blocksReceived map[uint32]*BlockConsensusStatus
// Commitment secret
secret kyber.Scalar
// Signal channel for starting a new consensus process // Signal channel for starting a new consensus process
ReadySignal chan int ReadySignal chan int
@ -53,10 +60,6 @@ type Consensus struct {
// Called when consensus on a new block is done // Called when consensus on a new block is done
OnConsensusDone func(*blockchain.Block) OnConsensusDone func(*blockchain.Block)
//// Network related fields
msgCategory byte
msgType byte
Log log.Logger Log log.Logger
} }
@ -76,24 +79,37 @@ type BlockConsensusStatus struct {
// FYI, see https://golang.org/doc/effective_go.html?#package-names // FYI, see https://golang.org/doc/effective_go.html?#package-names
func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) *Consensus { func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) *Consensus {
consensus := Consensus{} consensus := Consensus{}
Peers := peers
leaderPeer := leader
selfPeer := p2p.Peer{Port: port, Ip: ip}
if leaderPeer == selfPeer { if leader.Port == port && leader.Ip == ip {
consensus.IsLeader = true consensus.IsLeader = true
} else { } else {
consensus.IsLeader = false consensus.IsLeader = false
} }
consensus.commits = make(map[string]string) consensus.commitments = make(map[uint16]kyber.Point)
consensus.responses = make(map[string]string) consensus.responses = make(map[string]string)
consensus.leader = leaderPeer consensus.leader = leader
consensus.validators = Peers consensus.validators = peers
consensus.priKey = ip + ":" + port // use ip:port as unique key for now
// Initialize cosign bitmap
allPublics := make([]kyber.Point, 0)
for _, validatorPeer := range consensus.validators {
allPublics = append(allPublics, validatorPeer.PubKey)
}
allPublics = append(allPublics, leader.PubKey)
mask, err := crypto.NewMask(crypto.Curve, allPublics, consensus.leader.PubKey)
if err != nil {
panic("Failed to create commitment mask")
}
consensus.bitmap = mask
// Set private key for myself so that I can sign messages.
scalar := crypto.Curve.Scalar()
priKeyInBytes := crypto.Hash(ip + ":" + port)
scalar.UnmarshalBinary(priKeyInBytes[:]) // use ip:port as unique private key for now. TODO: use real private key
consensus.priKey = scalar
consensus.pubKey = crypto.GetPublicKeyFromScalar(crypto.Curve, consensus.priKey)
consensus.consensusId = 0 // or view Id in the original pbft paper consensus.consensusId = 0 // or view Id in the original pbft paper
myShardID, err := strconv.Atoi(ShardID) myShardID, err := strconv.Atoi(ShardID)
@ -111,7 +127,7 @@ func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) *
if err != nil { if err != nil {
consensus.Log.Crit("Regex Compilation Failed", "err", err, "consensus", consensus) consensus.Log.Crit("Regex Compilation Failed", "err", err, "consensus", consensus)
} }
socketId := reg.ReplaceAllString(consensus.priKey, "") socketId := reg.ReplaceAllString(ip+port, "") // A integer Id formed by unique IP/PORT pair
value, err := strconv.Atoi(socketId) value, err := strconv.Atoi(socketId)
consensus.nodeId = uint16(value) consensus.nodeId = uint16(value)
@ -125,10 +141,6 @@ func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) *
}() }()
} }
// The message category and type used for any messages sent for consensus
consensus.msgCategory = byte(common.COMMITTEE)
consensus.msgType = byte(CONSENSUS)
consensus.Log = log.New() consensus.Log = log.New()
return &consensus return &consensus
} }
@ -136,7 +148,7 @@ func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) *
// Reset the state of the consensus // Reset the state of the consensus
func (consensus *Consensus) ResetState() { func (consensus *Consensus) ResetState() {
consensus.state = FINISHED consensus.state = FINISHED
consensus.commits = make(map[string]string) consensus.commitments = make(map[uint16]kyber.Point)
consensus.responses = make(map[string]string) consensus.responses = make(map[string]string)
} }
@ -149,5 +161,5 @@ func (consensus *Consensus) String() string {
duty = "VLD" // validator duty = "VLD" // validator
} }
return fmt.Sprintf("[duty:%s, priKey:%s, ShardID:%v, nodeId:%v, state:%s]", return fmt.Sprintf("[duty:%s, priKey:%s, ShardID:%v, nodeId:%v, state:%s]",
duty, consensus.priKey, consensus.ShardID, consensus.nodeId, consensus.state) duty, fmt.Sprintf("%x", consensus.priKey), consensus.ShardID, consensus.nodeId, consensus.state)
} }

@ -5,9 +5,11 @@ import (
"crypto/sha256" "crypto/sha256"
"encoding/binary" "encoding/binary"
"encoding/gob" "encoding/gob"
"github.com/dedis/kyber"
"harmony-benchmark/blockchain" "harmony-benchmark/blockchain"
"harmony-benchmark/crypto"
"harmony-benchmark/p2p" "harmony-benchmark/p2p"
"strings" proto_consensus "harmony-benchmark/proto/consensus"
"time" "time"
) )
@ -33,26 +35,26 @@ func (consensus *Consensus) WaitForNewBlock(blockChannel chan blockchain.Block)
// Consensus message dispatcher for the leader // Consensus message dispatcher for the leader
func (consensus *Consensus) ProcessMessageLeader(message []byte) { func (consensus *Consensus) ProcessMessageLeader(message []byte) {
msgType, err := GetConsensusMessageType(message) msgType, err := proto_consensus.GetConsensusMessageType(message)
if err != nil { if err != nil {
consensus.Log.Error("Failed to get consensus message type.", "err", err, "consensus", consensus) consensus.Log.Error("Failed to get consensus message type.", "err", err, "consensus", consensus)
} }
payload, err := GetConsensusMessagePayload(message) payload, err := proto_consensus.GetConsensusMessagePayload(message)
if err != nil { if err != nil {
consensus.Log.Error("Failed to get consensus message payload.", "err", err, "consensus", consensus) consensus.Log.Error("Failed to get consensus message payload.", "err", err, "consensus", consensus)
} }
switch msgType { switch msgType {
case ANNOUNCE: case proto_consensus.ANNOUNCE:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus) consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
case COMMIT: case proto_consensus.COMMIT:
consensus.processCommitMessage(payload) consensus.processCommitMessage(payload)
case CHALLENGE: case proto_consensus.CHALLENGE:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus) consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
case RESPONSE: case proto_consensus.RESPONSE:
consensus.processResponseMessage(payload) consensus.processResponseMessage(payload)
case START_CONSENSUS: case proto_consensus.START_CONSENSUS:
consensus.processStartConsensusMessage(payload) consensus.processStartConsensusMessage(payload)
default: default:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus) consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
@ -80,6 +82,11 @@ func (consensus *Consensus) startConsensus(newBlock *blockchain.Block) {
p2p.BroadcastMessage(consensus.validators, msgToSend) p2p.BroadcastMessage(consensus.validators, msgToSend)
// Set state to ANNOUNCE_DONE // Set state to ANNOUNCE_DONE
consensus.state = ANNOUNCE_DONE consensus.state = ANNOUNCE_DONE
// Generate leader's own commitment
secret, commitment := crypto.Commit(crypto.Curve)
consensus.secret = secret
consensus.commitments[consensus.nodeId] = commitment
consensus.bitmap.SetKey(consensus.pubKey, true)
} }
// Constructs the announce message // Constructs the announce message
@ -111,7 +118,7 @@ func (consensus *Consensus) constructAnnounceMessage() []byte {
signature := signMessage(buffer.Bytes()) signature := signMessage(buffer.Bytes())
buffer.Write(signature) buffer.Write(signature)
return consensus.ConstructConsensusMessage(ANNOUNCE, buffer.Bytes()) return proto_consensus.ConstructConsensusMessage(proto_consensus.ANNOUNCE, buffer.Bytes())
} }
func signMessage(message []byte) []byte { func signMessage(message []byte) []byte {
@ -133,12 +140,12 @@ func (consensus *Consensus) processCommitMessage(payload []byte) {
offset += 32 offset += 32
// 2 byte validator id // 2 byte validator id
validatorId := string(payload[offset : offset+2]) validatorId := binary.BigEndian.Uint16(payload[offset : offset+2])
offset += 2 offset += 2
// 33 byte commit // 32 byte commit
commit := payload[offset : offset+33] commitment := payload[offset : offset+32]
offset += 33 offset += 32
// 64 byte of signature on previous data // 64 byte of signature on previous data
signature := payload[offset : offset+64] signature := payload[offset : offset+64]
@ -146,7 +153,6 @@ func (consensus *Consensus) processCommitMessage(payload []byte) {
//#### END: Read payload data //#### END: Read payload data
// TODO: make use of the data. This is just to avoid the unused variable warning // TODO: make use of the data. This is just to avoid the unused variable warning
_ = commit
_ = signature _ = signature
// check consensus Id // check consensus Id
@ -163,19 +169,20 @@ func (consensus *Consensus) processCommitMessage(payload []byte) {
} }
// proceed only when the message is not received before // proceed only when the message is not received before
_, ok := consensus.commits[validatorId] _, ok := consensus.commitments[validatorId]
shouldProcess := !ok shouldProcess := !ok
if shouldProcess { if shouldProcess {
consensus.commits[validatorId] = validatorId point := crypto.Curve.Point()
//consensus.Log.Debug("Number of commits received", "consensusId", consensus.consensusId, "count", len(consensus.commits)) point.UnmarshalBinary(commitment)
consensus.commitments[validatorId] = point
} }
if !shouldProcess { if !shouldProcess {
return return
} }
if len(consensus.commits) >= (2*len(consensus.validators))/3+1 && consensus.state < CHALLENGE_DONE { if len(consensus.commitments) >= (2*len(consensus.validators))/3+1 && consensus.state < CHALLENGE_DONE {
consensus.Log.Debug("Enough commits received with signatures", "numOfSignatures", len(consensus.commits)) consensus.Log.Debug("Enough commitments received with signatures", "numOfSignatures", len(consensus.commitments))
// Broadcast challenge // Broadcast challenge
msgToSend := consensus.constructChallengeMessage() msgToSend := consensus.constructChallengeMessage()
@ -204,10 +211,14 @@ func (consensus *Consensus) constructChallengeMessage() []byte {
buffer.Write(twoBytes) buffer.Write(twoBytes)
// 33 byte aggregated commit // 33 byte aggregated commit
buffer.Write(getAggregatedCommit(consensus.commits)) commitments := make([]kyber.Point, 0)
for _, val := range consensus.commitments {
commitments = append(commitments, val)
}
buffer.Write(getAggregatedCommit(commitments, consensus.bitmap))
// 33 byte aggregated key // 33 byte aggregated key
buffer.Write(getAggregatedKey(consensus.commits)) buffer.Write(getAggregatedKey(consensus.bitmap))
// 32 byte challenge // 32 byte challenge
buffer.Write(getChallenge()) buffer.Write(getChallenge())
@ -216,29 +227,22 @@ func (consensus *Consensus) constructChallengeMessage() []byte {
signature := signMessage(buffer.Bytes()) signature := signMessage(buffer.Bytes())
buffer.Write(signature) buffer.Write(signature)
return consensus.ConstructConsensusMessage(CHALLENGE, buffer.Bytes()) return proto_consensus.ConstructConsensusMessage(proto_consensus.CHALLENGE, buffer.Bytes())
} }
func getAggregatedCommit(commits map[string]string) []byte { func getAggregatedCommit(commitments []kyber.Point, bitmap *crypto.Mask) []byte {
// TODO: implement actual commit aggregation aggCommitment := crypto.AggregateCommitmentsOnly(crypto.Curve, commitments)
var commitArray []string bytes, err := aggCommitment.MarshalBinary()
for _, val := range commits { if err != nil {
commitArray = append(commitArray, val) panic("Failed to deserialize the aggregated commitment")
} }
var commit [32]byte return append(bytes[:], byte(0))
commit = sha256.Sum256([]byte(strings.Join(commitArray, "")))
return append(commit[:], byte(0))
} }
func getAggregatedKey(commits map[string]string) []byte { func getAggregatedKey(bitmap *crypto.Mask) []byte {
// TODO: implement actual key aggregation // TODO: implement actual key aggregation
var commitArray []string commitment := sha256.Sum256([]byte("ABC"))
for key := range commits { return append(commitment[:], byte(0))
commitArray = append(commitArray, key)
}
var commit [32]byte
commit = sha256.Sum256([]byte(strings.Join(commitArray, "")))
return append(commit[:], byte(0))
} }
func getChallenge() []byte { func getChallenge() []byte {

@ -1,6 +1,7 @@
package consensus package consensus
import ( import (
"harmony-benchmark/crypto"
"harmony-benchmark/p2p" "harmony-benchmark/p2p"
"testing" "testing"
) )
@ -19,10 +20,25 @@ func TestConstructAnnounceMessage(test *testing.T) {
} }
func TestConstructChallengeMessage(test *testing.T) { func TestConstructChallengeMessage(test *testing.T) {
leader := p2p.Peer{Ip: "1", Port: "2"} leaderPriKey := crypto.Curve.Scalar()
validator := p2p.Peer{Ip: "3", Port: "5"} priKeyInBytes := crypto.Hash("12")
leaderPriKey.UnmarshalBinary(priKeyInBytes[:]) // use ip:port as unique private key for now. TODO: use real private key
leaderPubKey := crypto.GetPublicKeyFromScalar(crypto.Curve, leaderPriKey)
leader := p2p.Peer{Ip: "1", Port: "2", PubKey: leaderPubKey}
validatorPriKey := crypto.Curve.Scalar()
priKeyInBytes = crypto.Hash("12")
validatorPriKey.UnmarshalBinary(priKeyInBytes[:]) // use ip:port as unique private key for now. TODO: use real private key
validatorPubKey := crypto.GetPublicKeyFromScalar(crypto.Curve, leaderPriKey)
validator := p2p.Peer{Ip: "3", Port: "5", PubKey: validatorPubKey}
consensus := NewConsensus("1", "2", "0", []p2p.Peer{leader, validator}, leader) consensus := NewConsensus("1", "2", "0", []p2p.Peer{leader, validator}, leader)
consensus.blockHash = [32]byte{} consensus.blockHash = [32]byte{}
consensus.commitments[0] = leaderPubKey
consensus.commitments[1] = validatorPubKey
consensus.bitmap.SetKey(leaderPubKey, true)
consensus.bitmap.SetKey(validatorPubKey, true)
msg := consensus.constructChallengeMessage() msg := consensus.constructChallengeMessage()
if len(msg) != 1+1+1+4+32+2+33+33+32+64 { if len(msg) != 1+1+1+4+32+2+33+33+32+64 {

@ -1,7 +1,6 @@
package consensus package consensus
import ( import (
"harmony-benchmark/common"
"harmony-benchmark/p2p" "harmony-benchmark/p2p"
"testing" "testing"
) )
@ -22,14 +21,6 @@ func TestNewConsensus(test *testing.T) {
test.Error("Consensus ReadySignal should be initialized") test.Error("Consensus ReadySignal should be initialized")
} }
if consensus.msgType != byte(CONSENSUS) {
test.Error("Consensus msgType should be CONSENSUS")
}
if consensus.msgCategory != byte(common.COMMITTEE) {
test.Error("Consensus msgCategory should be COMMITTEE")
}
if consensus.leader != leader { if consensus.leader != leader {
test.Error("Consensus Leader is set to wrong Peer") test.Error("Consensus Leader is set to wrong Peer")
} }

@ -4,33 +4,36 @@ import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/gob" "encoding/gob"
"github.com/dedis/kyber"
"harmony-benchmark/attack" "harmony-benchmark/attack"
"harmony-benchmark/blockchain" "harmony-benchmark/blockchain"
"harmony-benchmark/crypto"
"harmony-benchmark/p2p" "harmony-benchmark/p2p"
proto_consensus "harmony-benchmark/proto/consensus"
"regexp" "regexp"
"strconv" "strconv"
) )
// Validator's consensus message dispatcher // Validator's consensus message dispatcher
func (consensus *Consensus) ProcessMessageValidator(message []byte) { func (consensus *Consensus) ProcessMessageValidator(message []byte) {
msgType, err := GetConsensusMessageType(message) msgType, err := proto_consensus.GetConsensusMessageType(message)
if err != nil { if err != nil {
consensus.Log.Error("Failed to get consensus message type", "err", err, "consensus", consensus) consensus.Log.Error("Failed to get consensus message type", "err", err, "consensus", consensus)
} }
payload, err := GetConsensusMessagePayload(message) payload, err := proto_consensus.GetConsensusMessagePayload(message)
if err != nil { if err != nil {
consensus.Log.Error("Failed to get consensus message payload", "err", err, "consensus", consensus) consensus.Log.Error("Failed to get consensus message payload", "err", err, "consensus", consensus)
} }
switch msgType { switch msgType {
case ANNOUNCE: case proto_consensus.ANNOUNCE:
consensus.processAnnounceMessage(payload) consensus.processAnnounceMessage(payload)
case COMMIT: case proto_consensus.COMMIT:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus) consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
case CHALLENGE: case proto_consensus.CHALLENGE:
consensus.processChallengeMessage(payload) consensus.processChallengeMessage(payload)
case RESPONSE: case proto_consensus.RESPONSE:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus) consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
default: default:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus) consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
@ -127,7 +130,10 @@ func (consensus *Consensus) processAnnounceMessage(payload []byte) {
// TODO: return the signature(commit) to leader // TODO: return the signature(commit) to leader
// For now, simply return the private key of this node. // For now, simply return the private key of this node.
msgToSend := consensus.constructCommitMessage() secret, msgToSend := consensus.constructCommitMessage()
// Store the commitment secret
consensus.secret = secret
// consensus.Log.Debug("SENDING COMMIT", "consensusId", consensus.consensusId, "consensus", consensus) // consensus.Log.Debug("SENDING COMMIT", "consensusId", consensus.consensusId, "consensus", consensus)
p2p.SendMessage(consensus.leader, msgToSend) p2p.SendMessage(consensus.leader, msgToSend)
@ -136,7 +142,7 @@ func (consensus *Consensus) processAnnounceMessage(payload []byte) {
} }
// Construct the commit message to send to leader (assumption the consensus data is already verified) // Construct the commit message to send to leader (assumption the consensus data is already verified)
func (consensus *Consensus) constructCommitMessage() []byte { func (consensus *Consensus) constructCommitMessage() (secret kyber.Scalar, commitMsg []byte) {
buffer := bytes.NewBuffer([]byte{}) buffer := bytes.NewBuffer([]byte{})
// 4 byte consensus id // 4 byte consensus id
@ -152,20 +158,15 @@ func (consensus *Consensus) constructCommitMessage() []byte {
binary.BigEndian.PutUint16(twoBytes, consensus.nodeId) binary.BigEndian.PutUint16(twoBytes, consensus.nodeId)
buffer.Write(twoBytes) buffer.Write(twoBytes)
// 33 byte of commit // 32 byte of commit (Note it's different than Zilliqa's ECPoint which takes 33 bytes: https://crypto.stackexchange.com/questions/51703/how-to-convert-from-curve25519-33-byte-to-32-byte-representation)
commit := getCommitMessage() secret, commitment := crypto.Commit(crypto.Curve)
buffer.Write(commit) commitment.MarshalTo(buffer)
// 64 byte of signature on previous data // 64 byte of signature on previous data
signature := signMessage(buffer.Bytes()) signature := signMessage(buffer.Bytes())
buffer.Write(signature) buffer.Write(signature)
return consensus.ConstructConsensusMessage(COMMIT, buffer.Bytes()) return secret, proto_consensus.ConstructConsensusMessage(proto_consensus.COMMIT, buffer.Bytes())
}
func getCommitMessage() []byte {
// TODO: use real cosi signature
return make([]byte, 33)
} }
// Processes the challenge message sent from the leader // Processes the challenge message sent from the leader
@ -252,7 +253,7 @@ func (consensus *Consensus) processChallengeMessage(payload []byte) {
// If I received previous block (which haven't been processed. I will roll up to current block if everything checks. // If I received previous block (which haven't been processed. I will roll up to current block if everything checks.
} }
// TODO: verify aggregated commits with real schnor cosign verification // TODO: verify aggregated commitments with real schnor cosign verification
// TODO: return the signature(response) to leader // TODO: return the signature(response) to leader
// For now, simply return the private key of this node. // For now, simply return the private key of this node.
@ -324,7 +325,7 @@ func (consensus *Consensus) constructResponseMessage() []byte {
signature := signMessage(buffer.Bytes()) signature := signMessage(buffer.Bytes())
buffer.Write(signature) buffer.Write(signature)
return consensus.ConstructConsensusMessage(RESPONSE, buffer.Bytes()) return proto_consensus.ConstructConsensusMessage(proto_consensus.RESPONSE, buffer.Bytes())
} }
func getResponseMessage() []byte { func getResponseMessage() []byte {

@ -10,9 +10,9 @@ func TestConstructCommitMessage(test *testing.T) {
validator := p2p.Peer{Ip: "3", Port: "5"} validator := p2p.Peer{Ip: "3", Port: "5"}
consensus := NewConsensus("1", "2", "0", []p2p.Peer{leader, validator}, leader) consensus := NewConsensus("1", "2", "0", []p2p.Peer{leader, validator}, leader)
consensus.blockHash = [32]byte{} consensus.blockHash = [32]byte{}
msg := consensus.constructCommitMessage() _, msg := consensus.constructCommitMessage()
if len(msg) != 1+1+1+4+32+2+33+64 { if len(msg) != 1+1+1+4+32+2+32+64 {
test.Errorf("Commit message is not constructed in the correct size: %d", len(msg)) test.Errorf("Commit message is not constructed in the correct size: %d", len(msg))
} }
} }

@ -0,0 +1,419 @@
/*
Package cosi implements the collective signing (CoSi) algorithm as presented in
the paper "Keeping Authorities 'Honest or Bust' with Decentralized Witness
Cosigning" by Ewa Syta et al. See https://arxiv.org/abs/1503.08768. This
package only provides the functionality for the cryptographic operations of
CoSi. All network-related operations have to be handled elsewhere. Below we
describe a high-level overview of the CoSi protocol (using a star communication
topology). We refer to the research paper for further details on communication
over trees, exception mechanisms and signature verification policies.
The CoSi protocol has four phases executed between a list of participants P
having a protocol leader (index i = 0) and a list of other nodes (index i > 0).
The secret key of node i is denoted by a_i and the public key by A_i = [a_i]G
(where G is the base point of the underlying group and [...] denotes scalar
multiplication). The aggregate public key is given as A = \sum{i P}(A_i).
1. Announcement: The leader broadcasts an announcement to the other nodes
optionally including the message M to be signed. Upon receiving an announcement
message, a node starts its commitment phase.
2. Commitment: Each node i (including the leader) picks a random scalar v_i,
computes its commitment V_i = [v_i]G and sends V_i back to the leader. The
leader waits until it has received enough commitments (according to some
policy) from the other nodes or a timer has run out. Let P' be the nodes that
have sent their commitments. The leader computes an aggregate commitment V from
all commitments he has received, i.e., V = \sum{j P'}(V_j) and creates a
participation bitmask Z. The leader then broadcasts V and Z to the other
participations together with the message M if it was not sent in phase 1. Upon
receiving a commitment message, a node starts the challenge phase.
3. Challenge: Each node i computes the collective challenge c = H(V || A || M)
using a cryptographic hash function H (here: SHA512), computes its
response r_i = v_i + c*a_i and sends it back to the leader.
4. Response: The leader waits until he has received replies from all nodes in
P' or a timer has run out. If he has not enough replies he aborts. Finally,
the leader computes the aggregate response r = \sum{j P'}(r_j) and publishes
(V,r,Z) as the signature for the message M.
*/
package crypto
import (
"errors"
"fmt"
"github.com/dedis/kyber"
)
// Commit returns a random scalar v, generated from the given suite,
// and a corresponding commitment V = [v]G. If the given cipher stream is nil,
// a random stream is used.
func Commit(suite Suite) (v kyber.Scalar, V kyber.Point) {
random := suite.Scalar().Pick(suite.RandomStream())
commitment := suite.Point().Mul(random, nil)
return random, commitment
}
// AggregateCommitments returns the sum of the given commitments and the
// bitwise OR of the corresponding masks.
func AggregateCommitments(suite Suite, commitments []kyber.Point, masks [][]byte) (sum kyber.Point, commits []byte, err error) {
if len(commitments) != len(masks) {
return nil, nil, errors.New("mismatching lengths of commitment and mask slices")
}
aggCom := suite.Point().Null()
aggMask := make([]byte, len(masks[0]))
for i := range commitments {
aggCom = suite.Point().Add(aggCom, commitments[i])
aggMask, err = AggregateMasks(aggMask, masks[i])
if err != nil {
return nil, nil, err
}
}
return aggCom, aggMask, nil
}
// AggregateCommitmentsOnly returns the sum of the given commitments.
func AggregateCommitmentsOnly(suite Suite, commitments []kyber.Point) kyber.Point {
aggCom := suite.Point().Null()
for i := range commitments {
aggCom = suite.Point().Add(aggCom, commitments[i])
}
return aggCom
}
// Challenge creates the collective challenge from the given aggregate
// commitment V, aggregate public key A, and message M, i.e., it returns
// c = H(V || A || M).
func Challenge(suite Suite, commitment, public kyber.Point, message []byte) (kyber.Scalar, error) {
if commitment == nil {
return nil, errors.New("no commitment provided")
}
if message == nil {
return nil, errors.New("no message provided")
}
hash := suite.Hash()
if _, err := commitment.MarshalTo(hash); err != nil {
return nil, err
}
if _, err := public.MarshalTo(hash); err != nil {
return nil, err
}
hash.Write(message)
return suite.Scalar().SetBytes(hash.Sum(nil)), nil
}
// Response creates the response from the given random scalar v, (collective)
// challenge c, and private key a, i.e., it returns r = v + c*a.
func Response(suite Suite, private, random, challenge kyber.Scalar) (kyber.Scalar, error) {
if private == nil {
return nil, errors.New("no private key provided")
}
if random == nil {
return nil, errors.New("no random scalar provided")
}
if challenge == nil {
return nil, errors.New("no challenge provided")
}
ca := suite.Scalar().Mul(private, challenge)
return ca.Add(random, ca), nil
}
// AggregateResponses returns the sum of given responses.
func AggregateResponses(suite Suite, responses []kyber.Scalar) (kyber.Scalar, error) {
if responses == nil {
return nil, errors.New("no responses provided")
}
r := suite.Scalar().Zero()
for i := range responses {
r = r.Add(r, responses[i])
}
return r, nil
}
// Sign returns the collective signature from the given (aggregate) commitment
// V, (aggregate) response r, and participation bitmask Z using the EdDSA
// format, i.e., the signature is V || r || Z.
func Sign(suite Suite, commitment kyber.Point, response kyber.Scalar, mask *Mask) ([]byte, error) {
if commitment == nil {
return nil, errors.New("no commitment provided")
}
if response == nil {
return nil, errors.New("no response provided")
}
if mask == nil {
return nil, errors.New("no mask provided")
}
lenV := suite.PointLen()
lenSig := lenV + suite.ScalarLen()
VB, err := commitment.MarshalBinary()
if err != nil {
return nil, errors.New("marshalling of commitment failed")
}
RB, err := response.MarshalBinary()
if err != nil {
return nil, errors.New("marshalling of signature failed")
}
sig := make([]byte, lenSig+mask.Len())
copy(sig[:], VB)
copy(sig[lenV:lenSig], RB)
copy(sig[lenSig:], mask.mask)
return sig, nil
}
// Verify checks the given cosignature on the provided message using the list
// of public keys and cosigning policy.
func Verify(suite Suite, publics []kyber.Point, message, sig []byte, policy Policy) error {
if publics == nil {
return errors.New("no public keys provided")
}
if message == nil {
return errors.New("no message provided")
}
if sig == nil {
return errors.New("no signature provided")
}
if policy == nil {
policy = CompletePolicy{}
}
lenCom := suite.PointLen()
VBuff := sig[:lenCom]
V := suite.Point()
if err := V.UnmarshalBinary(VBuff); err != nil {
return errors.New("unmarshalling of commitment failed")
}
// Unpack the aggregate response
lenRes := lenCom + suite.ScalarLen()
rBuff := sig[lenCom:lenRes]
r := suite.Scalar().SetBytes(rBuff)
// Unpack the participation mask and get the aggregate public key
mask, err := NewMask(suite, publics, nil)
if err != nil {
return err
}
mask.SetMask(sig[lenRes:])
A := mask.AggregatePublic
ABuff, err := A.MarshalBinary()
if err != nil {
return errors.New("marshalling of aggregate public key failed")
}
// Recompute the challenge
hash := suite.Hash()
hash.Write(VBuff)
hash.Write(ABuff)
hash.Write(message)
buff := hash.Sum(nil)
k := suite.Scalar().SetBytes(buff)
// k * -aggPublic + s * B = k*-A + s*B
// from s = k * a + r => s * B = k * a * B + r * B <=> s*B = k*A + r*B
// <=> s*B + k*-A = r*B
minusPublic := suite.Point().Neg(A)
kA := suite.Point().Mul(k, minusPublic)
sB := suite.Point().Mul(r, nil)
left := suite.Point().Add(kA, sB)
if !left.Equal(V) {
return errors.New("recreated response is different from signature")
}
if !policy.Check(mask) {
return errors.New("the policy is not fulfilled")
}
return nil
}
// Mask represents a cosigning participation bitmask.
type Mask struct {
mask []byte
publics []kyber.Point
AggregatePublic kyber.Point
}
// NewMask returns a new participation bitmask for cosigning where all
// cosigners are disabled by default. If a public key is given it verifies that
// it is present in the list of keys and sets the corresponding index in the
// bitmask to 1 (enabled).
func NewMask(suite Suite, publics []kyber.Point, myKey kyber.Point) (*Mask, error) {
m := &Mask{
publics: publics,
}
m.mask = make([]byte, m.Len())
m.AggregatePublic = suite.Point().Null()
if myKey != nil {
found := false
for i, key := range publics {
if key.Equal(myKey) {
m.SetBit(i, true)
found = true
break
}
}
if !found {
return nil, errors.New("key not found")
}
}
return m, nil
}
// Mask returns a copy of the participation bitmask.
func (m *Mask) Mask() []byte {
clone := make([]byte, len(m.mask))
copy(clone[:], m.mask)
return clone
}
// Len returns the mask length in bytes.
func (m *Mask) Len() int {
return (len(m.publics) + 7) >> 3
}
// SetMask sets the participation bitmask according to the given byte slice
// interpreted in little-endian order, i.e., bits 0-7 of byte 0 correspond to
// cosigners 0-7, bits 0-7 of byte 1 correspond to cosigners 8-15, etc.
func (m *Mask) SetMask(mask []byte) error {
if m.Len() != len(mask) {
return fmt.Errorf("mismatching mask lengths")
}
for i := range m.publics {
byt := i >> 3
msk := byte(1) << uint(i&7)
if ((m.mask[byt] & msk) == 0) && ((mask[byt] & msk) != 0) {
m.mask[byt] ^= msk // flip bit in mask from 0 to 1
m.AggregatePublic.Add(m.AggregatePublic, m.publics[i])
}
if ((m.mask[byt] & msk) != 0) && ((mask[byt] & msk) == 0) {
m.mask[byt] ^= msk // flip bit in mask from 1 to 0
m.AggregatePublic.Sub(m.AggregatePublic, m.publics[i])
}
}
return nil
}
// SetBit enables (enable: true) or disables (enable: false) the bit
// in the participation mask of the given cosigner.
func (m *Mask) SetBit(i int, enable bool) error {
if i >= len(m.publics) {
return errors.New("index out of range")
}
byt := i >> 3
msk := byte(1) << uint(i&7)
if ((m.mask[byt] & msk) == 0) && enable {
m.mask[byt] ^= msk // flip bit in mask from 0 to 1
m.AggregatePublic.Add(m.AggregatePublic, m.publics[i])
}
if ((m.mask[byt] & msk) != 0) && !enable {
m.mask[byt] ^= msk // flip bit in mask from 1 to 0
m.AggregatePublic.Sub(m.AggregatePublic, m.publics[i])
}
return nil
}
// IndexEnabled checks whether the given index is enabled in the mask or not.
func (m *Mask) IndexEnabled(i int) (bool, error) {
if i >= len(m.publics) {
return false, errors.New("index out of range")
}
byt := i >> 3
msk := byte(1) << uint(i&7)
return ((m.mask[byt] & msk) != 0), nil
}
// KeyEnabled checks whether the index, corresponding to the given key, is
// enabled in the mask or not.
func (m *Mask) KeyEnabled(public kyber.Point) (bool, error) {
for i, key := range m.publics {
if key.Equal(public) {
return m.IndexEnabled(i)
}
}
return false, errors.New("key not found")
}
// SetKey set the bit in the mask for the given cosigner
func (m *Mask) SetKey(public kyber.Point, enable bool) error {
for i, key := range m.publics {
if key.Equal(public) {
return m.SetBit(i, enable)
}
}
return errors.New("key not found")
}
// CountEnabled returns the number of enabled nodes in the CoSi participation
// mask.
func (m *Mask) CountEnabled() int {
// hw is hamming weight
hw := 0
for i := range m.publics {
byt := i >> 3
msk := byte(1) << uint(i&7)
if (m.mask[byt] & msk) != 0 {
hw++
}
}
return hw
}
// CountTotal returns the total number of nodes this CoSi instance knows.
func (m *Mask) CountTotal() int {
return len(m.publics)
}
// AggregateMasks computes the bitwise OR of the two given participation masks.
func AggregateMasks(a, b []byte) ([]byte, error) {
if len(a) != len(b) {
return nil, errors.New("mismatching mask lengths")
}
m := make([]byte, len(a))
for i := range m {
m[i] = a[i] | b[i]
}
return m, nil
}
// Policy represents a fully customizable cosigning policy deciding what
// cosigner sets are and aren't sufficient for a collective signature to be
// considered acceptable to a verifier. The Check method may inspect the set of
// participants that cosigned by invoking cosi.Mask and/or cosi.MaskBit, and may
// use any other relevant contextual information (e.g., how security-critical
// the operation relying on the collective signature is) in determining whether
// the collective signature was produced by an acceptable set of cosigners.
type Policy interface {
Check(m *Mask) bool
}
// CompletePolicy is the default policy requiring that all participants have
// cosigned to make a collective signature valid.
type CompletePolicy struct {
}
// Check verifies that all participants have contributed to a collective
// signature.
func (p CompletePolicy) Check(m *Mask) bool {
return m.CountEnabled() == m.CountTotal()
}
// ThresholdPolicy allows to specify a simple t-of-n policy requring that at
// least the given threshold number of participants t have cosigned to make a
// collective signature valid.
type ThresholdPolicy struct {
thold int
}
// NewThresholdPolicy returns a new ThresholdPolicy with the given threshold.
func NewThresholdPolicy(thold int) *ThresholdPolicy {
return &ThresholdPolicy{thold: thold}
}
// Check verifies that at least a threshold number of participants have
// contributed to a collective signature.
func (p ThresholdPolicy) Check(m *Mask) bool {
return m.CountEnabled() >= p.thold
}

@ -0,0 +1,5 @@
package crypto
import "github.com/dedis/kyber/group/edwards25519"
var Curve = edwards25519.NewBlakeSHA256Ed25519()

@ -0,0 +1,10 @@
package crypto
import "github.com/dedis/kyber"
// Suite specifies the cryptographic building blocks required for the cosi package.
type Suite interface {
kyber.Group
kyber.HashFactory
kyber.Random
}

@ -0,0 +1,21 @@
package crypto
import (
"crypto/sha256"
"github.com/dedis/kyber"
)
func Hash(message string) [32]byte {
return sha256.Sum256([]byte(message))
}
func GetPublicKeyFromPrivateKey(suite Suite, priKey [32]byte) kyber.Point {
scalar := suite.Scalar()
scalar.UnmarshalBinary(priKey[:])
return suite.Point().Mul(scalar, nil)
}
// Same as GetPublicKeyFromPrivateKey, but it directly works on kyber.Scalar object.
func GetPublicKeyFromScalar(suite Suite, priKey kyber.Scalar) kyber.Point {
return suite.Point().Mul(priKey, nil)
}

@ -4,10 +4,11 @@ import (
"bytes" "bytes"
"encoding/gob" "encoding/gob"
"harmony-benchmark/blockchain" "harmony-benchmark/blockchain"
"harmony-benchmark/client"
"harmony-benchmark/common"
"harmony-benchmark/consensus"
"harmony-benchmark/p2p" "harmony-benchmark/p2p"
"harmony-benchmark/proto"
"harmony-benchmark/proto/client"
"harmony-benchmark/proto/consensus"
proto_node "harmony-benchmark/proto/node"
"net" "net"
"os" "os"
"time" "time"
@ -31,27 +32,27 @@ func (node *Node) NodeHandler(conn net.Conn) {
} }
consensusObj := node.Consensus consensusObj := node.Consensus
msgCategory, err := common.GetMessageCategory(content) msgCategory, err := proto.GetMessageCategory(content)
if err != nil { if err != nil {
node.log.Error("Read node type failed", "err", err, "node", node) node.log.Error("Read node type failed", "err", err, "node", node)
return return
} }
msgType, err := common.GetMessageType(content) msgType, err := proto.GetMessageType(content)
if err != nil { if err != nil {
node.log.Error("Read action type failed", "err", err, "node", node) node.log.Error("Read action type failed", "err", err, "node", node)
return return
} }
msgPayload, err := common.GetMessagePayload(content) msgPayload, err := proto.GetMessagePayload(content)
if err != nil { if err != nil {
node.log.Error("Read message payload failed", "err", err, "node", node) node.log.Error("Read message payload failed", "err", err, "node", node)
return return
} }
switch msgCategory { switch msgCategory {
case common.COMMITTEE: case proto.CONSENSUS:
actionType := consensus.CommitteeMessageType(msgType) actionType := consensus.ConsensusMessageType(msgType)
switch actionType { switch actionType {
case consensus.CONSENSUS: case consensus.CONSENSUS:
if consensusObj.IsLeader { if consensusObj.IsLeader {
@ -60,16 +61,16 @@ func (node *Node) NodeHandler(conn net.Conn) {
consensusObj.ProcessMessageValidator(msgPayload) consensusObj.ProcessMessageValidator(msgPayload)
} }
} }
case common.NODE: case proto.NODE:
actionType := NodeMessageType(msgType) actionType := proto_node.NodeMessageType(msgType)
switch actionType { switch actionType {
case TRANSACTION: case proto_node.TRANSACTION:
node.transactionMessageHandler(msgPayload) node.transactionMessageHandler(msgPayload)
case BLOCK: case proto_node.BLOCK:
if node.Client != nil { if node.Client != nil {
blockMsgType := BlockMessageType(msgPayload[0]) blockMsgType := proto_node.BlockMessageType(msgPayload[0])
switch blockMsgType { switch blockMsgType {
case SYNC: case proto_node.SYNC:
decoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the SYNC messge type decoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the SYNC messge type
blocks := new([]*blockchain.Block) blocks := new([]*blockchain.Block)
decoder.Decode(blocks) decoder.Decode(blocks)
@ -78,9 +79,9 @@ func (node *Node) NodeHandler(conn net.Conn) {
} }
} }
} }
case CONTROL: case proto_node.CONTROL:
controlType := msgPayload[0] controlType := msgPayload[0]
if ControlMessageType(controlType) == STOP { if proto_node.ControlMessageType(controlType) == proto_node.STOP {
node.log.Debug("Stopping Node", "node", node, "numBlocks", len(node.blockchain.Blocks), "numTxsProcessed", node.countNumTransactionsInBlockchain()) node.log.Debug("Stopping Node", "node", node, "numBlocks", len(node.blockchain.Blocks), "numTxsProcessed", node.countNumTransactionsInBlockchain())
sizeInBytes := node.UtxoPool.GetSizeInByteOfUtxoMap() sizeInBytes := node.UtxoPool.GetSizeInByteOfUtxoMap()
@ -111,7 +112,7 @@ func (node *Node) NodeHandler(conn net.Conn) {
os.Exit(0) os.Exit(0)
} }
} }
case common.CLIENT: case proto.CLIENT:
actionType := client.ClientMessageType(msgType) actionType := client.ClientMessageType(msgType)
switch actionType { switch actionType {
case client.TRANSACTION: case client.TRANSACTION:
@ -123,10 +124,10 @@ func (node *Node) NodeHandler(conn net.Conn) {
} }
func (node *Node) transactionMessageHandler(msgPayload []byte) { func (node *Node) transactionMessageHandler(msgPayload []byte) {
txMessageType := TransactionMessageType(msgPayload[0]) txMessageType := proto_node.TransactionMessageType(msgPayload[0])
switch txMessageType { switch txMessageType {
case SEND: case proto_node.SEND:
txDecoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the SEND messge type txDecoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the SEND messge type
txList := new([]*blockchain.Transaction) txList := new([]*blockchain.Transaction)
@ -135,7 +136,7 @@ func (node *Node) transactionMessageHandler(msgPayload []byte) {
node.log.Error("Failed deserializing transaction list", "node", node) node.log.Error("Failed deserializing transaction list", "node", node)
} }
node.addPendingTransactions(*txList) node.addPendingTransactions(*txList)
case REQUEST: case proto_node.REQUEST:
reader := bytes.NewBuffer(msgPayload[1:]) reader := bytes.NewBuffer(msgPayload[1:])
var txIds map[[32]byte]bool var txIds map[[32]byte]bool
buf := make([]byte, 32) // 32 byte hash Id buf := make([]byte, 32) // 32 byte hash Id
@ -157,7 +158,7 @@ func (node *Node) transactionMessageHandler(msgPayload []byte) {
} }
} }
// TODO: return the transaction list to requester // TODO: return the transaction list to requester
case UNLOCK: case proto_node.UNLOCK:
txAndProofDecoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the UNLOCK messge type txAndProofDecoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the UNLOCK messge type
txAndProofs := new([]*blockchain.Transaction) txAndProofs := new([]*blockchain.Transaction)
@ -242,7 +243,7 @@ func (node *Node) SendBackProofOfAcceptOrReject() {
func (node *Node) BroadcastNewBlock(newBlock *blockchain.Block) { func (node *Node) BroadcastNewBlock(newBlock *blockchain.Block) {
if node.ClientPeer != nil { if node.ClientPeer != nil {
node.log.Debug("SENDING NEW BLOCK TO CLIENT") node.log.Debug("SENDING NEW BLOCK TO CLIENT")
p2p.SendMessage(*node.ClientPeer, ConstructBlocksSyncMessage([]blockchain.Block{*newBlock})) p2p.SendMessage(*node.ClientPeer, proto_node.ConstructBlocksSyncMessage([]blockchain.Block{*newBlock}))
} }
} }

@ -3,6 +3,7 @@ package p2p
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"github.com/dedis/kyber"
"harmony-benchmark/attack" "harmony-benchmark/attack"
"log" "log"
"net" "net"
@ -14,7 +15,7 @@ import (
type Peer struct { type Peer struct {
Ip string // Ip address of the peer Ip string // Ip address of the peer
Port string // Port number of the peer Port string // Port number of the peer
PubKey string // Public key of the peer PubKey kyber.Point // Public key of the peer
} }
// SendMessage sends the message to the peer // SendMessage sends the message to the peer

@ -4,7 +4,8 @@ import (
"bytes" "bytes"
"encoding/gob" "encoding/gob"
"harmony-benchmark/blockchain" "harmony-benchmark/blockchain"
"harmony-benchmark/common" "harmony-benchmark/proto"
"harmony-benchmark/proto/node"
) )
// The specific types of message under CLIENT category // The specific types of message under CLIENT category
@ -24,7 +25,7 @@ const (
// [leader] Constructs the proof of accept or reject message that will be sent to client // [leader] Constructs the proof of accept or reject message that will be sent to client
func ConstructProofOfAcceptOrRejectMessage(proofs []blockchain.CrossShardTxProof) []byte { func ConstructProofOfAcceptOrRejectMessage(proofs []blockchain.CrossShardTxProof) []byte {
byteBuffer := bytes.NewBuffer([]byte{byte(common.CLIENT)}) byteBuffer := bytes.NewBuffer([]byte{byte(proto.CLIENT)})
byteBuffer.WriteByte(byte(TRANSACTION)) byteBuffer.WriteByte(byte(TRANSACTION))
byteBuffer.WriteByte(byte(PROOF_OF_LOCK)) byteBuffer.WriteByte(byte(PROOF_OF_LOCK))
encoder := gob.NewEncoder(byteBuffer) encoder := gob.NewEncoder(byteBuffer)
@ -35,9 +36,9 @@ func ConstructProofOfAcceptOrRejectMessage(proofs []blockchain.CrossShardTxProof
// [client] Constructs the unlock to commit or abort message that will be sent to leaders // [client] Constructs the unlock to commit or abort message that will be sent to leaders
func ConstructUnlockToCommitOrAbortMessage(txsAndProofs []blockchain.Transaction) []byte { func ConstructUnlockToCommitOrAbortMessage(txsAndProofs []blockchain.Transaction) []byte {
byteBuffer := bytes.NewBuffer([]byte{byte(common.NODE)}) byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)})
byteBuffer.WriteByte(byte(0)) // A temporary hack to represent node.TRANSACTION, to avoid cyclical import. TODO: Potentially solution is to refactor all the message enums into a common package byteBuffer.WriteByte(byte(node.TRANSACTION))
byteBuffer.WriteByte(byte(2)) // A temporary hack to represent node.UNLOCK, to avoid cyclical import. TODO: Potentially solution is to refactor all the message enums into a common package byteBuffer.WriteByte(byte(node.UNLOCK))
encoder := gob.NewEncoder(byteBuffer) encoder := gob.NewEncoder(byteBuffer)
encoder.Encode(txsAndProofs) encoder.Encode(txsAndProofs)
return byteBuffer.Bytes() return byteBuffer.Bytes()

@ -0,0 +1,62 @@
package proto
import (
"errors"
)
/*
The message structure of any message in Harmony network
---- content start -----
1 byte - message category
0x00: CONSENSUS
0x01: NODE...
1 byte - message type
- for CONSENSUS category
0x00: consensus
0x01: sharding ...
- for NODE category
0x00: transaction ...
n - 2 bytes - actual message payload
---- content end -----
*/
// The message category enum
type MessageCategory byte
const (
CONSENSUS MessageCategory = iota
NODE
CLIENT
// TODO: add more types
)
// MESSAGE_CATEGORY_BYTES is the number of bytes message category takes
const MESSAGE_CATEGORY_BYTES = 1
// MESSAGE_TYPE_BYTES is the number of bytes message type takes
const MESSAGE_TYPE_BYTES = 1
// Get the message category from the p2p message content
func GetMessageCategory(message []byte) (MessageCategory, error) {
if len(message) < MESSAGE_CATEGORY_BYTES {
return 0, errors.New("Failed to get message category: no data available.")
}
return MessageCategory(message[MESSAGE_CATEGORY_BYTES-1]), nil
}
// Get the message type from the p2p message content
func GetMessageType(message []byte) (byte, error) {
if len(message) < MESSAGE_CATEGORY_BYTES+MESSAGE_TYPE_BYTES {
return 0, errors.New("Failed to get message type: no data available.")
}
return byte(message[MESSAGE_CATEGORY_BYTES+MESSAGE_TYPE_BYTES-1]), nil
}
// Get the node message payload from the p2p message content
func GetMessagePayload(message []byte) ([]byte, error) {
if len(message) < MESSAGE_CATEGORY_BYTES+MESSAGE_TYPE_BYTES {
return []byte{}, errors.New("Failed to get message payload: no data available.")
}
return message[MESSAGE_CATEGORY_BYTES+MESSAGE_TYPE_BYTES:], nil
}

@ -3,6 +3,7 @@ package consensus
import ( import (
"bytes" "bytes"
"errors" "errors"
"harmony-benchmark/proto"
) )
/* /*
@ -33,7 +34,7 @@ COMMIT:
4 byte - consensus id 4 byte - consensus id
32 byte - block hash 32 byte - block hash
2 byte - validator id 2 byte - validator id
33 byte - commit message 32 byte - commit message (Note it's different than Zilliqa's ECPoint which takes 33 bytes: https://crypto.stackexchange.com/questions/51703/how-to-convert-from-curve25519-33-byte-to-32-byte-representation)
64 byte - signature 64 byte - signature
---- message end ----- ---- message end -----
@ -66,14 +67,14 @@ RESPONSE:
---- message end ----- ---- message end -----
*/ */
// the number of bytes consensus action type occupies // the number of bytes consensus message type occupies
const ACTION_TYPE_BYTES = 1 const CONSENSUS_MESSAGE_TYPE_BYTES = 1
// The specific types of message under COMMITTEE category // The specific types of message under CONSENSUS category
type CommitteeMessageType byte type ConsensusMessageType byte
const ( const (
CONSENSUS CommitteeMessageType = iota CONSENSUS ConsensusMessageType = iota
// TODO: add more types // TODO: add more types
) )
@ -118,13 +119,13 @@ func GetConsensusMessagePayload(message []byte) ([]byte, error) {
if len(message) < 2 { if len(message) < 2 {
return []byte{}, errors.New("Failed to get consensus message payload: no data available.") return []byte{}, errors.New("Failed to get consensus message payload: no data available.")
} }
return message[ACTION_TYPE_BYTES:], nil return message[CONSENSUS_MESSAGE_TYPE_BYTES:], nil
} }
// Concatenate msgType as one byte with payload, and return the whole byte array // Concatenate msgType as one byte with payload, and return the whole byte array
func (consensus Consensus) ConstructConsensusMessage(consensusMsgType MessageType, payload []byte) []byte { func ConstructConsensusMessage(consensusMsgType MessageType, payload []byte) []byte {
byteBuffer := bytes.NewBuffer([]byte{consensus.msgCategory}) byteBuffer := bytes.NewBuffer([]byte{byte(proto.CONSENSUS)})
byteBuffer.WriteByte(consensus.msgType) byteBuffer.WriteByte(byte(CONSENSUS))
byteBuffer.WriteByte(byte(consensusMsgType)) byteBuffer.WriteByte(byte(consensusMsgType))
byteBuffer.Write(payload) byteBuffer.Write(payload)
return byteBuffer.Bytes() return byteBuffer.Bytes()

@ -4,17 +4,16 @@ import (
"bytes" "bytes"
"encoding/gob" "encoding/gob"
"harmony-benchmark/blockchain" "harmony-benchmark/blockchain"
"harmony-benchmark/common" "harmony-benchmark/proto"
) )
// The specific types of message under NODE category // The specific types of message under NODE category
type NodeMessageType byte type NodeMessageType byte
const ( const (
TRANSACTION NodeMessageType = iota // TODO: Don't move this until the hack in client/message.go is resolved TRANSACTION NodeMessageType = iota
BLOCK BLOCK
CONTROL CONTROL
// TODO: add more types // TODO: add more types
) )
@ -24,7 +23,7 @@ type TransactionMessageType int
const ( const (
SEND TransactionMessageType = iota SEND TransactionMessageType = iota
REQUEST REQUEST
UNLOCK // The unlock to commit or abort message sent by the client to leaders. TODO: Don't move this until the hack in client/message.go is resolved UNLOCK
) )
// The types of messages used for NODE/BLOCK // The types of messages used for NODE/BLOCK
@ -43,7 +42,7 @@ const (
// Constructs serialized transactions // Constructs serialized transactions
func ConstructTransactionListMessage(transactions []*blockchain.Transaction) []byte { func ConstructTransactionListMessage(transactions []*blockchain.Transaction) []byte {
byteBuffer := bytes.NewBuffer([]byte{byte(common.NODE)}) byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)})
byteBuffer.WriteByte(byte(TRANSACTION)) byteBuffer.WriteByte(byte(TRANSACTION))
byteBuffer.WriteByte(byte(SEND)) byteBuffer.WriteByte(byte(SEND))
encoder := gob.NewEncoder(byteBuffer) encoder := gob.NewEncoder(byteBuffer)
@ -58,7 +57,7 @@ func ConstructTransactionListMessage(transactions []*blockchain.Transaction) []b
// Constructs serialized transactions // Constructs serialized transactions
func ConstructRequestTransactionsMessage(transactionIds [][]byte) []byte { func ConstructRequestTransactionsMessage(transactionIds [][]byte) []byte {
byteBuffer := bytes.NewBuffer([]byte{byte(common.NODE)}) byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)})
byteBuffer.WriteByte(byte(TRANSACTION)) byteBuffer.WriteByte(byte(TRANSACTION))
byteBuffer.WriteByte(byte(REQUEST)) byteBuffer.WriteByte(byte(REQUEST))
for _, txId := range transactionIds { for _, txId := range transactionIds {
@ -69,7 +68,7 @@ func ConstructRequestTransactionsMessage(transactionIds [][]byte) []byte {
// Constructs STOP message for node to stop // Constructs STOP message for node to stop
func ConstructStopMessage() []byte { func ConstructStopMessage() []byte {
byteBuffer := bytes.NewBuffer([]byte{byte(common.NODE)}) byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)})
byteBuffer.WriteByte(byte(CONTROL)) byteBuffer.WriteByte(byte(CONTROL))
byteBuffer.WriteByte(byte(STOP)) byteBuffer.WriteByte(byte(STOP))
return byteBuffer.Bytes() return byteBuffer.Bytes()
@ -77,7 +76,7 @@ func ConstructStopMessage() []byte {
// Constructs blocks sync message to send blocks to other nodes // Constructs blocks sync message to send blocks to other nodes
func ConstructBlocksSyncMessage(blocks []blockchain.Block) []byte { func ConstructBlocksSyncMessage(blocks []blockchain.Block) []byte {
byteBuffer := bytes.NewBuffer([]byte{byte(common.NODE)}) byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)})
byteBuffer.WriteByte(byte(BLOCK)) byteBuffer.WriteByte(byte(BLOCK))
byteBuffer.WriteByte(byte(SYNC)) byteBuffer.WriteByte(byte(SYNC))
encoder := gob.NewEncoder(byteBuffer) encoder := gob.NewEncoder(byteBuffer)
Loading…
Cancel
Save