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
go get github.com/go-stack/stack
go get ./...
```
## Usage
```

@ -14,6 +14,7 @@ import (
"time"
"github.com/shirou/gopsutil/process"
"harmony-benchmark/crypto"
)
const (
@ -37,6 +38,8 @@ func getLeader(myShardId string, config *[][]string) p2p.Peer {
if status == "leader" && myShardId == shardId {
leaderPeer.Ip = ip
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
@ -49,7 +52,8 @@ func getPeers(myIp, myPort, myShardId string, config *[][]string) []p2p.Peer {
if status != "validator" || ip == myIp && port == myPort || myShardId != shardId {
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)
}
return peerList

@ -12,6 +12,7 @@ import (
"harmony-benchmark/log"
"harmony-benchmark/node"
"harmony-benchmark/p2p"
proto_node "harmony-benchmark/proto/node"
"sync"
"time"
@ -219,14 +220,14 @@ func main() {
allCrossTxs = append(allCrossTxs, 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)
// Note cross shard txs are later sent in batch
}
if len(allCrossTxs) > 0 {
log.Debug("[Generator] Broadcasting cross-shard txs ...", "allCrossTxs", len(allCrossTxs))
msg := node.ConstructTransactionListMessage(allCrossTxs)
msg := proto_node.ConstructTransactionListMessage(allCrossTxs)
p2p.BroadcastMessage(leaders, msg)
// 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
msg := node.ConstructStopMessage()
msg := proto_node.ConstructStopMessage()
peers := append(configr.GetValidators(*configFile), leaders...)
p2p.BroadcastMessage(peers, msg)
}

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

@ -11,6 +11,7 @@ import (
"harmony-benchmark/log"
"harmony-benchmark/node"
"harmony-benchmark/p2p"
proto_node "harmony-benchmark/proto/node"
"math/rand"
"strconv"
"sync"
@ -302,14 +303,14 @@ func main() {
allCrossTxs = append(allCrossTxs, 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)
// Note cross shard txs are later sent in batch
}
if len(allCrossTxs) > 0 {
log.Debug("[Generator] Broadcasting cross-shard txs ...", "allCrossTxs", len(allCrossTxs))
msg := node.ConstructTransactionListMessage(allCrossTxs)
msg := proto_node.ConstructTransactionListMessage(allCrossTxs)
p2p.BroadcastMessage(leaders, msg)
// 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
msg := node.ConstructStopMessage()
msg := proto_node.ConstructStopMessage()
peers := append(configr.GetValidators(*configFile), leaders...)
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 (
"fmt"
"github.com/dedis/kyber"
"harmony-benchmark/blockchain"
"harmony-benchmark/common"
"harmony-benchmark/crypto"
"harmony-benchmark/log"
"harmony-benchmark/p2p"
"regexp"
@ -15,16 +16,20 @@ import (
// Consensus data containing all info related to one round of consensus process
type Consensus struct {
state ConsensusState
// Signatures collected from validators
commits map[string]string
// Commits collected from validators. A map from node Id to its commitment
commitments map[uint16]kyber.Point
// Commits collected from validators.
bitmap *crypto.Mask
// Signatures collected from validators
responses map[string]string
// List of validators
validators []p2p.Peer
// Leader
leader p2p.Peer
// private key of current node
priKey string
// private/public keys of current node
priKey kyber.Scalar
pubKey kyber.Point
// Whether I am leader. False means I am validator
IsLeader bool
// Leader or validator Id - 2 byte
@ -44,6 +49,8 @@ type Consensus struct {
// Validator specific fields
// Blocks received but not done with consensus yet
blocksReceived map[uint32]*BlockConsensusStatus
// Commitment secret
secret kyber.Scalar
// Signal channel for starting a new consensus process
ReadySignal chan int
@ -53,10 +60,6 @@ type Consensus struct {
// Called when consensus on a new block is done
OnConsensusDone func(*blockchain.Block)
//// Network related fields
msgCategory byte
msgType byte
Log log.Logger
}
@ -76,24 +79,37 @@ type BlockConsensusStatus struct {
// FYI, see https://golang.org/doc/effective_go.html?#package-names
func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) *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
} else {
consensus.IsLeader = false
}
consensus.commits = make(map[string]string)
consensus.commitments = make(map[uint16]kyber.Point)
consensus.responses = make(map[string]string)
consensus.leader = leaderPeer
consensus.validators = Peers
consensus.priKey = ip + ":" + port // use ip:port as unique key for now
consensus.leader = leader
consensus.validators = peers
// 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
myShardID, err := strconv.Atoi(ShardID)
@ -111,7 +127,7 @@ func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) *
if err != nil {
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)
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()
return &consensus
}
@ -136,7 +148,7 @@ func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) *
// Reset the state of the consensus
func (consensus *Consensus) ResetState() {
consensus.state = FINISHED
consensus.commits = make(map[string]string)
consensus.commitments = make(map[uint16]kyber.Point)
consensus.responses = make(map[string]string)
}
@ -149,5 +161,5 @@ func (consensus *Consensus) String() string {
duty = "VLD" // validator
}
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"
"encoding/binary"
"encoding/gob"
"github.com/dedis/kyber"
"harmony-benchmark/blockchain"
"harmony-benchmark/crypto"
"harmony-benchmark/p2p"
"strings"
proto_consensus "harmony-benchmark/proto/consensus"
"time"
)
@ -33,26 +35,26 @@ func (consensus *Consensus) WaitForNewBlock(blockChannel chan blockchain.Block)
// Consensus message dispatcher for the leader
func (consensus *Consensus) ProcessMessageLeader(message []byte) {
msgType, err := GetConsensusMessageType(message)
msgType, err := proto_consensus.GetConsensusMessageType(message)
if err != nil {
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 {
consensus.Log.Error("Failed to get consensus message payload.", "err", err, "consensus", consensus)
}
switch msgType {
case ANNOUNCE:
case proto_consensus.ANNOUNCE:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
case COMMIT:
case proto_consensus.COMMIT:
consensus.processCommitMessage(payload)
case CHALLENGE:
case proto_consensus.CHALLENGE:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
case RESPONSE:
case proto_consensus.RESPONSE:
consensus.processResponseMessage(payload)
case START_CONSENSUS:
case proto_consensus.START_CONSENSUS:
consensus.processStartConsensusMessage(payload)
default:
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)
// Set state to 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
@ -111,7 +118,7 @@ func (consensus *Consensus) constructAnnounceMessage() []byte {
signature := signMessage(buffer.Bytes())
buffer.Write(signature)
return consensus.ConstructConsensusMessage(ANNOUNCE, buffer.Bytes())
return proto_consensus.ConstructConsensusMessage(proto_consensus.ANNOUNCE, buffer.Bytes())
}
func signMessage(message []byte) []byte {
@ -133,12 +140,12 @@ func (consensus *Consensus) processCommitMessage(payload []byte) {
offset += 32
// 2 byte validator id
validatorId := string(payload[offset : offset+2])
validatorId := binary.BigEndian.Uint16(payload[offset : offset+2])
offset += 2
// 33 byte commit
commit := payload[offset : offset+33]
offset += 33
// 32 byte commit
commitment := payload[offset : offset+32]
offset += 32
// 64 byte of signature on previous data
signature := payload[offset : offset+64]
@ -146,7 +153,6 @@ func (consensus *Consensus) processCommitMessage(payload []byte) {
//#### END: Read payload data
// TODO: make use of the data. This is just to avoid the unused variable warning
_ = commit
_ = signature
// check consensus Id
@ -163,19 +169,20 @@ func (consensus *Consensus) processCommitMessage(payload []byte) {
}
// proceed only when the message is not received before
_, ok := consensus.commits[validatorId]
_, ok := consensus.commitments[validatorId]
shouldProcess := !ok
if shouldProcess {
consensus.commits[validatorId] = validatorId
//consensus.Log.Debug("Number of commits received", "consensusId", consensus.consensusId, "count", len(consensus.commits))
point := crypto.Curve.Point()
point.UnmarshalBinary(commitment)
consensus.commitments[validatorId] = point
}
if !shouldProcess {
return
}
if len(consensus.commits) >= (2*len(consensus.validators))/3+1 && consensus.state < CHALLENGE_DONE {
consensus.Log.Debug("Enough commits received with signatures", "numOfSignatures", len(consensus.commits))
if len(consensus.commitments) >= (2*len(consensus.validators))/3+1 && consensus.state < CHALLENGE_DONE {
consensus.Log.Debug("Enough commitments received with signatures", "numOfSignatures", len(consensus.commitments))
// Broadcast challenge
msgToSend := consensus.constructChallengeMessage()
@ -204,10 +211,14 @@ func (consensus *Consensus) constructChallengeMessage() []byte {
buffer.Write(twoBytes)
// 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
buffer.Write(getAggregatedKey(consensus.commits))
buffer.Write(getAggregatedKey(consensus.bitmap))
// 32 byte challenge
buffer.Write(getChallenge())
@ -216,29 +227,22 @@ func (consensus *Consensus) constructChallengeMessage() []byte {
signature := signMessage(buffer.Bytes())
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 {
// TODO: implement actual commit aggregation
var commitArray []string
for _, val := range commits {
commitArray = append(commitArray, val)
func getAggregatedCommit(commitments []kyber.Point, bitmap *crypto.Mask) []byte {
aggCommitment := crypto.AggregateCommitmentsOnly(crypto.Curve, commitments)
bytes, err := aggCommitment.MarshalBinary()
if err != nil {
panic("Failed to deserialize the aggregated commitment")
}
var commit [32]byte
commit = sha256.Sum256([]byte(strings.Join(commitArray, "")))
return append(commit[:], byte(0))
return append(bytes[:], byte(0))
}
func getAggregatedKey(commits map[string]string) []byte {
func getAggregatedKey(bitmap *crypto.Mask) []byte {
// TODO: implement actual key aggregation
var commitArray []string
for key := range commits {
commitArray = append(commitArray, key)
}
var commit [32]byte
commit = sha256.Sum256([]byte(strings.Join(commitArray, "")))
return append(commit[:], byte(0))
commitment := sha256.Sum256([]byte("ABC"))
return append(commitment[:], byte(0))
}
func getChallenge() []byte {

@ -1,6 +1,7 @@
package consensus
import (
"harmony-benchmark/crypto"
"harmony-benchmark/p2p"
"testing"
)
@ -19,10 +20,25 @@ func TestConstructAnnounceMessage(test *testing.T) {
}
func TestConstructChallengeMessage(test *testing.T) {
leader := p2p.Peer{Ip: "1", Port: "2"}
validator := p2p.Peer{Ip: "3", Port: "5"}
leaderPriKey := crypto.Curve.Scalar()
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.blockHash = [32]byte{}
consensus.commitments[0] = leaderPubKey
consensus.commitments[1] = validatorPubKey
consensus.bitmap.SetKey(leaderPubKey, true)
consensus.bitmap.SetKey(validatorPubKey, true)
msg := consensus.constructChallengeMessage()
if len(msg) != 1+1+1+4+32+2+33+33+32+64 {

@ -1,7 +1,6 @@
package consensus
import (
"harmony-benchmark/common"
"harmony-benchmark/p2p"
"testing"
)
@ -22,14 +21,6 @@ func TestNewConsensus(test *testing.T) {
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 {
test.Error("Consensus Leader is set to wrong Peer")
}

@ -4,33 +4,36 @@ import (
"bytes"
"encoding/binary"
"encoding/gob"
"github.com/dedis/kyber"
"harmony-benchmark/attack"
"harmony-benchmark/blockchain"
"harmony-benchmark/crypto"
"harmony-benchmark/p2p"
proto_consensus "harmony-benchmark/proto/consensus"
"regexp"
"strconv"
)
// Validator's consensus message dispatcher
func (consensus *Consensus) ProcessMessageValidator(message []byte) {
msgType, err := GetConsensusMessageType(message)
msgType, err := proto_consensus.GetConsensusMessageType(message)
if err != nil {
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 {
consensus.Log.Error("Failed to get consensus message payload", "err", err, "consensus", consensus)
}
switch msgType {
case ANNOUNCE:
case proto_consensus.ANNOUNCE:
consensus.processAnnounceMessage(payload)
case COMMIT:
case proto_consensus.COMMIT:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
case CHALLENGE:
case proto_consensus.CHALLENGE:
consensus.processChallengeMessage(payload)
case RESPONSE:
case proto_consensus.RESPONSE:
consensus.Log.Error("Unexpected message type", "msgType", msgType, "consensus", consensus)
default:
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
// 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)
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)
func (consensus *Consensus) constructCommitMessage() []byte {
func (consensus *Consensus) constructCommitMessage() (secret kyber.Scalar, commitMsg []byte) {
buffer := bytes.NewBuffer([]byte{})
// 4 byte consensus id
@ -152,20 +158,15 @@ func (consensus *Consensus) constructCommitMessage() []byte {
binary.BigEndian.PutUint16(twoBytes, consensus.nodeId)
buffer.Write(twoBytes)
// 33 byte of commit
commit := getCommitMessage()
buffer.Write(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)
secret, commitment := crypto.Commit(crypto.Curve)
commitment.MarshalTo(buffer)
// 64 byte of signature on previous data
signature := signMessage(buffer.Bytes())
buffer.Write(signature)
return consensus.ConstructConsensusMessage(COMMIT, buffer.Bytes())
}
func getCommitMessage() []byte {
// TODO: use real cosi signature
return make([]byte, 33)
return secret, proto_consensus.ConstructConsensusMessage(proto_consensus.COMMIT, buffer.Bytes())
}
// 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.
}
// 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
// For now, simply return the private key of this node.
@ -324,7 +325,7 @@ func (consensus *Consensus) constructResponseMessage() []byte {
signature := signMessage(buffer.Bytes())
buffer.Write(signature)
return consensus.ConstructConsensusMessage(RESPONSE, buffer.Bytes())
return proto_consensus.ConstructConsensusMessage(proto_consensus.RESPONSE, buffer.Bytes())
}
func getResponseMessage() []byte {

@ -10,9 +10,9 @@ func TestConstructCommitMessage(test *testing.T) {
validator := p2p.Peer{Ip: "3", Port: "5"}
consensus := NewConsensus("1", "2", "0", []p2p.Peer{leader, validator}, leader)
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))
}
}

@ -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"
"encoding/gob"
"harmony-benchmark/blockchain"
"harmony-benchmark/client"
"harmony-benchmark/common"
"harmony-benchmark/consensus"
"harmony-benchmark/p2p"
"harmony-benchmark/proto"
"harmony-benchmark/proto/client"
"harmony-benchmark/proto/consensus"
proto_node "harmony-benchmark/proto/node"
"net"
"os"
"time"
@ -31,27 +32,27 @@ func (node *Node) NodeHandler(conn net.Conn) {
}
consensusObj := node.Consensus
msgCategory, err := common.GetMessageCategory(content)
msgCategory, err := proto.GetMessageCategory(content)
if err != nil {
node.log.Error("Read node type failed", "err", err, "node", node)
return
}
msgType, err := common.GetMessageType(content)
msgType, err := proto.GetMessageType(content)
if err != nil {
node.log.Error("Read action type failed", "err", err, "node", node)
return
}
msgPayload, err := common.GetMessagePayload(content)
msgPayload, err := proto.GetMessagePayload(content)
if err != nil {
node.log.Error("Read message payload failed", "err", err, "node", node)
return
}
switch msgCategory {
case common.COMMITTEE:
actionType := consensus.CommitteeMessageType(msgType)
case proto.CONSENSUS:
actionType := consensus.ConsensusMessageType(msgType)
switch actionType {
case consensus.CONSENSUS:
if consensusObj.IsLeader {
@ -60,16 +61,16 @@ func (node *Node) NodeHandler(conn net.Conn) {
consensusObj.ProcessMessageValidator(msgPayload)
}
}
case common.NODE:
actionType := NodeMessageType(msgType)
case proto.NODE:
actionType := proto_node.NodeMessageType(msgType)
switch actionType {
case TRANSACTION:
case proto_node.TRANSACTION:
node.transactionMessageHandler(msgPayload)
case BLOCK:
case proto_node.BLOCK:
if node.Client != nil {
blockMsgType := BlockMessageType(msgPayload[0])
blockMsgType := proto_node.BlockMessageType(msgPayload[0])
switch blockMsgType {
case SYNC:
case proto_node.SYNC:
decoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the SYNC messge type
blocks := new([]*blockchain.Block)
decoder.Decode(blocks)
@ -78,9 +79,9 @@ func (node *Node) NodeHandler(conn net.Conn) {
}
}
}
case CONTROL:
case proto_node.CONTROL:
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())
sizeInBytes := node.UtxoPool.GetSizeInByteOfUtxoMap()
@ -111,7 +112,7 @@ func (node *Node) NodeHandler(conn net.Conn) {
os.Exit(0)
}
}
case common.CLIENT:
case proto.CLIENT:
actionType := client.ClientMessageType(msgType)
switch actionType {
case client.TRANSACTION:
@ -123,10 +124,10 @@ func (node *Node) NodeHandler(conn net.Conn) {
}
func (node *Node) transactionMessageHandler(msgPayload []byte) {
txMessageType := TransactionMessageType(msgPayload[0])
txMessageType := proto_node.TransactionMessageType(msgPayload[0])
switch txMessageType {
case SEND:
case proto_node.SEND:
txDecoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the SEND messge type
txList := new([]*blockchain.Transaction)
@ -135,7 +136,7 @@ func (node *Node) transactionMessageHandler(msgPayload []byte) {
node.log.Error("Failed deserializing transaction list", "node", node)
}
node.addPendingTransactions(*txList)
case REQUEST:
case proto_node.REQUEST:
reader := bytes.NewBuffer(msgPayload[1:])
var txIds map[[32]byte]bool
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
case UNLOCK:
case proto_node.UNLOCK:
txAndProofDecoder := gob.NewDecoder(bytes.NewReader(msgPayload[1:])) // skip the UNLOCK messge type
txAndProofs := new([]*blockchain.Transaction)
@ -242,7 +243,7 @@ func (node *Node) SendBackProofOfAcceptOrReject() {
func (node *Node) BroadcastNewBlock(newBlock *blockchain.Block) {
if node.ClientPeer != nil {
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 (
"bytes"
"encoding/binary"
"github.com/dedis/kyber"
"harmony-benchmark/attack"
"log"
"net"
@ -14,7 +15,7 @@ import (
type Peer struct {
Ip string // Ip address 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

@ -4,7 +4,8 @@ import (
"bytes"
"encoding/gob"
"harmony-benchmark/blockchain"
"harmony-benchmark/common"
"harmony-benchmark/proto"
"harmony-benchmark/proto/node"
)
// 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
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(PROOF_OF_LOCK))
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
func ConstructUnlockToCommitOrAbortMessage(txsAndProofs []blockchain.Transaction) []byte {
byteBuffer := bytes.NewBuffer([]byte{byte(common.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(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 := bytes.NewBuffer([]byte{byte(proto.NODE)})
byteBuffer.WriteByte(byte(node.TRANSACTION))
byteBuffer.WriteByte(byte(node.UNLOCK))
encoder := gob.NewEncoder(byteBuffer)
encoder.Encode(txsAndProofs)
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 (
"bytes"
"errors"
"harmony-benchmark/proto"
)
/*
@ -33,7 +34,7 @@ COMMIT:
4 byte - consensus id
32 byte - block hash
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
---- message end -----
@ -66,14 +67,14 @@ RESPONSE:
---- message end -----
*/
// the number of bytes consensus action type occupies
const ACTION_TYPE_BYTES = 1
// the number of bytes consensus message type occupies
const CONSENSUS_MESSAGE_TYPE_BYTES = 1
// The specific types of message under COMMITTEE category
type CommitteeMessageType byte
// The specific types of message under CONSENSUS category
type ConsensusMessageType byte
const (
CONSENSUS CommitteeMessageType = iota
CONSENSUS ConsensusMessageType = iota
// TODO: add more types
)
@ -118,13 +119,13 @@ func GetConsensusMessagePayload(message []byte) ([]byte, error) {
if len(message) < 2 {
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
func (consensus Consensus) ConstructConsensusMessage(consensusMsgType MessageType, payload []byte) []byte {
byteBuffer := bytes.NewBuffer([]byte{consensus.msgCategory})
byteBuffer.WriteByte(consensus.msgType)
func ConstructConsensusMessage(consensusMsgType MessageType, payload []byte) []byte {
byteBuffer := bytes.NewBuffer([]byte{byte(proto.CONSENSUS)})
byteBuffer.WriteByte(byte(CONSENSUS))
byteBuffer.WriteByte(byte(consensusMsgType))
byteBuffer.Write(payload)
return byteBuffer.Bytes()

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