From 6a408a2e9782f0adca109b08caded402b23286de Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 28 Jul 2018 15:57:56 +0800 Subject: [PATCH 01/19] Change COMMITTEE message category to CONSENSUS, which is more understandable --- common/message.go | 10 +++++----- consensus/consensus.go | 2 +- consensus/consensus_test.go | 4 ++-- consensus/message.go | 6 +++--- node/node_handler.go | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/common/message.go b/common/message.go index c1dafc80d..f8c8da6b1 100644 --- a/common/message.go +++ b/common/message.go @@ -7,14 +7,14 @@ import ( // TODO: Fix the comments below. /* -Node will process the content of the p2p message +The message structure of any message in Harmony network ---- content start ----- 1 byte - message category - 0x00: COMMITTEE + 0x00: CONSENSUS 0x01: NODE... 1 byte - message type - - for COMMITTEE category + - for CONSENSUS category 0x00: consensus 0x01: sharding ... - for NODE category @@ -28,7 +28,7 @@ n - 2 bytes - actual message payload const NODE_TYPE_BYTES = 1 // ACTION_TYPE_BYTES is # of bytes for message type which can be -// - for COMMITTEE category +// - for CONSENSUS category // 0x00: consensus // 0x01: sharding ... // - for NODE category @@ -39,7 +39,7 @@ const ACTION_TYPE_BYTES = 1 type MessageCategory byte const ( - COMMITTEE MessageCategory = iota + CONSENSUS MessageCategory = iota NODE CLIENT // TODO: add more types diff --git a/consensus/consensus.go b/consensus/consensus.go index 14c0fc06d..400901f77 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -126,7 +126,7 @@ 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.msgCategory = byte(common.CONSENSUS) consensus.msgType = byte(CONSENSUS) consensus.Log = log.New() diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index 655c22a65..3dbf21fe9 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -26,8 +26,8 @@ func TestNewConsensus(test *testing.T) { test.Error("Consensus msgType should be CONSENSUS") } - if consensus.msgCategory != byte(common.COMMITTEE) { - test.Error("Consensus msgCategory should be COMMITTEE") + if consensus.msgCategory != byte(common.CONSENSUS) { + test.Error("Consensus msgCategory should be CONSENSUS") } if consensus.leader != leader { diff --git a/consensus/message.go b/consensus/message.go index 230711b98..87d0ffe9d 100644 --- a/consensus/message.go +++ b/consensus/message.go @@ -69,11 +69,11 @@ RESPONSE: // the number of bytes consensus action type occupies const ACTION_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 ) diff --git a/node/node_handler.go b/node/node_handler.go index 43c79c1a6..34b8e8b08 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -50,8 +50,8 @@ func (node *Node) NodeHandler(conn net.Conn) { } switch msgCategory { - case common.COMMITTEE: - actionType := consensus.CommitteeMessageType(msgType) + case common.CONSENSUS: + actionType := consensus.ConsensusMessageType(msgType) switch actionType { case consensus.CONSENSUS: if consensusObj.IsLeader { From a54d4487b715c346b93f66a6796ebd7d17a826e7 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 28 Jul 2018 16:22:37 +0800 Subject: [PATCH 02/19] Correct const names for message category and type size --- common/message.go | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/common/message.go b/common/message.go index f8c8da6b1..c1a303a02 100644 --- a/common/message.go +++ b/common/message.go @@ -24,18 +24,13 @@ n - 2 bytes - actual message payload */ -// NODE_TYPE_BYTES is # of bytes message category -const NODE_TYPE_BYTES = 1 +// MESSAGE_CATEGORY_BYTES is the number of bytes message category takes +const MESSAGE_CATEGORY_BYTES = 1 -// ACTION_TYPE_BYTES is # of bytes for message type which can be -// - for CONSENSUS category -// 0x00: consensus -// 0x01: sharding ... -// - for NODE category -// 0x00: transaction ... -const ACTION_TYPE_BYTES = 1 +// MESSAGE_TYPE_BYTES is the number of bytes message type takes +const MESSAGE_TYPE_BYTES = 1 -// The CATEGORY of messages +// The message category enum type MessageCategory byte const ( @@ -47,24 +42,24 @@ const ( // 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.") + if len(message) < MESSAGE_CATEGORY_BYTES { + return 0, errors.New("Failed to get message category: no data available.") } - return MessageCategory(message[NODE_TYPE_BYTES-1]), nil + 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) < NODE_TYPE_BYTES+ACTION_TYPE_BYTES { - return 0, errors.New("Failed to get action type: no data available.") + if len(message) < MESSAGE_CATEGORY_BYTES+MESSAGE_TYPE_BYTES { + return 0, errors.New("Failed to get message type: no data available.") } - return byte(message[NODE_TYPE_BYTES+ACTION_TYPE_BYTES-1]), nil + 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) < NODE_TYPE_BYTES+ACTION_TYPE_BYTES { + if len(message) < MESSAGE_CATEGORY_BYTES+MESSAGE_TYPE_BYTES { return []byte{}, errors.New("Failed to get message payload: no data available.") } - return message[NODE_TYPE_BYTES+ACTION_TYPE_BYTES:], nil + return message[MESSAGE_CATEGORY_BYTES+MESSAGE_TYPE_BYTES:], nil } From 3d37dae7ca0aa17c9532d567e951de1b06ff2bf3 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 28 Jul 2018 16:34:55 +0800 Subject: [PATCH 03/19] Refactor common folder into proto folder and rename common/message.go into proto/common.go --- client/message.go | 10 +++++----- consensus/consensus.go | 4 ++-- consensus/consensus_test.go | 4 ++-- node/message.go | 12 ++++++------ node/node_handler.go | 14 +++++++------- common/message.go => proto/common.go | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) rename common/message.go => proto/common.go (99%) diff --git a/client/message.go b/client/message.go index 372fe004c..3c6df8716 100644 --- a/client/message.go +++ b/client/message.go @@ -4,7 +4,7 @@ import ( "bytes" "encoding/gob" "harmony-benchmark/blockchain" - "harmony-benchmark/common" + "harmony-benchmark/proto" ) // The specific types of message under CLIENT category @@ -24,7 +24,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 +35,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(0)) // A temporary hack to represent node.TRANSACTION, to avoid cyclical import. TODO: Potentially solution is to refactor all the message enums into a proto 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 proto package encoder := gob.NewEncoder(byteBuffer) encoder.Encode(txsAndProofs) return byteBuffer.Bytes() diff --git a/consensus/consensus.go b/consensus/consensus.go index 400901f77..aed834f02 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -4,9 +4,9 @@ package consensus // consensus import ( "fmt" "harmony-benchmark/blockchain" - "harmony-benchmark/common" "harmony-benchmark/log" "harmony-benchmark/p2p" + "harmony-benchmark/proto" "regexp" "strconv" "sync" @@ -126,7 +126,7 @@ 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.CONSENSUS) + consensus.msgCategory = byte(proto.CONSENSUS) consensus.msgType = byte(CONSENSUS) consensus.Log = log.New() diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index 3dbf21fe9..ce3a863d7 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -1,8 +1,8 @@ package consensus import ( - "harmony-benchmark/common" "harmony-benchmark/p2p" + "harmony-benchmark/proto" "testing" ) @@ -26,7 +26,7 @@ func TestNewConsensus(test *testing.T) { test.Error("Consensus msgType should be CONSENSUS") } - if consensus.msgCategory != byte(common.CONSENSUS) { + if consensus.msgCategory != byte(proto.CONSENSUS) { test.Error("Consensus msgCategory should be CONSENSUS") } diff --git a/node/message.go b/node/message.go index fa1278e5d..2a8691fa2 100644 --- a/node/message.go +++ b/node/message.go @@ -4,14 +4,14 @@ 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 // TODO: Don't move this until the hack in client/common.go is resolved BLOCK CONTROL @@ -43,7 +43,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 +58,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 +69,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 +77,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) diff --git a/node/node_handler.go b/node/node_handler.go index 34b8e8b08..8b5fc7cd4 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -5,9 +5,9 @@ import ( "encoding/gob" "harmony-benchmark/blockchain" "harmony-benchmark/client" - "harmony-benchmark/common" "harmony-benchmark/consensus" "harmony-benchmark/p2p" + "harmony-benchmark/proto" "net" "os" "time" @@ -31,26 +31,26 @@ 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.CONSENSUS: + case proto.CONSENSUS: actionType := consensus.ConsensusMessageType(msgType) switch actionType { case consensus.CONSENSUS: @@ -60,7 +60,7 @@ func (node *Node) NodeHandler(conn net.Conn) { consensusObj.ProcessMessageValidator(msgPayload) } } - case common.NODE: + case proto.NODE: actionType := NodeMessageType(msgType) switch actionType { case TRANSACTION: @@ -111,7 +111,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: diff --git a/common/message.go b/proto/common.go similarity index 99% rename from common/message.go rename to proto/common.go index c1a303a02..0a600d82b 100644 --- a/common/message.go +++ b/proto/common.go @@ -1,4 +1,4 @@ -package common +package proto import ( "errors" From 737732150fa6289cc976546ef550e2ed44ffbf61 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 28 Jul 2018 16:53:54 +0800 Subject: [PATCH 04/19] Refactor node/message.go to proto/node.go, p2p/message.go to p2p.message_reader.go --- p2p/{message.go => message_reader.go} | 0 proto/common.go | 1 - node/message.go => proto/node.go | 11 +++++------ 3 files changed, 5 insertions(+), 7 deletions(-) rename p2p/{message.go => message_reader.go} (100%) rename node/message.go => proto/node.go (88%) diff --git a/p2p/message.go b/p2p/message_reader.go similarity index 100% rename from p2p/message.go rename to p2p/message_reader.go diff --git a/proto/common.go b/proto/common.go index 0a600d82b..0f2baea7f 100644 --- a/proto/common.go +++ b/proto/common.go @@ -4,7 +4,6 @@ import ( "errors" ) -// TODO: Fix the comments below. /* The message structure of any message in Harmony network diff --git a/node/message.go b/proto/node.go similarity index 88% rename from node/message.go rename to proto/node.go index 2a8691fa2..f56f42de2 100644 --- a/node/message.go +++ b/proto/node.go @@ -1,10 +1,9 @@ -package node +package proto import ( "bytes" "encoding/gob" "harmony-benchmark/blockchain" - "harmony-benchmark/proto" ) // The specific types of message under NODE category @@ -43,7 +42,7 @@ const ( // Constructs serialized transactions func ConstructTransactionListMessage(transactions []*blockchain.Transaction) []byte { - byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)}) + byteBuffer := bytes.NewBuffer([]byte{byte(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(proto.NODE)}) + byteBuffer := bytes.NewBuffer([]byte{byte(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(proto.NODE)}) + byteBuffer := bytes.NewBuffer([]byte{byte(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(proto.NODE)}) + byteBuffer := bytes.NewBuffer([]byte{byte(NODE)}) byteBuffer.WriteByte(byte(BLOCK)) byteBuffer.WriteByte(byte(SYNC)) encoder := gob.NewEncoder(byteBuffer) From 80ae8cdcf6aed4ffc9a2e07c327b93fc03dc09ab Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 28 Jul 2018 17:09:39 +0800 Subject: [PATCH 05/19] Refactor proto node.go into it's own package --- client/txgen/main.go | 7 ++++--- proto/common.go | 14 ++++++-------- proto/{ => node}/node.go | 11 ++++++----- 3 files changed, 16 insertions(+), 16 deletions(-) rename proto/{ => node}/node.go (88%) diff --git a/client/txgen/main.go b/client/txgen/main.go index 9d2dd9986..772e7f4fb 100644 --- a/client/txgen/main.go +++ b/client/txgen/main.go @@ -11,6 +11,7 @@ import ( "harmony-benchmark/log" "harmony-benchmark/node" "harmony-benchmark/p2p" + proto_node "harmony-benchmark/proto/node" "math/rand" "strconv" "sync" @@ -301,14 +302,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 @@ -325,7 +326,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) } diff --git a/proto/common.go b/proto/common.go index 0f2baea7f..fd697013b 100644 --- a/proto/common.go +++ b/proto/common.go @@ -5,7 +5,6 @@ import ( ) /* - The message structure of any message in Harmony network ---- content start ----- @@ -20,15 +19,8 @@ The message structure of any message in Harmony network 0x00: transaction ... n - 2 bytes - actual message payload ---- content end ----- - */ -// 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 - // The message category enum type MessageCategory byte @@ -39,6 +31,12 @@ const ( // 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 { diff --git a/proto/node.go b/proto/node/node.go similarity index 88% rename from proto/node.go rename to proto/node/node.go index f56f42de2..2a8691fa2 100644 --- a/proto/node.go +++ b/proto/node/node.go @@ -1,9 +1,10 @@ -package proto +package node import ( "bytes" "encoding/gob" "harmony-benchmark/blockchain" + "harmony-benchmark/proto" ) // The specific types of message under NODE category @@ -42,7 +43,7 @@ const ( // Constructs serialized transactions func ConstructTransactionListMessage(transactions []*blockchain.Transaction) []byte { - byteBuffer := bytes.NewBuffer([]byte{byte(NODE)}) + byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)}) byteBuffer.WriteByte(byte(TRANSACTION)) byteBuffer.WriteByte(byte(SEND)) encoder := gob.NewEncoder(byteBuffer) @@ -57,7 +58,7 @@ func ConstructTransactionListMessage(transactions []*blockchain.Transaction) []b // Constructs serialized transactions func ConstructRequestTransactionsMessage(transactionIds [][]byte) []byte { - byteBuffer := bytes.NewBuffer([]byte{byte(NODE)}) + byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)}) byteBuffer.WriteByte(byte(TRANSACTION)) byteBuffer.WriteByte(byte(REQUEST)) for _, txId := range transactionIds { @@ -68,7 +69,7 @@ func ConstructRequestTransactionsMessage(transactionIds [][]byte) []byte { // Constructs STOP message for node to stop func ConstructStopMessage() []byte { - byteBuffer := bytes.NewBuffer([]byte{byte(NODE)}) + byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)}) byteBuffer.WriteByte(byte(CONTROL)) byteBuffer.WriteByte(byte(STOP)) return byteBuffer.Bytes() @@ -76,7 +77,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(NODE)}) + byteBuffer := bytes.NewBuffer([]byte{byte(proto.NODE)}) byteBuffer.WriteByte(byte(BLOCK)) byteBuffer.WriteByte(byte(SYNC)) encoder := gob.NewEncoder(byteBuffer) From f783dbb47b48b9a64234f8e2c5cb3f94f1200151 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 28 Jul 2018 17:29:35 +0800 Subject: [PATCH 06/19] Refactor consensus and client's message.go file --- consensus/consensus.go | 9 --------- client/message.go => proto/client/client.go | 5 +++-- consensus/message.go => proto/consensus/consensus.go | 7 ++++--- proto/node/node.go | 4 ++-- 4 files changed, 9 insertions(+), 16 deletions(-) rename client/message.go => proto/client/client.go (76%) rename consensus/message.go => proto/consensus/consensus.go (93%) diff --git a/consensus/consensus.go b/consensus/consensus.go index aed834f02..9509af4f7 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -6,7 +6,6 @@ import ( "harmony-benchmark/blockchain" "harmony-benchmark/log" "harmony-benchmark/p2p" - "harmony-benchmark/proto" "regexp" "strconv" "sync" @@ -53,10 +52,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 } @@ -125,10 +120,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(proto.CONSENSUS) - consensus.msgType = byte(CONSENSUS) - consensus.Log = log.New() return &consensus } diff --git a/client/message.go b/proto/client/client.go similarity index 76% rename from client/message.go rename to proto/client/client.go index 3c6df8716..74700c443 100644 --- a/client/message.go +++ b/proto/client/client.go @@ -5,6 +5,7 @@ import ( "encoding/gob" "harmony-benchmark/blockchain" "harmony-benchmark/proto" + "harmony-benchmark/proto/node" ) // The specific types of message under CLIENT category @@ -36,8 +37,8 @@ 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(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 proto 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 proto package + byteBuffer.WriteByte(byte(node.TRANSACTION)) + byteBuffer.WriteByte(byte(node.UNLOCK)) encoder := gob.NewEncoder(byteBuffer) encoder.Encode(txsAndProofs) return byteBuffer.Bytes() diff --git a/consensus/message.go b/proto/consensus/consensus.go similarity index 93% rename from consensus/message.go rename to proto/consensus/consensus.go index 87d0ffe9d..8ff738454 100644 --- a/consensus/message.go +++ b/proto/consensus/consensus.go @@ -3,6 +3,7 @@ package consensus import ( "bytes" "errors" + "harmony-benchmark/proto" ) /* @@ -122,9 +123,9 @@ func GetConsensusMessagePayload(message []byte) ([]byte, error) { } // 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() diff --git a/proto/node/node.go b/proto/node/node.go index 2a8691fa2..47828bc49 100644 --- a/proto/node/node.go +++ b/proto/node/node.go @@ -11,7 +11,7 @@ import ( type NodeMessageType byte const ( - TRANSACTION NodeMessageType = iota // TODO: Don't move this until the hack in client/common.go is resolved + TRANSACTION NodeMessageType = iota BLOCK CONTROL @@ -24,7 +24,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 From 651af74bf04e13a03f6bc292e2b213a06cb69af0 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 28 Jul 2018 18:05:47 +0800 Subject: [PATCH 07/19] Fix bad code from last refactoring --- client/client.go | 7 ++++--- consensus/consensus_leader.go | 19 ++++++++++--------- consensus/consensus_validator.go | 17 +++++++++-------- node/node_handler.go | 29 +++++++++++++++-------------- proto/consensus/consensus.go | 6 +++--- proto/node/node.go | 1 - 6 files changed, 41 insertions(+), 38 deletions(-) diff --git a/client/client.go b/client/client.go index 7397275ff..c7dd891a4 100644 --- a/client/client.go +++ b/client/client.go @@ -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 diff --git a/consensus/consensus_leader.go b/consensus/consensus_leader.go index 850d54430..8ddf30e5e 100644 --- a/consensus/consensus_leader.go +++ b/consensus/consensus_leader.go @@ -7,6 +7,7 @@ import ( "encoding/gob" "harmony-benchmark/blockchain" "harmony-benchmark/p2p" + proto_consensus "harmony-benchmark/proto/consensus" "strings" "time" ) @@ -33,26 +34,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) @@ -111,7 +112,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 { @@ -216,7 +217,7 @@ 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 { diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 076de3183..28d88c1b3 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -7,30 +7,31 @@ import ( "harmony-benchmark/attack" "harmony-benchmark/blockchain" "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) @@ -160,7 +161,7 @@ func (consensus *Consensus) constructCommitMessage() []byte { signature := signMessage(buffer.Bytes()) buffer.Write(signature) - return consensus.ConstructConsensusMessage(COMMIT, buffer.Bytes()) + return proto_consensus.ConstructConsensusMessage(proto_consensus.COMMIT, buffer.Bytes()) } func getCommitMessage() []byte { @@ -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 { diff --git a/node/node_handler.go b/node/node_handler.go index 8b5fc7cd4..68d89799f 100644 --- a/node/node_handler.go +++ b/node/node_handler.go @@ -4,10 +4,11 @@ import ( "bytes" "encoding/gob" "harmony-benchmark/blockchain" - "harmony-benchmark/client" - "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" @@ -61,15 +62,15 @@ func (node *Node) NodeHandler(conn net.Conn) { } } case proto.NODE: - actionType := NodeMessageType(msgType) + 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() @@ -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})) } } diff --git a/proto/consensus/consensus.go b/proto/consensus/consensus.go index 8ff738454..c26925e97 100644 --- a/proto/consensus/consensus.go +++ b/proto/consensus/consensus.go @@ -67,8 +67,8 @@ 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 CONSENSUS category type ConsensusMessageType byte @@ -119,7 +119,7 @@ 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 diff --git a/proto/node/node.go b/proto/node/node.go index 47828bc49..960793c7e 100644 --- a/proto/node/node.go +++ b/proto/node/node.go @@ -14,7 +14,6 @@ const ( TRANSACTION NodeMessageType = iota BLOCK CONTROL - // TODO: add more types ) From 82b93a26d2f12221851b08235019d9ea48c15ba2 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Sat, 28 Jul 2018 18:22:15 +0800 Subject: [PATCH 08/19] Fix consensus_test.go --- consensus/consensus_test.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index ce3a863d7..341fd708e 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -2,7 +2,6 @@ package consensus import ( "harmony-benchmark/p2p" - "harmony-benchmark/proto" "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(proto.CONSENSUS) { - test.Error("Consensus msgCategory should be CONSENSUS") - } - if consensus.leader != leader { test.Error("Consensus Leader is set to wrong Peer") } From fe9bb236b79e2763f80e3f4e8e24c40c453abab1 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Tue, 31 Jul 2018 21:09:05 +0800 Subject: [PATCH 09/19] Define cryptography primitives: Point, Scalar. Following dedis/cothority/kyber --- crypto/primitives.go | 109 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 crypto/primitives.go diff --git a/crypto/primitives.go b/crypto/primitives.go new file mode 100644 index 000000000..67bf65375 --- /dev/null +++ b/crypto/primitives.go @@ -0,0 +1,109 @@ +package crypto + +import ( + "crypto/cipher" +) + +// A Scalar kyber.y represents a scalar value by which +// a Point (group element) may be encrypted to produce another Point. +// This is an exponent in DSA-style groups, +// in which security is based on the Discrete Logarithm assumption, +// and a scalar multiplier in elliptic curve groups. +type Scalar interface { + // Equality test for two Scalars derived from the same Group + Equal(s2 Scalar) bool + + // Set sets the receiver equal to another Scalar a + Set(a Scalar) Scalar + + // Clone creates a new Scalar with same value + Clone() Scalar + + // Set sets the receiver to a small integer value + SetInt64(v int64) Scalar + + // Set to the additive identity (0) + Zero() Scalar + + // Set to the modular sum of scalars a and b + Add(a, b Scalar) Scalar + + // Set to the modular difference a - b + Sub(a, b Scalar) Scalar + + // Set to the modular negation of scalar a + Neg(a Scalar) Scalar + + // Set to the multiplicative identity (1) + One() Scalar + + // Set to the modular product of scalars a and b + Mul(a, b Scalar) Scalar + + // Set to the modular division of scalar a by scalar b + Div(a, b Scalar) Scalar + + // Set to the modular inverse of scalar a + Inv(a Scalar) Scalar + + // Set to a fresh random or pseudo-random scalar + Pick(rand cipher.Stream) Scalar + + // SetBytes sets the scalar from a byte-slice, + // reducing if necessary to the appropriate modulus. + // The endianess of the byte-slice is determined by the + // implementation. + SetBytes([]byte) Scalar +} + +// A Point kyber.y represents an element of a public-key cryptographic Group. +// For example, +// this is a number modulo the prime P in a DSA-style Schnorr group, +// or an (x, y) point on an elliptic curve. +// A Point can contain a Diffie-Hellman public key, an ElGamal ciphertext, etc. +type Point interface { + // Equality test for two Points derived from the same Group + Equal(s2 Point) bool + + // Null sets the receiver to the neutral identity element. + Null() Point + + // Set sets the receiver to this group's standard base point. + Base() Point + + // Pick sets the receiver to a fresh random or pseudo-random Point. + Pick(rand cipher.Stream) Point + + // Set sets the receiver equal to another Point p. + Set(p Point) Point + + // Clone clones the underlying point. + Clone() Point + + // Maximum number of bytes that can be embedded in a single + // group element via Pick(). + EmbedLen() int + + // Embed encodes a limited amount of specified data in the + // Point, using r as a source of cryptographically secure + // random data. Implementations only embed the first EmbedLen + // bytes of the given data. + Embed(data []byte, r cipher.Stream) Point + + // Extract data embedded in a point chosen via Embed(). + // Returns an error if doesn't represent valid embedded data. + Data() ([]byte, error) + + // Add points so that their scalars add homomorphically + Add(a, b Point) Point + + // Subtract points so that their scalars subtract homomorphically + Sub(a, b Point) Point + + // Set to the negation of point a + Neg(a Point) Point + + // Multiply point p by the scalar s. + // If p == nil, multiply with the standard base point Base(). + Mul(s Scalar, p Point) Point +} From 2e8ac6a6e70c565da11c38b9242b574165ce6ab5 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Tue, 31 Jul 2018 21:17:41 +0800 Subject: [PATCH 10/19] Fix import issue of btctxgen.go after my refactor on proto/ --- client/btctxgen/btctxgen.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/btctxgen/btctxgen.go b/client/btctxgen/btctxgen.go index 2e0021301..01a6ee939 100644 --- a/client/btctxgen/btctxgen.go +++ b/client/btctxgen/btctxgen.go @@ -12,6 +12,7 @@ import ( "harmony-benchmark/log" "harmony-benchmark/node" "harmony-benchmark/p2p" + proto_node "harmony-benchmark/proto/node" "math/rand" "sync" "time" @@ -247,14 +248,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 @@ -271,7 +272,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) } From a142e2a853cc891922867da6b4afd666f3dec385 Mon Sep 17 00:00:00 2001 From: Richard Liu Date: Tue, 31 Jul 2018 10:54:45 -0700 Subject: [PATCH 11/19] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb7d16620..163525655 100644 --- a/README.md +++ b/README.md @@ -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 ``` From b91f23c69606b984bf3e81dcff0511a7fab45d17 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 1 Aug 2018 13:27:02 +0800 Subject: [PATCH 12/19] Reuse Dedis lab's kyber library for schnorr multi-sig --- crypto/cosi.go | 399 +++++++++++++++++++++++++++++++++++++++++++ crypto/primitives.go | 109 ------------ crypto/suite.go | 10 ++ 3 files changed, 409 insertions(+), 109 deletions(-) create mode 100644 crypto/cosi.go delete mode 100644 crypto/primitives.go create mode 100644 crypto/suite.go diff --git a/crypto/cosi.go b/crypto/cosi.go new file mode 100644 index 000000000..ab18ca177 --- /dev/null +++ b/crypto/cosi.go @@ -0,0 +1,399 @@ +/* +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 +} + +// 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") +} + +// 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 +} diff --git a/crypto/primitives.go b/crypto/primitives.go deleted file mode 100644 index 67bf65375..000000000 --- a/crypto/primitives.go +++ /dev/null @@ -1,109 +0,0 @@ -package crypto - -import ( - "crypto/cipher" -) - -// A Scalar kyber.y represents a scalar value by which -// a Point (group element) may be encrypted to produce another Point. -// This is an exponent in DSA-style groups, -// in which security is based on the Discrete Logarithm assumption, -// and a scalar multiplier in elliptic curve groups. -type Scalar interface { - // Equality test for two Scalars derived from the same Group - Equal(s2 Scalar) bool - - // Set sets the receiver equal to another Scalar a - Set(a Scalar) Scalar - - // Clone creates a new Scalar with same value - Clone() Scalar - - // Set sets the receiver to a small integer value - SetInt64(v int64) Scalar - - // Set to the additive identity (0) - Zero() Scalar - - // Set to the modular sum of scalars a and b - Add(a, b Scalar) Scalar - - // Set to the modular difference a - b - Sub(a, b Scalar) Scalar - - // Set to the modular negation of scalar a - Neg(a Scalar) Scalar - - // Set to the multiplicative identity (1) - One() Scalar - - // Set to the modular product of scalars a and b - Mul(a, b Scalar) Scalar - - // Set to the modular division of scalar a by scalar b - Div(a, b Scalar) Scalar - - // Set to the modular inverse of scalar a - Inv(a Scalar) Scalar - - // Set to a fresh random or pseudo-random scalar - Pick(rand cipher.Stream) Scalar - - // SetBytes sets the scalar from a byte-slice, - // reducing if necessary to the appropriate modulus. - // The endianess of the byte-slice is determined by the - // implementation. - SetBytes([]byte) Scalar -} - -// A Point kyber.y represents an element of a public-key cryptographic Group. -// For example, -// this is a number modulo the prime P in a DSA-style Schnorr group, -// or an (x, y) point on an elliptic curve. -// A Point can contain a Diffie-Hellman public key, an ElGamal ciphertext, etc. -type Point interface { - // Equality test for two Points derived from the same Group - Equal(s2 Point) bool - - // Null sets the receiver to the neutral identity element. - Null() Point - - // Set sets the receiver to this group's standard base point. - Base() Point - - // Pick sets the receiver to a fresh random or pseudo-random Point. - Pick(rand cipher.Stream) Point - - // Set sets the receiver equal to another Point p. - Set(p Point) Point - - // Clone clones the underlying point. - Clone() Point - - // Maximum number of bytes that can be embedded in a single - // group element via Pick(). - EmbedLen() int - - // Embed encodes a limited amount of specified data in the - // Point, using r as a source of cryptographically secure - // random data. Implementations only embed the first EmbedLen - // bytes of the given data. - Embed(data []byte, r cipher.Stream) Point - - // Extract data embedded in a point chosen via Embed(). - // Returns an error if doesn't represent valid embedded data. - Data() ([]byte, error) - - // Add points so that their scalars add homomorphically - Add(a, b Point) Point - - // Subtract points so that their scalars subtract homomorphically - Sub(a, b Point) Point - - // Set to the negation of point a - Neg(a Point) Point - - // Multiply point p by the scalar s. - // If p == nil, multiply with the standard base point Base(). - Mul(s Scalar, p Point) Point -} diff --git a/crypto/suite.go b/crypto/suite.go new file mode 100644 index 000000000..69f9b0a4c --- /dev/null +++ b/crypto/suite.go @@ -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 +} From 3ceab2168d7404e1be5bb7840f3c055337c47326 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 1 Aug 2018 13:33:28 +0800 Subject: [PATCH 13/19] Create the secret and real commitment message for validator commit --- consensus/consensus.go | 3 +++ consensus/consensus_leader.go | 6 +++--- consensus/consensus_validator.go | 23 ++++++++++++----------- consensus/consensus_validator_test.go | 4 ++-- proto/consensus/consensus.go | 2 +- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 9509af4f7..29f24561f 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -3,6 +3,7 @@ package consensus // consensus import ( "fmt" + "github.com/dedis/kyber" "harmony-benchmark/blockchain" "harmony-benchmark/log" "harmony-benchmark/p2p" @@ -43,6 +44,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 diff --git a/consensus/consensus_leader.go b/consensus/consensus_leader.go index 8ddf30e5e..8ab3ce7f5 100644 --- a/consensus/consensus_leader.go +++ b/consensus/consensus_leader.go @@ -137,9 +137,9 @@ func (consensus *Consensus) processCommitMessage(payload []byte) { validatorId := string(payload[offset : offset+2]) offset += 2 - // 33 byte commit - commit := payload[offset : offset+33] - offset += 33 + // 32 byte commit + commit := payload[offset : offset+32] + offset += 32 // 64 byte of signature on previous data signature := payload[offset : offset+64] diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 28d88c1b3..333c73cea 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -4,8 +4,11 @@ import ( "bytes" "encoding/binary" "encoding/gob" + "github.com/dedis/kyber" + "github.com/dedis/kyber/group/edwards25519" "harmony-benchmark/attack" "harmony-benchmark/blockchain" + "harmony-benchmark/crypto" "harmony-benchmark/p2p" proto_consensus "harmony-benchmark/proto/consensus" "regexp" @@ -128,7 +131,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) @@ -137,7 +143,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 @@ -153,20 +159,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(edwards25519.NewBlakeSHA256Ed25519()) + commitment.MarshalTo(buffer) // 64 byte of signature on previous data signature := signMessage(buffer.Bytes()) buffer.Write(signature) - return proto_consensus.ConstructConsensusMessage(proto_consensus.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 diff --git a/consensus/consensus_validator_test.go b/consensus/consensus_validator_test.go index 0f9477e2b..dcd028066 100644 --- a/consensus/consensus_validator_test.go +++ b/consensus/consensus_validator_test.go @@ -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)) } } diff --git a/proto/consensus/consensus.go b/proto/consensus/consensus.go index c26925e97..f8af12045 100644 --- a/proto/consensus/consensus.go +++ b/proto/consensus/consensus.go @@ -34,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 ----- From 8f85868345ab7b082a77223ed0b2ff8bd279cac8 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 1 Aug 2018 17:21:46 +0800 Subject: [PATCH 14/19] Add ed25519 curve, hashing, pri/pub key utilities into crypto/ --- crypto/curve.go | 5 +++++ crypto/utils.go | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 crypto/curve.go create mode 100644 crypto/utils.go diff --git a/crypto/curve.go b/crypto/curve.go new file mode 100644 index 000000000..55174b87d --- /dev/null +++ b/crypto/curve.go @@ -0,0 +1,5 @@ +package crypto + +import "github.com/dedis/kyber/group/edwards25519" + +var Curve = edwards25519.NewBlakeSHA256Ed25519() diff --git a/crypto/utils.go b/crypto/utils.go new file mode 100644 index 000000000..9b53806b8 --- /dev/null +++ b/crypto/utils.go @@ -0,0 +1,16 @@ +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) +} From 86093d6ffc217eaf54bebe7bb4de5af9fe41d0c7 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 1 Aug 2018 17:23:58 +0800 Subject: [PATCH 15/19] Add real public and private key into Peer object; initialize schnorr multi-signature bitmap in the consensus --- benchmark.go | 6 +++++- consensus/consensus.go | 36 +++++++++++++++++++++----------- consensus/consensus_leader.go | 2 +- consensus/consensus_validator.go | 3 +-- p2p/peer.go | 7 ++++--- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/benchmark.go b/benchmark.go index e5e681a12..5ed80d1ab 100644 --- a/benchmark.go +++ b/benchmark.go @@ -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 diff --git a/consensus/consensus.go b/consensus/consensus.go index 29f24561f..1e9546374 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/dedis/kyber" "harmony-benchmark/blockchain" + "harmony-benchmark/crypto" "harmony-benchmark/log" "harmony-benchmark/p2p" "regexp" @@ -15,8 +16,10 @@ import ( // Consensus data containing all info related to one round of consensus process type Consensus struct { state ConsensusState - // Signatures collected from validators + // Commits collected from validators. commits map[string]string + // Commits collected from validators. + commitments *crypto.Mask // Signatures collected from validators responses map[string]string // List of validators @@ -24,7 +27,7 @@ type Consensus struct { // Leader leader p2p.Peer // private key of current node - priKey string + priKey [32]byte // Whether I am leader. False means I am validator IsLeader bool // Leader or validator Id - 2 byte @@ -74,11 +77,8 @@ 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 @@ -87,12 +87,24 @@ func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) * consensus.commits = make(map[string]string) consensus.responses = make(map[string]string) - consensus.leader = leaderPeer - consensus.validators = Peers + consensus.leader = leader + 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.commitments = mask - consensus.consensusId = 0 // or view Id in the original pbft paper + // Set private key for myself so that I can sign messages. + consensus.priKey = crypto.Hash(ip + ":" + port) // use ip:port as unique private key for now. TODO: use real private key + consensus.consensusId = 0 // or view Id in the original pbft paper myShardID, err := strconv.Atoi(ShardID) if err != nil { @@ -109,7 +121,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) @@ -143,5 +155,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) } diff --git a/consensus/consensus_leader.go b/consensus/consensus_leader.go index 8ab3ce7f5..54aa03f1a 100644 --- a/consensus/consensus_leader.go +++ b/consensus/consensus_leader.go @@ -81,6 +81,7 @@ 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 } // Constructs the announce message @@ -168,7 +169,6 @@ func (consensus *Consensus) processCommitMessage(payload []byte) { shouldProcess := !ok if shouldProcess { consensus.commits[validatorId] = validatorId - //consensus.Log.Debug("Number of commits received", "consensusId", consensus.consensusId, "count", len(consensus.commits)) } if !shouldProcess { diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 333c73cea..6c73d84dc 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -5,7 +5,6 @@ import ( "encoding/binary" "encoding/gob" "github.com/dedis/kyber" - "github.com/dedis/kyber/group/edwards25519" "harmony-benchmark/attack" "harmony-benchmark/blockchain" "harmony-benchmark/crypto" @@ -160,7 +159,7 @@ func (consensus *Consensus) constructCommitMessage() (secret kyber.Scalar, commi buffer.Write(twoBytes) // 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(edwards25519.NewBlakeSHA256Ed25519()) + secret, commitment := crypto.Commit(crypto.Curve) commitment.MarshalTo(buffer) // 64 byte of signature on previous data diff --git a/p2p/peer.go b/p2p/peer.go index e63df3ac9..8dec47784 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -3,6 +3,7 @@ package p2p import ( "bytes" "encoding/binary" + "github.com/dedis/kyber" "harmony-benchmark/attack" "log" "net" @@ -12,9 +13,9 @@ import ( // Peer is the object for a p2p peer (node) type Peer struct { - Ip string // Ip address of the peer - Port string // Port number of the peer - PubKey string // Public key of the peer + Ip string // Ip address of the peer + Port string // Port number of the peer + PubKey kyber.Point // Public key of the peer } // SendMessage sends the message to the peer From d115ba6ab9033dcdea18c7b5b0041d7e3adbf9ec Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 1 Aug 2018 17:57:20 +0800 Subject: [PATCH 16/19] Add SetKey function in cosi.go so we can directly set bitmap based on public key --- crypto/cosi.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crypto/cosi.go b/crypto/cosi.go index ab18ca177..9e1ba4cdb 100644 --- a/crypto/cosi.go +++ b/crypto/cosi.go @@ -326,6 +326,16 @@ func (m *Mask) KeyEnabled(public kyber.Point) (bool, error) { 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 { From c03c08d4d96e93afb7ee8cf9702679898e068b96 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 1 Aug 2018 17:58:37 +0800 Subject: [PATCH 17/19] Implement the crypto commitment for phase-announce and phase-commit in cosi --- consensus/consensus.go | 26 ++++++++++------- consensus/consensus_leader.go | 48 +++++++++++++++----------------- consensus/consensus_validator.go | 2 +- crypto/utils.go | 5 ++++ 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 1e9546374..31117696b 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -16,18 +16,20 @@ import ( // Consensus data containing all info related to one round of consensus process type Consensus struct { state ConsensusState + // Commits collected from validators. A map from node Id to its commitment + commitments map[uint16]kyber.Point // Commits collected from validators. - commits map[string]string - // Commits collected from validators. - commitments *crypto.Mask + 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 [32]byte + // 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 @@ -84,7 +86,7 @@ func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) * consensus.IsLeader = false } - consensus.commits = make(map[string]string) + consensus.commitments = make(map[uint16]kyber.Point) consensus.responses = make(map[string]string) consensus.leader = leader @@ -100,11 +102,15 @@ func NewConsensus(ip, port, ShardID string, peers []p2p.Peer, leader p2p.Peer) * if err != nil { panic("Failed to create commitment mask") } - consensus.commitments = mask + consensus.bitmap = mask // Set private key for myself so that I can sign messages. - consensus.priKey = crypto.Hash(ip + ":" + port) // use ip:port as unique private key for now. TODO: use real private key - consensus.consensusId = 0 // or view Id in the original pbft paper + 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) if err != nil { @@ -142,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) } diff --git a/consensus/consensus_leader.go b/consensus/consensus_leader.go index 54aa03f1a..778b7f8ed 100644 --- a/consensus/consensus_leader.go +++ b/consensus/consensus_leader.go @@ -5,10 +5,11 @@ import ( "crypto/sha256" "encoding/binary" "encoding/gob" + "github.com/dedis/kyber" "harmony-benchmark/blockchain" + "harmony-benchmark/crypto" "harmony-benchmark/p2p" proto_consensus "harmony-benchmark/proto/consensus" - "strings" "time" ) @@ -82,6 +83,10 @@ func (consensus *Consensus) startConsensus(newBlock *blockchain.Block) { // 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 @@ -135,11 +140,11 @@ 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 // 32 byte commit - commit := payload[offset : offset+32] + commitment := payload[offset : offset+32] offset += 32 // 64 byte of signature on previous data @@ -148,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 @@ -165,18 +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 + 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() @@ -205,10 +211,10 @@ func (consensus *Consensus) constructChallengeMessage() []byte { buffer.Write(twoBytes) // 33 byte aggregated commit - buffer.Write(getAggregatedCommit(consensus.commits)) + buffer.Write(getAggregatedCommit(consensus.commitments)) // 33 byte aggregated key - buffer.Write(getAggregatedKey(consensus.commits)) + buffer.Write(getAggregatedKey(consensus.bitmap)) // 32 byte challenge buffer.Write(getChallenge()) @@ -220,26 +226,16 @@ func (consensus *Consensus) constructChallengeMessage() []byte { return proto_consensus.ConstructConsensusMessage(proto_consensus.CHALLENGE, buffer.Bytes()) } -func getAggregatedCommit(commits map[string]string) []byte { +func getAggregatedCommit(commits map[uint16]kyber.Point) []byte { // TODO: implement actual commit aggregation - var commitArray []string - for _, val := range commits { - commitArray = append(commitArray, val) - } - 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 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 { diff --git a/consensus/consensus_validator.go b/consensus/consensus_validator.go index 6c73d84dc..4f80066a6 100644 --- a/consensus/consensus_validator.go +++ b/consensus/consensus_validator.go @@ -253,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. diff --git a/crypto/utils.go b/crypto/utils.go index 9b53806b8..04984aac6 100644 --- a/crypto/utils.go +++ b/crypto/utils.go @@ -14,3 +14,8 @@ func GetPublicKeyFromPrivateKey(suite Suite, priKey [32]byte) kyber.Point { 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) +} From b2ea1dacfa17bc289ebc0503dd30f3fa9d22dc36 Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 1 Aug 2018 18:28:47 +0800 Subject: [PATCH 18/19] Add utility function to aggregate commitments, but not bitmaps --- crypto/cosi.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crypto/cosi.go b/crypto/cosi.go index 9e1ba4cdb..301785e2b 100644 --- a/crypto/cosi.go +++ b/crypto/cosi.go @@ -74,6 +74,16 @@ func AggregateCommitments(suite Suite, commitments []kyber.Point, masks [][]byte 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). From 7e2634cdc09d97a20df72baaa8fbc1e7328994cd Mon Sep 17 00:00:00 2001 From: Rongjian Lan Date: Wed, 1 Aug 2018 18:29:15 +0800 Subject: [PATCH 19/19] Make leader aggregate real commitments --- consensus/consensus_leader.go | 17 ++++++++++++----- consensus/consensus_leader_test.go | 20 ++++++++++++++++++-- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/consensus/consensus_leader.go b/consensus/consensus_leader.go index 778b7f8ed..70c6d7c41 100644 --- a/consensus/consensus_leader.go +++ b/consensus/consensus_leader.go @@ -211,7 +211,11 @@ func (consensus *Consensus) constructChallengeMessage() []byte { buffer.Write(twoBytes) // 33 byte aggregated commit - buffer.Write(getAggregatedCommit(consensus.commitments)) + 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.bitmap)) @@ -226,10 +230,13 @@ func (consensus *Consensus) constructChallengeMessage() []byte { return proto_consensus.ConstructConsensusMessage(proto_consensus.CHALLENGE, buffer.Bytes()) } -func getAggregatedCommit(commits map[uint16]kyber.Point) []byte { - // TODO: implement actual commit aggregation - commitment := sha256.Sum256([]byte("ABC")) - return append(commitment[:], byte(0)) +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") + } + return append(bytes[:], byte(0)) } func getAggregatedKey(bitmap *crypto.Mask) []byte { diff --git a/consensus/consensus_leader_test.go b/consensus/consensus_leader_test.go index 6e1e96397..40c359662 100644 --- a/consensus/consensus_leader_test.go +++ b/consensus/consensus_leader_test.go @@ -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 {