Commit b23f5c28 authored by caopingcp's avatar caopingcp

tendermint add aggregate signature

parent 4b5cd0a0
......@@ -23,6 +23,7 @@ require (
github.com/miguelmota/go-solidity-sha3 v0.1.0
github.com/mr-tron/base58 v1.1.3
github.com/pborman/uuid v1.2.0
github.com/phoreproject/bls v0.0.0-20200525203911-a88a5ae26844
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v0.9.2 // indirect
github.com/prometheus/common v0.4.1
......
This diff is collapsed.
......@@ -43,6 +43,8 @@ dbPath="datadir/addrbook"
dbCache=4
grpcLogFile="grpc33.log"
[p2p.sub.dht]
channel=123
[rpc]
jrpcBindAddr="localhost:8801"
......@@ -73,6 +75,7 @@ powLimitBits = "0x1f2fffff"
[consensus.sub.tendermint]
genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
genesisAmount=100000000
genesisBlockTime=1514533394
timeoutTxAvail=1000
timeoutPropose=1000
......@@ -86,14 +89,21 @@ skipTimeoutCommit=false
createEmptyBlocks=true
createEmptyBlocksInterval=1
validatorNodes=["127.0.0.1:46656", "127.0.0.2:46656"]
fastSync=false
# Propose阶段是否预执行区块
preExec=false
# 签名算法,支持"secp256k1","ed25519","sm2","bls",默认为"ed25519"
signName="ed25519"
# 是否使用聚合签名,签名算法需支持该特性,比如"bls"
useAggregateSignature=false
[store]
name="mavl"
name="kvmvcc"
driver="leveldb"
dbPath="datadir/mavltree"
dbPath="datadir/kvmvcc"
dbCache=128
[store.sub.mavl]
[store.sub.kvmvcc]
enableMavlPrefix=false
enableMVCC=false
......@@ -126,3 +136,9 @@ signType="auth_ecdsa"
superManager=[
"14KEKbYtKKQm4wMthSK9J4La4nAiidGozt",
]
[metrics]
#是否使能发送metrics数据的发送
enableMetrics=false
#数据保存模式
dataEmitMode="influxdb"
......@@ -25,6 +25,7 @@ const (
tryListenSeconds = 5
handshakeTimeout = 20 // * time.Second,
maxSendQueueSize = 1024
minSendQueueSize = 10
defaultSendTimeout = 60 * time.Second
//MaxMsgPacketPayloadSize define
MaxMsgPacketPayloadSize = 10 * 1024 * 1024
......@@ -57,11 +58,12 @@ func Parallel(tasks ...func()) {
wg.Wait()
}
// GenAddressByPubKey method
func GenAddressByPubKey(pubkey crypto.PubKey) []byte {
// GenIDByPubKey method
func GenIDByPubKey(pubkey crypto.PubKey) ID {
//must add 3 bytes ahead to make compatibly
typeAddr := append([]byte{byte(0x01), byte(0x01), byte(0x20)}, pubkey.Bytes()...)
return crypto.Ripemd160(typeAddr)
typeAddr := append([]byte{byte(0x01), byte(0x01), byte(0x20)}, pubkey.Bytes()[:32]...)
address := crypto.Ripemd160(typeAddr)
return ID(hex.EncodeToString(address))
}
// IP2IPPort struct
......@@ -129,6 +131,7 @@ type Node struct {
state *ConsensusState
broadcastChannel chan MsgInfo
unicastChannel chan MsgInfo
started uint32 // atomic
stopped uint32 // atomic
quit chan struct{}
......@@ -136,8 +139,6 @@ type Node struct {
// NewNode method
func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivKey, network string, version string, state *ConsensusState) *Node {
address := GenAddressByPubKey(privKey.PubKey())
node := &Node{
peerSet: NewPeerSet(),
seeds: seeds,
......@@ -148,16 +149,18 @@ func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivK
privKey: privKey,
Network: network,
Version: version,
ID: ID(hex.EncodeToString(address)),
ID: GenIDByPubKey(privKey.PubKey()),
dialing: NewMutexMap(),
reconnecting: NewMutexMap(),
broadcastChannel: make(chan MsgInfo, maxSendQueueSize),
unicastChannel: make(chan MsgInfo, minSendQueueSize),
state: state,
localIPs: make(map[string]net.IP),
}
state.SetOurID(node.ID)
state.SetBroadcastChannel(node.broadcastChannel)
state.SetUnicastChannel(node.unicastChannel)
localIPs := getNaiveExternalAddress(true)
if len(localIPs) > 0 {
......@@ -179,7 +182,7 @@ func (node *Node) Start() {
if err == nil {
break
} else if i < tryListenSeconds-1 {
time.Sleep(time.Second * 1)
time.Sleep(time.Second)
}
}
if err != nil {
......@@ -213,6 +216,7 @@ func (node *Node) Start() {
go node.StartConsensusRoutine()
go node.BroadcastRoutine()
go node.UnicastRoutine()
}
}
......@@ -305,13 +309,33 @@ func (node *Node) BroadcastRoutine() {
for {
msg, ok := <-node.broadcastChannel
if !ok {
tendermintlog.Debug("broadcastChannel closed")
tendermintlog.Info("broadcastChannel closed")
return
}
node.Broadcast(msg)
}
}
// BroadcastRoutine receive to broadcast
func (node *Node) UnicastRoutine() {
for {
msg, ok := <-node.unicastChannel
if !ok {
tendermintlog.Info("unicastChannel closed")
return
}
for _, peer := range node.peerSet.List() {
if peer.ID() == msg.PeerID {
success := peer.Send(msg)
if !success {
tendermintlog.Error("send failure in UnicastRoutine")
}
break
}
}
}
}
func (node *Node) connectComming(inConn net.Conn) {
maxPeers := maxNumPeers
if maxPeers <= node.peerSet.Size() {
......@@ -640,7 +664,7 @@ func dial(addr string) (net.Conn, error) {
func newOutboundPeerConn(addr string, ourNodePrivKey crypto.PrivKey, onPeerError func(Peer, interface{}), state *ConsensusState) (*peerConn, error) {
conn, err := dial(addr)
if err != nil {
return &peerConn{}, fmt.Errorf("Error creating peer:%v", err)
return &peerConn{}, fmt.Errorf("newOutboundPeerConn dial fail:%v", err)
}
pc, err := newPeerConn(conn, true, true, ourNodePrivKey, onPeerError, state)
......@@ -684,7 +708,7 @@ func newPeerConn(
// Encrypt connection
conn, err = MakeSecretConnection(conn, ourNodePrivKey)
if err != nil {
return pc, fmt.Errorf("Error creating peer:%v", err)
return pc, fmt.Errorf("MakeSecretConnection fail:%v", err)
}
// Only the information we already have
......
......@@ -54,17 +54,19 @@ func TestParallel(t *testing.T) {
assert.Equal(t, 6, sum)
}
func TestGenAddressByPubKey(t *testing.T) {
func TestGenIDByPubKey(t *testing.T) {
tmp, err := hex.DecodeString(privKey)
assert.Nil(t, err)
priv, err := secureConnCrypto.PrivKeyFromBytes(tmp)
assert.Nil(t, err)
addr := GenAddressByPubKey(priv.PubKey())
id := GenIDByPubKey(priv.PubKey())
addr, err := hex.DecodeString(string(id))
assert.Nil(t, err)
strAddr := fmt.Sprintf("%X", addr)
assert.Equal(t, expectAddress, strAddr)
fmt.Println("TestGenAddressByPubKey ok")
fmt.Println("TestGenIDByPubKey ok")
}
func TestIP2IPPort(t *testing.T) {
......@@ -154,7 +156,6 @@ func testUpdateStateRoutine(t *testing.T, pc *peerConn) {
},
}
ps := pc.state
pc.waitQuit.Add(1)
go pc.updateStateRoutine()
//NewRoundStepID msg
......@@ -249,7 +250,6 @@ func testUpdateStateRoutine(t *testing.T, pc *peerConn) {
assert.NotNil(t, ps.getVoteBitArray(3, 2, ttypes.VoteTypePrecommit))
pc.quitUpdate <- struct{}{}
pc.waitQuit.Wait()
fmt.Println("testUpdateStateRoutine ok")
}
......@@ -8,13 +8,11 @@ import (
"bufio"
"bytes"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net"
"reflect"
"runtime/debug"
"sync"
"sync/atomic"
"time"
......@@ -56,8 +54,6 @@ type Peer interface {
Stop()
SetTransferChannel(chan MsgInfo)
//Set(string, interface{})
//Get(string) interface{}
}
// PeerConnState struct
......@@ -85,10 +81,8 @@ type peerConn struct {
started uint32 //atomic
stopped uint32 // atomic
quitSend chan struct{}
quitUpdate chan struct{}
quitBeat chan struct{}
waitQuit sync.WaitGroup
transferChannel chan MsgInfo
......@@ -222,8 +216,7 @@ func (pc *peerConn) ID() ID {
if len(pc.id) != 0 {
return pc.id
}
address := GenAddressByPubKey(pc.conn.(*SecretConnection).RemotePubKey())
pc.id = ID(hex.EncodeToString(address))
pc.id = GenIDByPubKey(pc.conn.(*SecretConnection).RemotePubKey())
return pc.id
}
......@@ -265,6 +258,11 @@ func (pc *peerConn) SetTransferChannel(transferChannel chan MsgInfo) {
pc.transferChannel = transferChannel
}
func (pc *peerConn) String() string {
return fmt.Sprintf("PeerConn{outbound:%v persistent:%v ip:%s id:%s started:%v stopped:%v}",
pc.outbound, pc.persistent, pc.ip.String(), pc.id, pc.started, pc.stopped)
}
func (pc *peerConn) CloseConn() {
err := pc.conn.Close() // nolint: errcheck
if err != nil {
......@@ -358,7 +356,7 @@ func (pc *peerConn) Send(msg MsgInfo) bool {
atomic.AddInt32(&pc.sendQueueSize, 1)
return true
case <-time.After(defaultSendTimeout):
tendermintlog.Error("send msg timeout", "peerip", msg.PeerIP, "msg", msg.Msg)
tendermintlog.Error("send msg timeout", "peerip", msg.PeerIP, "msg", msg)
return false
}
}
......@@ -379,9 +377,25 @@ func (pc *peerConn) TrySend(msg MsgInfo) bool {
// PickSendVote picks a vote and sends it to the peer.
// Returns true if vote was sent.
func (pc *peerConn) PickSendVote(votes ttypes.VoteSetReader) bool {
if vote, ok := pc.state.PickVoteToSend(votes); ok {
if useAggSig {
if pc.state.AggPrecommit {
time.Sleep(pc.myState.PeerGossipSleep())
return false
}
aggVote := votes.GetAggVote()
if aggVote != nil {
msg := MsgInfo{TypeID: ttypes.AggVoteID, Msg: aggVote.AggVote, PeerID: pc.id, PeerIP: pc.ip.String()}
tendermintlog.Debug("Sending aggregate vote message", "msg", msg)
if pc.Send(msg) {
pc.state.SetHasAggPrecommit(aggVote)
time.Sleep(pc.myState.PeerGossipSleep())
return true
}
}
return false
} else if vote, ok := pc.state.PickVoteToSend(votes); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
tendermintlog.Debug("Sending vote message", "vote", msg)
tendermintlog.Debug("Sending vote message", "msg", msg)
if pc.Send(msg) {
pc.state.SetHasVote(vote)
return true
......@@ -406,7 +420,6 @@ func (pc *peerConn) Start() error {
pc.pongChannel = make(chan struct{})
pc.sendQueue = make(chan MsgInfo, maxSendQueueSize)
pc.sendBuffer = make([]byte, 0, MaxMsgPacketPayloadSize)
pc.quitSend = make(chan struct{})
pc.quitUpdate = make(chan struct{})
pc.quitBeat = make(chan struct{})
pc.state = &PeerConnState{ip: pc.ip, PeerRoundState: ttypes.PeerRoundState{
......@@ -417,7 +430,6 @@ func (pc *peerConn) Start() error {
}}
pc.updateStateQueue = make(chan MsgInfo, maxSendQueueSize)
pc.heartbeatQueue = make(chan proto.Message, 100)
pc.waitQuit.Add(5) //heartbeatRoutine, updateStateRoutine,gossipDataRoutine,gossipVotesRoutine,queryMaj23Routine
go pc.sendRoutine()
go pc.recvRoutine()
......@@ -434,27 +446,8 @@ func (pc *peerConn) Start() error {
func (pc *peerConn) Stop() {
if atomic.CompareAndSwapUint32(&pc.stopped, 0, 1) {
pc.quitSend <- struct{}{}
pc.quitUpdate <- struct{}{}
pc.quitBeat <- struct{}{}
pc.waitQuit.Wait()
tendermintlog.Info("peerConn stop waitQuit", "peerIP", pc.ip.String())
close(pc.sendQueue)
pc.sendQueue = nil
pc.transferChannel = nil
pc.CloseConn()
tendermintlog.Info("peerConn stop finish", "peerIP", pc.ip.String())
}
}
// Catch panics, usually caused by remote disconnects.
func (pc *peerConn) _recover() {
if r := recover(); r != nil {
stack := debug.Stack()
err := StackError{r, stack}
pc.stopForError(err)
tendermintlog.Info("peerConn close connection", "peerIP", pc.ip.String())
}
}
......@@ -468,12 +461,9 @@ func (pc *peerConn) stopForError(r interface{}) {
}
func (pc *peerConn) sendRoutine() {
defer pc._recover()
FOR_LOOP:
for {
select {
case <-pc.quitSend:
break FOR_LOOP
case msg := <-pc.sendQueue:
bytes, err := proto.Marshal(msg.Msg)
if err != nil {
......@@ -525,7 +515,6 @@ FOR_LOOP:
}
func (pc *peerConn) recvRoutine() {
defer pc._recover()
FOR_LOOP:
for {
//typeID+msgLen+msg
......@@ -563,7 +552,7 @@ FOR_LOOP:
continue
}
if pc.transferChannel != nil && (pkt.TypeID == ttypes.ProposalID || pkt.TypeID == ttypes.VoteID ||
pkt.TypeID == ttypes.ProposalBlockID) {
pkt.TypeID == ttypes.ProposalBlockID || pkt.TypeID == ttypes.AggVoteID) {
pc.transferChannel <- MsgInfo{pkt.TypeID, realMsg.(proto.Message), pc.ID(), pc.ip.String()}
if pkt.TypeID == ttypes.ProposalID {
proposal := realMsg.(*tmtypes.Proposal)
......@@ -577,6 +566,9 @@ FOR_LOOP:
block := &ttypes.TendermintBlock{TendermintBlock: realMsg.(*tmtypes.TendermintBlock)}
tendermintlog.Debug("Receiving proposal block", "block-height", block.Header.Height, "peerip", pc.ip.String())
pc.state.SetHasProposalBlock(block)
} else if pkt.TypeID == ttypes.AggVoteID {
aggVote := &ttypes.AggVote{AggVote: realMsg.(*tmtypes.AggVote)}
tendermintlog.Debug("Receiving aggregate vote", "aggVote-height", aggVote.Height, "peerip", pc.ip.String())
}
} else if pkt.TypeID == ttypes.ProposalHeartbeatID {
pc.heartbeatQueue <- realMsg.(*tmtypes.Heartbeat)
......@@ -591,10 +583,8 @@ FOR_LOOP:
}
}
}
close(pc.pongChannel)
close(pc.heartbeatQueue)
close(pc.updateStateQueue)
pc.quitUpdate <- struct{}{}
pc.quitBeat <- struct{}{}
tendermintlog.Info("peerConn stop recvRoutine", "peerIP", pc.ip.String())
}
......@@ -603,7 +593,6 @@ FOR_LOOP:
for {
select {
case <-pc.quitUpdate:
pc.waitQuit.Done()
break FOR_LOOP
case msg := <-pc.updateStateQueue:
typeID := msg.TypeID
......@@ -662,6 +651,7 @@ FOR_LOOP:
}
}
}
close(pc.updateStateQueue)
tendermintlog.Info("peerConn stop updateStateRoutine", "peerIP", pc.ip.String())
}
......@@ -670,15 +660,17 @@ FOR_LOOP:
for {
select {
case <-pc.quitBeat:
pc.waitQuit.Done()
break FOR_LOOP
case heartbeat := <-pc.heartbeatQueue:
msg := heartbeat.(*tmtypes.Heartbeat)
tendermintlog.Debug("Received proposal heartbeat message",
"height", msg.Height, "round", msg.Round, "sequence", msg.Sequence,
"valIdx", msg.ValidatorIndex, "valAddr", msg.ValidatorAddress)
msg, ok := heartbeat.(*tmtypes.Heartbeat)
if ok {
tendermintlog.Debug("Received proposal heartbeat message",
"height", msg.Height, "round", msg.Round, "sequence", msg.Sequence,
"valIdx", msg.ValidatorIndex, "valAddr", msg.ValidatorAddress)
}
}
}
close(pc.heartbeatQueue)
tendermintlog.Info("peerConn stop heartbeatRoutine", "peerIP", pc.ip.String())
}
......@@ -687,7 +679,6 @@ OUTER_LOOP:
for {
// Manage disconnects from self or peer.
if !pc.IsRunning() {
pc.waitQuit.Done()
tendermintlog.Info("peerConn stop gossipDataRoutine", "peerIP", pc.ip.String())
return
}
......@@ -789,7 +780,6 @@ OUTER_LOOP:
for {
// Manage disconnects from self or peer.
if !pc.IsRunning() {
pc.waitQuit.Done()
tendermintlog.Info("peerConn stop gossipVotesRoutine", "peerIP", pc.ip.String())
return
}
......@@ -806,7 +796,7 @@ OUTER_LOOP:
// If height matches, then send LastCommit, Prevotes, Precommits.
if rs.Height == prs.Height {
if pc.gossipVotesForHeight(rs, &prs.PeerRoundState) {
if !useAggSig && pc.gossipVotesForHeight(rs, &prs.PeerRoundState) {
continue OUTER_LOOP
}
}
......@@ -914,7 +904,6 @@ OUTER_LOOP:
for {
// Manage disconnects from self or peer.
if !pc.IsRunning() {
pc.waitQuit.Done()
tendermintlog.Info("peerConn stop queryMaj23Routine", "peerIP", pc.ip.String())
return
}
......@@ -1003,20 +992,6 @@ OUTER_LOOP:
}
}
// StackError struct
type StackError struct {
Err interface{}
Stack []byte
}
func (se StackError) String() string {
return fmt.Sprintf("Error: %v\nStack: %s", se.Err, se.Stack)
}
func (se StackError) Error() string {
return se.String()
}
// GetRoundState returns an atomic snapshot of the PeerRoundState.
// There's no point in mutating it since it won't change PeerState.
func (ps *PeerConnState) GetRoundState() *ttypes.PeerRoundState {
......@@ -1073,6 +1048,23 @@ func (ps *PeerConnState) SetHasProposalBlock(block *ttypes.TendermintBlock) {
ps.ProposalBlock = true
}
// SetHasAggPrecommit sets the given aggregate precommit as known for the peer.
func (ps *PeerConnState) SetHasAggPrecommit(aggVote *ttypes.AggVote) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != aggVote.Height || ps.Round != int(aggVote.Round) {
return
}
if ps.AggPrecommit {
return
}
tendermintlog.Debug("Peer set aggregate precommit", "peerip", ps.ip.String(),
"peer-state", fmt.Sprintf("%v/%v/%v", ps.Height, ps.Round, ps.Step),
"aggVote(H/R)", fmt.Sprintf("%v/%v", aggVote.Height, aggVote.Round))
ps.AggPrecommit = true
}
// PickVoteToSend picks a vote to send to the peer.
// Returns true if a vote was picked.
// NOTE: `votes` must be the correct Size() for the Height().
......@@ -1266,6 +1258,7 @@ func (ps *PeerConnState) ApplyNewRoundStepMessage(msg *tmtypes.NewRoundStepMsg)
// We'll update the BitArray capacity later.
ps.Prevotes = nil
ps.Precommits = nil
ps.AggPrecommit = false
}
if psHeight == msg.Height && psRound != int(msg.Round) && int(msg.Round) == psCatchupCommitRound {
// Peer caught up to CatchupCommitRound.
......
......@@ -16,12 +16,13 @@ import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"time"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/plugin/plugin/consensus/tendermint/types"
ttypes "github.com/33cn/plugin/plugin/consensus/tendermint/types"
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/nacl/secretbox"
"golang.org/x/crypto/ripemd160"
......@@ -33,7 +34,6 @@ const (
dataMaxSize = 1024
totalFrameSize = dataMaxSize + dataLenSize
sealedFrameSize = totalFrameSize + secretbox.Overhead
authSigMsgSize = (32) + (64)
) // fixed size (length prefixed) byte arrays
// SecretConnection Implements net.Conn
......@@ -62,7 +62,7 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (*
// (see DJB's Curve25519 paper: http://cr.yp.to/ecdh/curve25519-20060209.pdf)
remEphPub, err := shareEphPubKey(conn, locEphPub)
if err != nil {
return nil, err
return nil, fmt.Errorf("shareEphPubKey: %v", err)
}
// Compute common shared secret.
......@@ -96,7 +96,7 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (*
// Share (in secret) each other's pubkey & challenge signature
authSigMsg, err := shareAuthSignature(sc, locPubKey, locSignature)
if err != nil {
return nil, err
return nil, fmt.Errorf("shareAuthSignature: %v", err)
}
remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig
if !remPubKey.VerifyBytes(challenge[:], remSignature) {
......@@ -205,7 +205,7 @@ func genEphKeys() (ephPub, ephPriv *[32]byte) {
var err error
ephPub, ephPriv, err = box.GenerateKey(crand.Reader)
if err != nil {
types.PanicCrisis("Could not generate ephemeral keypairs")
ttypes.PanicCrisis("Could not generate ephemeral keypairs")
}
return
}
......@@ -282,26 +282,28 @@ type authSigMessage struct {
func shareAuthSignature(sc io.ReadWriter, pubKey crypto.PubKey, signature crypto.Signature) (*authSigMessage, error) {
var recvMsg authSigMessage
var err1, err2 error
pubLen := len(pubKey.Bytes())
sigLen := len(signature.Bytes())
Parallel(
func() {
msgByte := make([]byte, len(pubKey.Bytes())+len(signature.Bytes()))
copy(msgByte, pubKey.Bytes())
copy(msgByte[len(pubKey.Bytes()):], signature.Bytes())
msgByte := make([]byte, pubLen+sigLen)
copy(msgByte, pubKey.Bytes()[:pubLen])
copy(msgByte[pubLen:], signature.Bytes())
_, err1 = sc.Write(msgByte)
},
func() {
readBuffer := make([]byte, authSigMsgSize)
readBuffer := make([]byte, pubLen+sigLen)
_, err2 = io.ReadFull(sc, readBuffer)
if err2 != nil {
return
}
recvMsg.Key, err2 = types.ConsensusCrypto.PubKeyFromBytes(readBuffer[:32])
recvMsg.Key, err2 = ttypes.ConsensusCrypto.PubKeyFromBytes(readBuffer[:pubLen])
if err2 != nil {
return
}
recvMsg.Sig, err2 = types.ConsensusCrypto.SignatureFromBytes(readBuffer[32:])
recvMsg.Sig, err2 = ttypes.ConsensusCrypto.SignatureFromBytes(readBuffer[pubLen:])
if err2 != nil {
return
}
......
......@@ -385,7 +385,7 @@ func getprivkey(key string) crypto.PrivKey {
if err != nil {
panic(err)
}
priv, err := cr.PrivKeyFromBytes(bkey)
priv, err := cr.PrivKeyFromBytes(bkey[:32])
if err != nil {
panic(err)
}
......
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tendermint
import (
......
......@@ -24,11 +24,14 @@ import (
"github.com/golang/protobuf/proto"
)
const tendermintVersion = "0.1.0"
const (
tendermintVersion = "0.1.0"
)
var (
tendermintlog = log15.New("module", "tendermint")
genesis string
genesisAmount int64 = 1e8
genesisBlockTime int64
timeoutTxAvail int32 = 1000
timeoutPropose int32 = 3000 // millisecond
......@@ -41,12 +44,15 @@ var (
skipTimeoutCommit = false
createEmptyBlocks = false
fastSync = false
preExec = false
createEmptyBlocksInterval int32 // second
validatorNodes = []string{"127.0.0.1:46656"}
peerGossipSleepDuration int32 = 100
peerGossipSleepDuration int32 = 200
peerQueryMaj23SleepDuration int32 = 2000
zeroHash [32]byte
random *rand.Rand
signName = "ed25519"
useAggSig = false
)
func init() {
......@@ -65,7 +71,6 @@ type Client struct {
pubKey string
csState *ConsensusState
csStore *ConsensusStore // save consensus state
crypto crypto.Crypto
node *Node
txsAvailable chan int64
stopC chan struct{}
......@@ -73,6 +78,7 @@ type Client struct {
type subConfig struct {
Genesis string `json:"genesis"`
GenesisAmount int64 `json:"genesisAmount"`
GenesisBlockTime int64 `json:"genesisBlockTime"`
TimeoutTxAvail int32 `json:"timeoutTxAvail"`
TimeoutPropose int32 `json:"timeoutPropose"`
......@@ -87,9 +93,12 @@ type subConfig struct {
CreateEmptyBlocksInterval int32 `json:"createEmptyBlocksInterval"`
ValidatorNodes []string `json:"validatorNodes"`
FastSync bool `json:"fastSync"`
PreExec bool `json:"preExec"`
SignName string `json:"signName"`
UseAggregateSignature bool `json:"useAggregateSignature"`
}
func (client *Client) applyConfig(sub []byte) {
func applyConfig(sub []byte) {
var subcfg subConfig
if sub != nil {
types.MustDecode(sub, &subcfg)
......@@ -97,6 +106,9 @@ func (client *Client) applyConfig(sub []byte) {
if subcfg.Genesis != "" {
genesis = subcfg.Genesis
}
if subcfg.GenesisAmount > 0 {
genesisAmount = subcfg.GenesisAmount
}
if subcfg.GenesisBlockTime > 0 {
genesisBlockTime = subcfg.GenesisBlockTime
}
......@@ -133,6 +145,11 @@ func (client *Client) applyConfig(sub []byte) {
validatorNodes = subcfg.ValidatorNodes
}
fastSync = subcfg.FastSync
preExec = subcfg.PreExec
if subcfg.SignName != "" {
signName = subcfg.SignName
}
useAggSig = subcfg.UseAggregateSignature
}
// DefaultDBProvider returns a database using the DBBackend and DBDir
......@@ -144,37 +161,47 @@ func DefaultDBProvider(name string) dbm.DB {
// New ...
func New(cfg *types.Consensus, sub []byte) queue.Module {
tendermintlog.Info("Start to create tendermint client")
applyConfig(sub)
//init rand
ttypes.Init()
genDoc, err := ttypes.GenesisDocFromFile("genesis.json")
if err != nil {
tendermintlog.Error("NewTendermintClient", "msg", "GenesisDocFromFile failded", "error", err)
signType, ok := ttypes.SignMap[signName]
if !ok {
tendermintlog.Error("Invalid sign name")
return nil
}
cr, err := crypto.New(types.GetSignName("", types.ED25519))
ttypes.CryptoName = types.GetSignName("", signType)
cr, err := crypto.New(ttypes.CryptoName)
if err != nil {
tendermintlog.Error("NewTendermintClient", "err", err)
return nil
}
ttypes.ConsensusCrypto = cr
priv, err := cr.GenKey()
if useAggSig {
_, err = crypto.ToAggregate(ttypes.ConsensusCrypto)
if err != nil {
tendermintlog.Error("ConsensusCrypto not support aggregate signature", "name", ttypes.CryptoName)
return nil
}
}
genDoc, err := ttypes.GenesisDocFromFile("genesis.json")
if err != nil {
tendermintlog.Error("NewTendermintClient", "GenKey err", err)
tendermintlog.Error("NewTendermintClient", "msg", "GenesisDocFromFile fail", "error", err)
return nil
}
privValidator := ttypes.LoadOrGenPrivValidatorFS("priv_validator.json")
if privValidator == nil {
tendermintlog.Error("NewTendermintClient create priv_validator file failed")
tendermintlog.Error("NewTendermintClient create priv_validator file fail")
return nil
}
ttypes.InitMessageMap()
priv := privValidator.PrivKey
pubkey := privValidator.GetPubKey().KeyString()
c := drivers.NewBaseClient(cfg)
client := &Client{
......@@ -184,21 +211,13 @@ func New(cfg *types.Consensus, sub []byte) queue.Module {
privKey: priv,
pubKey: pubkey,
csStore: NewConsensusStore(),
crypto: cr,
txsAvailable: make(chan int64, 1),
stopC: make(chan struct{}, 1),
}
c.SetChild(client)
client.applyConfig(sub)
return client
}
// PrivValidator returns the Node's PrivValidator.
func (client *Client) PrivValidator() ttypes.PrivValidator {
return client.privValidator
}
// GenesisDoc returns the Node's GenesisDoc.
func (client *Client) GenesisDoc() *ttypes.GenesisDoc {
return client.genesisDoc
......@@ -216,6 +235,7 @@ func (client *Client) GenesisState() *State {
// Close TODO:may need optimize
func (client *Client) Close() {
client.BaseClient.Close()
client.node.Stop()
client.stopC <- struct{}{}
tendermintlog.Info("consensus tendermint closed")
......@@ -335,7 +355,7 @@ func (client *Client) CreateGenesisTx() (ret []*types.Transaction) {
//gen payload
g := &cty.CoinsAction_Genesis{}
g.Genesis = &types.AssetsGenesis{}
g.Genesis.Amount = 1e8 * types.Coin
g.Genesis.Amount = genesisAmount * types.Coin
tx.Payload = types.Encode(&cty.CoinsAction{Value: g, Ty: cty.CoinsActionGenesis})
ret = append(ret, &tx)
return
......@@ -405,10 +425,13 @@ func (client *Client) ProcEvent(msg *queue.Message) bool {
// CreateBlock a routine monitor whether some transactions available and tell client by available channel
func (client *Client) CreateBlock() {
issleep := true
for {
if client.IsClosed() {
tendermintlog.Info("CreateBlock quit")
break
}
if !client.csState.IsRunning() {
tendermintlog.Error("consensus not running now")
tendermintlog.Info("consensus not running")
time.Sleep(time.Second)
continue
}
......@@ -483,7 +506,6 @@ func (client *Client) BuildBlock() *types.Block {
client.AddTxsToBlock(&newblock, txs)
//固定难度
newblock.Difficulty = cfg.GetP(0).PowLimitBits
//newblock.TxHash = merkle.CalcMerkleRoot(newblock.Txs)
newblock.BlockTime = types.Now().Unix()
if lastBlock.BlockTime >= newblock.BlockTime {
newblock.BlockTime = lastBlock.BlockTime + 1
......@@ -498,6 +520,9 @@ func (client *Client) CommitBlock(block *types.Block) error {
if retErr != nil {
tendermintlog.Info("CommitBlock fail", "err", retErr)
if client.WaitBlock(block.Height) {
if !preExec {
return nil
}
curBlock, err := client.RequestBlock(block.Height)
if err == nil {
if bytes.Equal(curBlock.Hash(cfg), block.Hash(cfg)) {
......@@ -636,7 +661,7 @@ func (client *Client) Query_NodeInfo(req *types.ReqNil) (types.Message, error) {
return &tmtypes.ValidatorSet{Validators: validators, Proposer: &tmtypes.Validator{}}, nil
}
//比较newBlock是不是最优区块
// CmpBestBlock 比较newBlock是不是最优区块
func (client *Client) CmpBestBlock(newBlock *types.Block, cmpBlock *types.Block) bool {
return false
}
......@@ -272,13 +272,12 @@ func CheckState(t *testing.T, client *Client) {
assert.Equal(t, client.csState.Prevote(0), 1000*time.Millisecond)
assert.Equal(t, client.csState.Precommit(0), 1000*time.Millisecond)
assert.Equal(t, client.csState.PeerGossipSleep(), 100*time.Millisecond)
assert.Equal(t, client.csState.PeerGossipSleep(), 200*time.Millisecond)
assert.Equal(t, client.csState.PeerQueryMaj23Sleep(), 2000*time.Millisecond)
assert.Equal(t, client.csState.IsProposer(), true)
assert.Nil(t, client.csState.GetPrevotesState(state.LastBlockHeight, 0, nil))
assert.Nil(t, client.csState.GetPrecommitsState(state.LastBlockHeight, 0, nil))
assert.NotEmpty(t, client.PrivValidator())
assert.NotEmpty(t, client.csState.GetPrivValidator())
assert.Len(t, client.GenesisDoc().Validators, 1)
msg1, err := client.Query_IsHealthy(&types.ReqNil{})
......
......@@ -139,7 +139,7 @@ func Put(ip string, size string, privkey string) {
fmt.Fprintln(os.Stderr, err)
return
}
url := "http://" + ip + ":9801"
url := "http://" + ip + ":8801"
if privkey == "" {
_, priv := genaddress()
privkey = common.ToHex(priv.Bytes())
......
......@@ -13,18 +13,11 @@ import (
"time"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/common/merkle"
"github.com/33cn/chain33/types"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
)
var (
blocklog = log15.New("module", "tendermint-block")
// ConsensusCrypto define
ConsensusCrypto crypto.Crypto
)
// BlockID struct
type BlockID struct {
tmtypes.BlockID
......@@ -88,11 +81,6 @@ func (b *TendermintBlock) ValidateBasic() error {
return errors.New("Zero Header.Height")
}
newTxs := int64(len(b.Data.Txs))
if b.Header.NumTxs != newTxs {
return fmt.Errorf("Wrong Header.NumTxs. Expected %v, got %v", newTxs, b.Header.NumTxs)
}
if b.Header.TotalTxs < 0 {
return errors.New("Negative Header.TotalTxs")
}
......@@ -192,7 +180,7 @@ func (h *Header) Hash() []byte {
}
bytes, err := json.Marshal(h)
if err != nil {
blocklog.Error("block header Hash() marshal failed", "error", err)
ttlog.Error("block header Hash() marshal failed", "error", err)
return nil
}
return crypto.Ripemd160(bytes)
......@@ -254,6 +242,9 @@ func (commit *Commit) FirstPrecommit() *tmtypes.Vote {
// Height returns the height of the commit
func (commit *Commit) Height() int64 {
if commit.AggVote != nil {
return commit.AggVote.Height
}
if len(commit.Precommits) == 0 {
return 0
}
......@@ -262,6 +253,9 @@ func (commit *Commit) Height() int64 {
// Round returns the round of the commit
func (commit *Commit) Round() int {
if commit.AggVote != nil {
return int(commit.AggVote.Round)
}
if len(commit.Precommits) == 0 {
return 0
}
......@@ -283,6 +277,10 @@ func (commit *Commit) Size() int {
// BitArray returns a BitArray of which validators voted in this commit
func (commit *Commit) BitArray() *BitArray {
if commit.AggVote != nil {
bitArray := &BitArray{TendermintBitArray: commit.AggVote.ValidatorArray}
return bitArray.copy()
}
if commit.bitArray == nil {
commit.bitArray = NewBitArray(len(commit.Precommits))
for i, precommit := range commit.Precommits {
......@@ -301,7 +299,16 @@ func (commit *Commit) GetByIndex(index int) *Vote {
// IsCommit returns true if there is at least one vote
func (commit *Commit) IsCommit() bool {
return len(commit.Precommits) != 0
return len(commit.Precommits) != 0 || commit.AggVote != nil
}
// GetAggVote ...
func (commit *Commit) GetAggVote() *AggVote {
if commit == nil {
return nil
}
aggVote := &AggVote{commit.AggVote}
return aggVote.Copy()
}
// ValidateBasic performs basic validation that doesn't involve state data.
......@@ -338,18 +345,35 @@ func (commit *Commit) ValidateBasic() error {
round, precommit.Round)
}
}
// validate the aggVote
if commit.AggVote != nil {
if commit.AggVote.Type != uint32(VoteTypePrecommit) {
return fmt.Errorf("Invalid aggVote type. Expected Precommit, got %v", commit.AggVote.Type)
}
if commit.AggVote.Height != height {
return fmt.Errorf("Invalid aggVote height. Expected %v, got %v", height, commit.AggVote.Height)
}
if int(commit.AggVote.Round) != round {
return fmt.Errorf("Invalid aggVote round. Expected %v, got %v", round, commit.AggVote.Round)
}
}
return nil
}
// Hash returns the hash of the commit
func (commit *Commit) Hash() []byte {
if commit.hash == nil {
bs := make([][]byte, len(commit.Precommits))
for i, item := range commit.Precommits {
precommit := Vote{Vote: item}
bs[i] = precommit.Hash()
if commit.AggVote != nil {
aggVote := &AggVote{AggVote: commit.AggVote}
commit.hash = aggVote.Hash()
} else {
bs := make([][]byte, len(commit.Precommits))
for i, item := range commit.Precommits {
precommit := Vote{Vote: item}
bs[i] = precommit.Hash()
}
commit.hash = merkle.GetMerkleRoot(bs)
}
commit.hash = merkle.GetMerkleRoot(bs)
}
return commit.hash
}
......@@ -366,9 +390,11 @@ func (commit *Commit) StringIndented(indent string) string {
return Fmt(`Commit{
%s BlockID: %v
%s Precommits: %v
%s AggVote: %v
%s}#%v`,
indent, commit.BlockID,
indent, strings.Join(precommitStrings, "\n"+indent+" "),
indent, commit.AggVote.String(),
indent, commit.hash)
}
......
......@@ -4,7 +4,17 @@
package types
import "errors"
import (
"errors"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
)
const (
AuthBLS = 259
)
var (
// ErrHeightLessThanOne error type
......@@ -18,3 +28,17 @@ var (
// ErrLastBlockID error type
ErrLastBlockID = errors.New("ErrLastBlockID")
)
var (
ttlog = log15.New("module", "tendermint-types")
// ConsensusCrypto define
ConsensusCrypto crypto.Crypto
CryptoName string
// SignMap define sign type
SignMap = map[string]int{
"secp256k1": types.SECP256K1,
"ed25519": types.ED25519,
"sm2": types.SM2,
"bls": AuthBLS,
}
)
......@@ -138,6 +138,30 @@ func (hvs *HeightVoteSet) AddVote(vote *Vote, peerID string) (added bool, err er
return
}
// AddAggVote Duplicate votes return added=false, err=nil.
// By convention, peerKey is "" if origin is self.
func (hvs *HeightVoteSet) AddAggVote(vote *AggVote, peerID string) (added bool, err error) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if !IsVoteTypeValid(byte(vote.Type)) {
return
}
round := int(vote.Round)
voteSet := hvs.getVoteSet(round, byte(vote.Type))
if voteSet == nil {
if rndz := hvs.peerCatchupRounds[peerID]; len(rndz) < 2 {
hvs.addRound(int(vote.Round))
voteSet = hvs.getVoteSet(round, byte(vote.Type))
hvs.peerCatchupRounds[peerID] = append(rndz, round)
} else {
err = errors.New("Peer has sent a aggregate vote that does not match our round for more than one round")
return
}
}
added, err = voteSet.AddAggVote(vote)
return
}
// Prevotes ...
func (hvs *HeightVoteSet) Prevotes(round int) *VoteSet {
hvs.mtx.Lock()
......
......@@ -113,7 +113,7 @@ func (params *ConsensusParams) Validate() error {
func (params *ConsensusParams) Hash() []byte {
bytes, err := json.Marshal(params)
if err != nil {
blocklog.Error("block header Hash() marshal failed", "error", err)
ttlog.Error("block header Hash() marshal failed", "error", err)
return nil
}
return crypto.Ripemd160(bytes)
......
......@@ -6,6 +6,7 @@ package types
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
......@@ -14,8 +15,6 @@ import (
"sync"
"time"
"encoding/hex"
"github.com/33cn/chain33/common/crypto"
)
......@@ -296,15 +295,15 @@ func (pv *PrivValidatorImp) save() {
LastStep: pv.LastStep,
LastSignature: nil,
}
privValFS.PrivKey = KeyText{Kind: "ed25519", Data: Fmt("%X", pv.PrivKey.Bytes()[:])}
privValFS.PubKey = KeyText{Kind: "ed25519", Data: pv.PubKey.KeyString()}
privValFS.PrivKey = KeyText{Kind: CryptoName, Data: Fmt("%X", pv.PrivKey.Bytes()[:])}
privValFS.PubKey = KeyText{Kind: CryptoName, Data: pv.PubKey.KeyString()}
if len(pv.LastSignBytes) != 0 {
tmp := Fmt("%X", pv.LastSignBytes[:])
privValFS.LastSignBytes = tmp
}
if pv.LastSignature != nil {
sig := Fmt("%X", pv.LastSignature.Bytes()[:])
privValFS.LastSignature = &KeyText{Kind: "ed25519", Data: sig}
privValFS.LastSignature = &KeyText{Kind: CryptoName, Data: sig}
}
jsonBytes, err := json.Marshal(privValFS)
if err != nil {
......
......@@ -42,6 +42,7 @@ const (
ProposalHeartbeatID = byte(0x08)
ProposalBlockID = byte(0x09)
ValidBlockID = byte(0x0a)
AggVoteID = byte(0x0b)
PacketTypePing = byte(0xff)
PacketTypePong = byte(0xfe)
......@@ -60,6 +61,7 @@ func InitMessageMap() {
ProposalHeartbeatID: reflect.TypeOf(tmtypes.Heartbeat{}),
ProposalBlockID: reflect.TypeOf(tmtypes.TendermintBlock{}),
ValidBlockID: reflect.TypeOf(tmtypes.ValidBlockMsg{}),
AggVoteID: reflect.TypeOf(tmtypes.AggVote{}),
}
}
......@@ -186,6 +188,7 @@ type PeerRoundState struct {
LastCommit *BitArray // All commit precommits of commit for last height.
CatchupCommitRound int // Round that we have commit for. Not necessarily unique. -1 if none.
CatchupCommit *BitArray // All commit precommits peer has for this height & CatchupCommitRound
AggPrecommit bool // True if peer has aggregate precommit for this round
}
// String returns a string representation of the PeerRoundState
......@@ -204,17 +207,19 @@ func (prs PeerRoundState) StringIndented(indent string) string {
%s Prevotes %v
%s Precommits %v
%s LastCommit %v (round %v)
%s Catchup %v (round %v)
%s CatchupCommit %v (round %v)
%s AggPrecommit %v
%s}`,
indent, prs.Height, prs.Round, prs.Step, prs.StartTime,
indent, prs.Proposal,
indent, prs.ProposalBlock,
indent, prs.ProposalBlock,
indent, prs.ProposalBlockHash,
indent, prs.ProposalPOL, prs.ProposalPOLRound,
indent, prs.Prevotes,
indent, prs.Precommits,
indent, prs.LastCommit, prs.LastCommitRound,
indent, prs.CatchupCommit, prs.CatchupCommitRound,
indent, prs.AggPrecommit,
indent)
}
......@@ -274,6 +279,12 @@ type CanonicalJSONOnceVote struct {
Vote CanonicalJSONVote `json:"vote"`
}
// CanonicalJSONOnceAggVote ...
type CanonicalJSONOnceAggVote struct {
ChainID string `json:"chain_id"`
AggVote CanonicalJSONVote `json:"agg_vote"`
}
// CanonicalJSONOnceHeartbeat ...
type CanonicalJSONOnceHeartbeat struct {
ChainID string `json:"chain_id"`
......@@ -305,11 +316,26 @@ func CanonicalProposal(proposal *Proposal) CanonicalJSONProposal {
// CanonicalVote ...
func CanonicalVote(vote *Vote) CanonicalJSONVote {
timestamp := ""
if !vote.UseAggSig {
timestamp = CanonicalTime(time.Unix(0, vote.Timestamp))
}
return CanonicalJSONVote{
BlockID: CanonicalJSONBlockID{Hash: vote.BlockID.Hash},
Height: vote.Height,
Round: int(vote.Round),
Timestamp: timestamp,
Type: byte(vote.Type),
}
}
// CanonicalAggVote ...
func CanonicalAggVote(vote *AggVote) CanonicalJSONVote {
return CanonicalJSONVote{
BlockID: CanonicalJSONBlockID{Hash: vote.BlockID.Hash},
Height: vote.Height,
Round: int(vote.Round),
Timestamp: CanonicalTime(time.Unix(0, vote.Timestamp)),
Timestamp: "",
Type: byte(vote.Type),
}
}
......
......@@ -13,7 +13,6 @@ import (
"time"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/log/log15"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
)
......@@ -27,7 +26,7 @@ var (
ErrVoteNonDeterministicSignature = errors.New("Non-deterministic signature")
ErrVoteConflict = errors.New("Conflicting vote")
ErrVoteNil = errors.New("Nil vote")
votelog = log15.New("module", "tendermint-vote")
ErrAggVoteNil = errors.New("Nil aggregate vote")
)
// Signable is an interface for all signable things.
......@@ -156,7 +155,7 @@ func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int, err *error
byteVote, e := json.Marshal(&canonical)
if e != nil {
*err = e
votelog.Error("vote WriteSignBytes marshal failed", "err", e)
ttlog.Error("vote WriteSignBytes marshal failed", "err", e)
return
}
number, writeErr := w.Write(byteVote)
......@@ -200,7 +199,7 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
sig, err := ConsensusCrypto.SignatureFromBytes(vote.Signature)
if err != nil {
votelog.Error("vote Verify failed", "err", err)
ttlog.Error("vote Verify fail", "err", err)
return err
}
......@@ -217,7 +216,109 @@ func (vote *Vote) Hash() []byte {
}
bytes, err := json.Marshal(vote)
if err != nil {
votelog.Error("vote hash marshal failed", "err", err)
ttlog.Error("vote hash marshal failed", "err", err)
return nil
}
return crypto.Ripemd160(bytes)
}
// AggVote Represents a prevote, precommit, or commit vote from validators for consensus.
type AggVote struct {
*tmtypes.AggVote
}
// WriteSignBytes ...
func (aggVote *AggVote) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
if *err != nil {
return
}
canonical := CanonicalJSONOnceAggVote{
chainID,
CanonicalAggVote(aggVote),
}
byteVote, e := json.Marshal(&canonical)
if e != nil {
*err = e
ttlog.Error("aggVote WriteSignBytes marshal failed", "err", e)
return
}
number, writeErr := w.Write(byteVote)
*n = number
*err = writeErr
}
// Verify ...
func (aggVote *AggVote) Verify(chainID string, valSet *ValidatorSet) error {
aggSig, err := ConsensusCrypto.SignatureFromBytes(aggVote.Signature)
if err != nil {
return errors.New("invalid aggregate signature")
}
pubs := make([]crypto.PubKey, 0)
arr := &BitArray{TendermintBitArray: aggVote.ValidatorArray}
for i, val := range valSet.Validators {
if arr.GetIndex(i) {
pub, _ := ConsensusCrypto.PubKeyFromBytes(val.PubKey)
pubs = append(pubs, pub)
}
}
origVote := &Vote{&tmtypes.Vote{
BlockID: aggVote.BlockID,
Height: aggVote.Height,
Round: aggVote.Round,
Timestamp: aggVote.Timestamp,
Type: aggVote.Type,
UseAggSig: true,
}}
aggr, err := crypto.ToAggregate(ConsensusCrypto)
if err != nil {
return err
}
err = aggr.VerifyAggregatedOne(pubs, SignBytes(chainID, origVote), aggSig)
if err != nil {
ttlog.Error("aggVote Verify fail", "err", err, "aggVote", aggVote, "aggSig", aggSig)
return err
}
return nil
}
// Copy ...
func (aggVote *AggVote) Copy() *AggVote {
copy := *aggVote
return &copy
}
func (aggVote *AggVote) String() string {
if aggVote == nil {
return "nil-AggVote"
}
var typeString string
switch byte(aggVote.Type) {
case VoteTypePrevote:
typeString = "Prevote"
case VoteTypePrecommit:
typeString = "Precommit"
default:
PanicSanity("Unknown vote type")
}
bitArray := &BitArray{TendermintBitArray: aggVote.ValidatorArray}
return fmt.Sprintf("AggVote{%X %v/%02d/%v(%v) %X %X @ %s %v}",
Fingerprint(aggVote.ValidatorAddress),
aggVote.Height, aggVote.Round, aggVote.Type, typeString,
Fingerprint(aggVote.BlockID.Hash), aggVote.Signature,
CanonicalTime(time.Unix(0, aggVote.Timestamp)),
bitArray)
}
// Hash ...
func (aggVote *AggVote) Hash() []byte {
if aggVote == nil {
return nil
}
bytes, err := json.Marshal(aggVote)
if err != nil {
ttlog.Error("aggVote hash marshal failed", "err", err)
return nil
}
......
......@@ -12,13 +12,10 @@ import (
"strings"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/common/merkle"
"github.com/pkg/errors"
)
var validatorsetlog = log15.New("module", "tendermint-val")
// Validator ...
type Validator struct {
Address []byte `json:"address"`
......@@ -313,7 +310,7 @@ func (valSet *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height
if valSet.Size() != len(commit.Precommits) {
return fmt.Errorf("Invalid commit -- wrong set size: %v vs %v", valSet.Size(), len(commit.Precommits))
}
validatorsetlog.Debug("VerifyCommit will get commit height", "height", commit.Height())
ttlog.Debug("VerifyCommit will get commit height", "height", commit.Height())
commitHeight := commit.Height()
if height != commitHeight {
return fmt.Errorf("VerifyCommit 1 Invalid commit -- wrong height: %v vs %v", height, commitHeight)
......@@ -322,41 +319,65 @@ func (valSet *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height
talliedVotingPower := int64(0)
round := commit.Round()
for idx, item := range commit.Precommits {
// may be nil if validator skipped.
if item == nil || len(item.Signature) == 0 {
continue
}
precommit := &Vote{Vote: item}
if precommit.Height != height {
return fmt.Errorf("VerifyCommit 2 Invalid commit -- wrong height: %v vs %v", height, precommit.Height)
}
if int(precommit.Round) != round {
return fmt.Errorf("Invalid commit -- wrong round: %v vs %v", round, precommit.Round)
}
if precommit.Type != uint32(VoteTypePrecommit) {
return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx)
if commit.AggVote != nil {
aggVote := &AggVote{AggVote: commit.AggVote}
// Make sure the step matches
if (aggVote.Height != height) ||
(int(aggVote.Round) != round) ||
(aggVote.Type != uint32(VoteTypePrecommit)) {
return errors.Wrapf(ErrVoteUnexpectedStep, "Got %d/%d/%d, expected %d/%d/%d",
height, round, VoteTypePrecommit,
aggVote.Height, aggVote.Round, aggVote.Type)
}
_, val := valSet.GetByIndex(idx)
// Validate signature
precommitSignBytes := SignBytes(chainID, precommit)
sig, err := ConsensusCrypto.SignatureFromBytes(precommit.Signature)
if err != nil {
return fmt.Errorf("VerifyCommit SignatureFromBytes [%X] failed:%v", precommit.Signature, err)
}
pubkey, err := ConsensusCrypto.PubKeyFromBytes(val.PubKey)
// Check signature
err := aggVote.Verify(chainID, valSet)
if err != nil {
return fmt.Errorf("VerifyCommit PubKeyFromBytes [%X] failed:%v", val.PubKey, err)
return err
}
if !pubkey.VerifyBytes(precommitSignBytes, sig) {
return fmt.Errorf("Invalid commit -- invalid signature: %v", precommit)
// calc voting power
arr := &BitArray{TendermintBitArray: aggVote.ValidatorArray}
for i, val := range valSet.Validators {
if arr.GetIndex(i) {
talliedVotingPower += val.VotingPower
}
}
if !bytes.Equal(blockID.Hash, precommit.BlockID.Hash) {
continue // Not an error, but doesn't count
} else {
for idx, item := range commit.Precommits {
// may be nil if validator skipped.
if item == nil || len(item.Signature) == 0 {
continue
}
precommit := &Vote{Vote: item}
if precommit.Height != height {
return fmt.Errorf("VerifyCommit 2 Invalid commit -- wrong height: %v vs %v", height, precommit.Height)
}
if int(precommit.Round) != round {
return fmt.Errorf("Invalid commit -- wrong round: %v vs %v", round, precommit.Round)
}
if precommit.Type != uint32(VoteTypePrecommit) {
return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx)
}
_, val := valSet.GetByIndex(idx)
// Validate signature
precommitSignBytes := SignBytes(chainID, precommit)
sig, err := ConsensusCrypto.SignatureFromBytes(precommit.Signature)
if err != nil {
return fmt.Errorf("VerifyCommit SignatureFromBytes [%X] failed:%v", precommit.Signature, err)
}
pubkey, err := ConsensusCrypto.PubKeyFromBytes(val.PubKey)
if err != nil {
return fmt.Errorf("VerifyCommit PubKeyFromBytes [%X] failed:%v", val.PubKey, err)
}
if !pubkey.VerifyBytes(precommitSignBytes, sig) {
return fmt.Errorf("Invalid commit -- invalid signature: %v", precommit)
}
if !bytes.Equal(blockID.Hash, precommit.BlockID.Hash) {
continue // Not an error, but doesn't count
}
// Good precommit!
talliedVotingPower += val.VotingPower
}
// Good precommit!
talliedVotingPower += val.VotingPower
}
if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
......
......@@ -8,7 +8,9 @@ import (
"bytes"
"strings"
"sync"
"time"
"github.com/33cn/chain33/common/crypto"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/pkg/errors"
)
......@@ -63,6 +65,7 @@ type VoteSet struct {
maj23 *tmtypes.BlockID // First 2/3 majority seen
votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes
peerMaj23s map[string]*tmtypes.BlockID // Maj23 for each peer
aggVote *AggVote // aggregate vote
}
// NewVoteSet Constructs a new VoteSet struct used to accumulate votes for given height/round.
......@@ -82,6 +85,7 @@ func NewVoteSet(chainID string, height int64, round int, voteType byte, valSet *
maj23: nil,
votesByBlock: make(map[string]*blockVotes, valSet.Size()),
peerMaj23s: make(map[string]*tmtypes.BlockID),
aggVote: nil,
}
}
......@@ -133,7 +137,7 @@ func (voteSet *VoteSet) Size() int {
// NOTE: Vote must not be nil
func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) {
if voteSet == nil {
PanicSanity("AddVote() on nil VoteSet")
return false, errors.New("nil vote set")
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
......@@ -291,6 +295,146 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower
return true, conflicting
}
// AddAggVote Returns added=true if aggVote is valid and new
func (voteSet *VoteSet) AddAggVote(vote *AggVote) (bool, error) {
if voteSet == nil {
return false, errors.New("nil vote set")
}
if vote == nil {
return false, ErrAggVoteNil
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
valAddr := vote.ValidatorAddress
valset := voteSet.valSet
if len(valAddr) == 0 {
return false, errors.Wrap(ErrVoteInvalidValidatorAddress, "Empty address")
}
// Make sure the step matches
if (vote.Height != voteSet.height) ||
(int(vote.Round) != voteSet.round) ||
(vote.Type != uint32(voteSet.voteType)) {
return false, errors.Wrapf(ErrVoteUnexpectedStep, "Got %d/%d/%d, expected %d/%d/%d",
voteSet.height, voteSet.round, voteSet.voteType,
vote.Height, vote.Round, vote.Type)
}
// Ensure that signer is proposer
propAddr := valset.Proposer.Address
if !bytes.Equal(valAddr, propAddr) {
return false, errors.Wrapf(ErrVoteInvalidValidatorAddress,
"aggVote.ValidatorAddress (%X) does not match proposer address (%X)",
valAddr, propAddr)
}
// If we already know of this vote, return false
if voteSet.aggVote != nil {
if bytes.Equal(voteSet.aggVote.Signature, vote.Signature) {
return false, nil // duplicate
}
return false, errors.Wrapf(ErrVoteNonDeterministicSignature, "Existing vote: %v; New vote: %v", voteSet.aggVote, vote)
}
// Check signature
err := vote.Verify(voteSet.chainID, voteSet.valSet)
if err != nil {
return false, err
}
// Check maj32
sum := int64(0)
arr := &BitArray{TendermintBitArray: vote.ValidatorArray}
for i, val := range valset.Validators {
if arr.GetIndex(i) {
sum += val.VotingPower
}
}
quorum := voteSet.valSet.TotalVotingPower()*2/3 + 1
if sum < quorum {
return false, errors.New("less than 2/3 total power")
}
voteSet.votesBitArray = arr.copy()
voteSet.aggVote = vote
voteSet.maj23 = vote.BlockID
voteSet.sum = sum
votesByBlock := newBlockVotes(false, voteSet.valSet.Size())
votesByBlock.bitArray = arr.copy()
votesByBlock.sum = sum
voteSet.votesByBlock[string(voteSet.maj23.Hash)] = votesByBlock
return true, nil
}
// SetAggVote generate aggregate vote when voteSet have 2/3 majority
func (voteSet *VoteSet) SetAggVote() error {
if voteSet == nil {
return errors.New("nil vote set")
}
if voteSet.maj23 == nil {
return errors.New("no 2/3 majority in voteSet")
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
blockKey := string(voteSet.maj23.Hash)
votesByBlock, ok := voteSet.votesByBlock[blockKey]
if !ok {
return errors.New("no 2/3 majority blockKey")
}
bitArray := votesByBlock.bitArray.copy()
sigs := make([]crypto.Signature, 0)
for _, vote := range votesByBlock.votes {
if vote != nil {
sig, err := ConsensusCrypto.SignatureFromBytes(vote.Signature)
if err != nil {
return errors.New("invalid aggregate signature")
}
sigs = append(sigs, sig)
}
}
aggr, err := crypto.ToAggregate(ConsensusCrypto)
if err != nil {
return err
}
aggSig, err := aggr.Aggregate(sigs)
if err != nil {
return err
}
aggVote := &AggVote{&tmtypes.AggVote{
ValidatorAddress: voteSet.valSet.Proposer.Address,
ValidatorArray: bitArray.TendermintBitArray,
Height: voteSet.height,
Round: int32(voteSet.round),
Timestamp: time.Now().UnixNano(),
Type: uint32(voteSet.voteType),
BlockID: voteSet.maj23,
Signature: aggSig.Bytes(),
}}
// Verify aggVote
err = aggVote.Verify(voteSet.chainID, voteSet.valSet)
if err != nil {
return err
}
voteSet.aggVote = aggVote
return nil
}
// GetAggVote ...
func (voteSet *VoteSet) GetAggVote() *AggVote {
if voteSet == nil {
return nil
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if voteSet.aggVote == nil {
return nil
}
return voteSet.aggVote.Copy()
}
// SetPeerMaj23 If a peer claims that it has 2/3 majority for given blockKey, call this.
// NOTE: if there are too many peers, or too much peer churn,
// this can cause memory issues.
......@@ -446,12 +590,12 @@ func (voteSet *VoteSet) StringIndented(indent string) string {
}
}
return Fmt(`VoteSet{
%s H:%v R:%v T:%v
%s H:%v R:%v T:%v +2/3:%X
%s %v
%s %v
%s %v
%s}`,
indent, voteSet.height, voteSet.round, voteSet.voteType,
indent, voteSet.height, voteSet.round, voteSet.voteType, voteSet.maj23,
indent, strings.Join(voteStrings, "\n"+indent+" "),
indent, voteSet.votesBitArray,
indent, voteSet.peerMaj23s,
......@@ -491,10 +635,15 @@ func (voteSet *VoteSet) MakeCommit() *tmtypes.TendermintCommit {
votesCopy[i] = &tmtypes.Vote{}
}
}
//copy(votesCopy, voteSet.votes)
var aggVote *tmtypes.AggVote
if voteSet.aggVote != nil {
copy := voteSet.aggVote.Copy()
aggVote = copy.AggVote
}
return &tmtypes.TendermintCommit{
BlockID: voteSet.maj23,
Precommits: votesCopy,
AggVote: aggVote,
}
}
......@@ -549,4 +698,5 @@ type VoteSetReader interface {
BitArray() *BitArray
GetByIndex(int) *Vote
IsCommit() bool
GetAggVote() *AggVote
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bls
import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"github.com/33cn/chain33/common/crypto"
"github.com/phoreproject/bls/g1pubs"
)
const (
BLSPrivateKeyLength = 32
BLSPublicKeyLength = 48
BLSSignatureLength = 96
)
// Driver driver
type Driver struct{}
// GenKey create private key
func (d Driver) GenKey() (crypto.PrivKey, error) {
privKeyBytes := new([BLSPrivateKeyLength]byte)
priv, err := g1pubs.RandKey(rand.Reader)
if err != nil {
return nil, err
}
privBytes := priv.Serialize()
copy(privKeyBytes[:], privBytes[:])
return PrivKeyBLS(*privKeyBytes), nil
}
// PrivKeyFromBytes create private key from bytes
func (d Driver) PrivKeyFromBytes(b []byte) (privKey crypto.PrivKey, err error) {
if len(b) != BLSPrivateKeyLength {
return nil, errors.New("invalid bls priv key byte")
}
privKeyBytes := new([BLSPrivateKeyLength]byte)
copy(privKeyBytes[:], b[:BLSPrivateKeyLength])
priv := g1pubs.DeserializeSecretKey(*privKeyBytes)
if priv == nil {
return nil, errors.New("invalid bls privkey")
}
privBytes := priv.Serialize()
copy(privKeyBytes[:], privBytes[:])
return PrivKeyBLS(*privKeyBytes), nil
}
// PubKeyFromBytes create public key from bytes
func (d Driver) PubKeyFromBytes(b []byte) (pubKey crypto.PubKey, err error) {
if len(b) != BLSPublicKeyLength {
return nil, errors.New("invalid bls pub key byte")
}
pubKeyBytes := new([BLSPublicKeyLength]byte)
copy(pubKeyBytes[:], b[:])
return PubKeyBLS(*pubKeyBytes), nil
}
// SignatureFromBytes create signature from bytes
func (d Driver) SignatureFromBytes(b []byte) (sig crypto.Signature, err error) {
sigBytes := new([BLSSignatureLength]byte)
copy(sigBytes[:], b[:])
return SignatureBLS(*sigBytes), nil
}
//Aggregate aggregates signatures together into a new signature.
func (d Driver) Aggregate(sigs []crypto.Signature) (crypto.Signature, error) {
if len(sigs) == 0 {
return nil, errors.New("no signatures to aggregate")
}
g1sigs := make([]*g1pubs.Signature, 0, len(sigs))
for i, sig := range sigs {
g1sig, err := ConvertToSignature(sig)
if err != nil {
return nil, fmt.Errorf("%v(index: %d)", err, i)
}
g1sigs = append(g1sigs, g1sig)
}
agsig := g1pubs.AggregateSignatures(g1sigs)
return SignatureBLS(agsig.Serialize()), nil
}
//AggregatePublic aggregates public keys together into a new PublicKey.
func (d Driver) AggregatePublic(pubs []crypto.PubKey) (crypto.PubKey, error) {
if len(pubs) == 0 {
return nil, errors.New("no public keys to aggregate")
}
//blank public key
g1pubs := g1pubs.NewAggregatePubkey()
for i, pub := range pubs {
g1pub, err := ConvertToPublicKey(pub)
if err != nil {
return nil, fmt.Errorf("%v(index: %d)", err, i)
}
g1pubs.Aggregate(g1pub)
}
return PubKeyBLS(g1pubs.Serialize()), nil
}
// VerifyAggregatedOne verifies each public key against a message.
func (d Driver) VerifyAggregatedOne(pubs []crypto.PubKey, m []byte, sig crypto.Signature) error {
g1pubs := make([]*g1pubs.PublicKey, 0, len(pubs))
for i, pub := range pubs {
g1pub, err := ConvertToPublicKey(pub)
if err != nil {
return fmt.Errorf("%v(index: %d)", err, i)
}
g1pubs = append(g1pubs, g1pub)
}
g1sig, err := ConvertToSignature(sig)
if err != nil {
return err
}
if g1sig.VerifyAggregateCommon(g1pubs, m) {
return nil
}
return errors.New("bls signature mismatch")
}
// VerifyAggregatedN verifies each public key against each message.
func (d Driver) VerifyAggregatedN(pubs []crypto.PubKey, ms [][]byte, sig crypto.Signature) error {
g1pubs := make([]*g1pubs.PublicKey, 0, len(pubs))
for i, pub := range pubs {
g1pub, err := ConvertToPublicKey(pub)
if err != nil {
return fmt.Errorf("%v(index: %d)", err, i)
}
g1pubs = append(g1pubs, g1pub)
}
g1sig, err := ConvertToSignature(sig)
if err != nil {
return err
}
if len(g1pubs) != len(ms) {
return fmt.Errorf("different length of pubs and messages, %d vs %d", len(g1pubs), len(ms))
}
if g1sig.VerifyAggregate(g1pubs, ms) {
return nil
}
return errors.New("bls signature mismatch")
}
// ConvertToSignature convert to BLS Signature
func ConvertToSignature(sig crypto.Signature) (*g1pubs.Signature, error) {
// unwrap if needed
if wrap, ok := sig.(SignatureS); ok {
sig = wrap.Signature
}
sigBLS, ok := sig.(SignatureBLS)
if !ok {
return nil, errors.New("invalid bls signature")
}
g1sig, err := g1pubs.DeserializeSignature(sigBLS)
if err != nil {
return nil, err
}
return g1sig, nil
}
// ConvertToPublicKey convert to BLS PublicKey
func ConvertToPublicKey(pub crypto.PubKey) (*g1pubs.PublicKey, error) {
pubBLS, ok := pub.(PubKeyBLS)
if !ok {
return nil, errors.New("invalid bls public key")
}
g1pub, err := g1pubs.DeserializePublicKey(pubBLS)
if err != nil {
return nil, err
}
return g1pub, nil
}
// PrivKeyBLS PrivKey
type PrivKeyBLS [BLSPrivateKeyLength]byte
// Bytes convert to bytes
func (privKey PrivKeyBLS) Bytes() []byte {
s := make([]byte, BLSPrivateKeyLength)
copy(s, privKey[:])
return s
}
// Sign create signature
func (privKey PrivKeyBLS) Sign(msg []byte) crypto.Signature {
priv := g1pubs.DeserializeSecretKey(privKey)
sig := g1pubs.Sign(msg, priv)
return SignatureBLS(sig.Serialize())
}
// PubKey convert to public key
func (privKey PrivKeyBLS) PubKey() crypto.PubKey {
priv := g1pubs.DeserializeSecretKey(privKey)
return PubKeyBLS(g1pubs.PrivToPub(priv).Serialize())
}
// Equals check privkey is equal
func (privKey PrivKeyBLS) Equals(other crypto.PrivKey) bool {
if otherSecp, ok := other.(PrivKeyBLS); ok {
return bytes.Equal(privKey[:], otherSecp[:])
}
return false
}
// String convert to string
func (privKey PrivKeyBLS) String() string {
return fmt.Sprintf("PrivKeyBLS{*****}")
}
// PubKeyBLS PubKey
type PubKeyBLS [BLSPublicKeyLength]byte
// Bytes convert to bytes
func (pubKey PubKeyBLS) Bytes() []byte {
s := make([]byte, BLSPublicKeyLength)
copy(s, pubKey[:])
return s
}
// VerifyBytes verify signature
func (pubKey PubKeyBLS) VerifyBytes(msg []byte, sig crypto.Signature) bool {
pub, err := g1pubs.DeserializePublicKey(pubKey)
if err != nil {
fmt.Println("invalid bls pubkey")
return false
}
g1sig, err := ConvertToSignature(sig)
if err != nil {
fmt.Println("ConvertToSignature fail:", err)
return false
}
return g1pubs.Verify(msg, pub, g1sig)
}
// String convert to string
func (pubKey PubKeyBLS) String() string {
return fmt.Sprintf("PubKeyBLS{%X}", pubKey[:])
}
// KeyString Must return the full bytes in hex.
// Used for map keying, etc.
func (pubKey PubKeyBLS) KeyString() string {
return fmt.Sprintf("%X", pubKey[:])
}
// Equals check public key is equal
func (pubKey PubKeyBLS) Equals(other crypto.PubKey) bool {
if otherSecp, ok := other.(PubKeyBLS); ok {
return bytes.Equal(pubKey[:], otherSecp[:])
}
return false
}
// SignatureBLS Signature
type SignatureBLS [BLSSignatureLength]byte
// SignatureS signature struct
type SignatureS struct {
crypto.Signature
}
// Bytes convert signature to bytes
func (sig SignatureBLS) Bytes() []byte {
s := make([]byte, len(sig))
copy(s, sig[:])
return s
}
// IsZero check signature is zero
func (sig SignatureBLS) IsZero() bool { return len(sig) == 0 }
// String convert signature to string
func (sig SignatureBLS) String() string {
fingerprint := make([]byte, len(sig[:]))
copy(fingerprint, sig[:])
return fmt.Sprintf("/%X.../", fingerprint)
}
// Equals check signature equals
func (sig SignatureBLS) Equals(other crypto.Signature) bool {
if otherEd, ok := other.(SignatureBLS); ok {
return bytes.Equal(sig[:], otherEd[:])
}
return false
}
// Name name
const Name = "bls"
// ID id
const ID = 259
func init() {
crypto.Register(Name, &Driver{})
crypto.RegisterType(Name, ID)
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bls
import (
"fmt"
"testing"
"github.com/33cn/chain33/common/crypto"
"github.com/stretchr/testify/assert"
)
var blsDrv = &Driver{}
func TestGenKey(t *testing.T) {
sk, err := blsDrv.GenKey()
assert.NoError(t, err)
assert.NotEmpty(t, sk)
pk := sk.PubKey()
assert.NotEmpty(t, pk)
sk2, _ := blsDrv.GenKey()
assert.NotEqual(t, sk.Bytes(), sk2.Bytes(), "should not generate two same key", sk, sk2)
}
func TestSignAndVerify(t *testing.T) {
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
m1 := []byte("message to be signed. 将要做签名的消息")
// sign and verify
sig1 := sk.Sign(m1)
ret := pk.VerifyBytes(m1, sig1)
assert.Equal(t, true, ret)
// different message should have different signature
m2 := []byte("message to be signed. 将要做签名的消息.")
sig2 := sk.Sign(m2)
assert.NotEqual(t, sig1, sig2, "different message got the same signature", sig1, sig2)
// different key should have different signature for a same message.
sk2, _ := blsDrv.GenKey()
sig12 := sk2.Sign(m1)
ret = pk.VerifyBytes(m1, sig12)
assert.Equal(t, false, ret)
}
func TestAggregate(t *testing.T) {
m := []byte("message to be signed. 将要做签名的消息")
n := 8
pubs := make([]crypto.PubKey, 0, n)
sigs := make([]crypto.Signature, 0, n) //signatures for the same message
msgs := make([][]byte, 0, n)
dsigs := make([]crypto.Signature, 0, n) //signatures for each (key,message) pair
for i := 0; i < n; i++ {
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
pubs = append(pubs, pk)
sigs = append(sigs, sk.Sign(m))
msgi := append(m, byte(i))
msgs = append(msgs, msgi)
dsigs = append(dsigs, sk.Sign(msgi))
}
asig, err := blsDrv.Aggregate(sigs)
assert.NoError(t, err)
// One
err = blsDrv.VerifyAggregatedOne(pubs, m, asig)
assert.NoError(t, err)
apub, err := blsDrv.AggregatePublic(pubs)
assert.NoError(t, err)
ret := apub.VerifyBytes(m, asig)
assert.Equal(t, true, ret)
// N
adsig, err := blsDrv.Aggregate(dsigs)
assert.NoError(t, err)
err = blsDrv.VerifyAggregatedN(pubs, msgs, adsig)
assert.NoError(t, err)
//lose some messages will cause an error
err = blsDrv.VerifyAggregatedN(pubs, msgs[1:], adsig)
assert.Error(t, err)
//with out-of-order public keys, will has no effect on VerifyAggregatedOne, but DO effects VerifyAggregatedN
pubs[0], pubs[1] = pubs[1], pubs[0]
err = blsDrv.VerifyAggregatedOne(pubs, m, asig)
assert.NoError(t, err)
err = blsDrv.VerifyAggregatedN(pubs, msgs, adsig)
assert.Error(t, err)
//invalid length
_, err = blsDrv.Aggregate(nil)
assert.Error(t, err)
_, err = blsDrv.AggregatePublic(make([]crypto.PubKey, 0))
assert.Error(t, err)
}
//benchmark
func BenchmarkBLSAggregateSignature(b *testing.B) {
msg := []byte(">16 character identical message")
n := 200
sigs := make([]crypto.Signature, 0, n) //signatures for the same message
for i := 0; i < n; i++ {
sk, _ := blsDrv.GenKey()
sigs = append(sigs, sk.Sign(msg))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
blsDrv.Aggregate(sigs) //nolint:errcheck
}
}
func BenchmarkBLSSign(b *testing.B) {
sks := make([]crypto.PrivKey, b.N)
msgs := make([][]byte, 0, b.N)
for i := range sks {
sks[i], _ = blsDrv.GenKey()
msgs = append(msgs, []byte(fmt.Sprintf("Hello world! 16 characters %d", i)))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
sks[i].Sign(msgs[i])
}
}
func BenchmarkBLSVerify(b *testing.B) {
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
m := []byte(">16 character identical message")
sig := sk.Sign(m)
b.ResetTimer()
for i := 0; i < b.N; i++ {
pk.VerifyBytes(m, sig) //nolint:errcheck
}
}
func BenchmarkBlsManager_VerifyAggregatedOne(b *testing.B) {
m := []byte("message to be signed. 将要做签名的消息")
n := 100
pubs := make([]crypto.PubKey, 0, n)
sigs := make([]crypto.Signature, 0, n) //signatures for the same message
for i := 0; i < n; i++ {
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
pubs = append(pubs, pk)
sigs = append(sigs, sk.Sign(m))
}
asig, _ := blsDrv.Aggregate(sigs)
b.ResetTimer()
for i := 0; i < b.N; i++ {
blsDrv.VerifyAggregatedOne(pubs, m, asig) //nolint:errcheck
}
}
func BenchmarkBlsManager_VerifyAggregatedN(b *testing.B) {
m := []byte("message to be signed. 将要做签名的消息")
n := 100
pubs := make([]crypto.PubKey, 0, n)
sigs := make([]crypto.Signature, 0, n)
msgs := make([][]byte, 0, n)
for i := 0; i < n; i++ {
mi := append(m, byte(i))
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
pubs = append(pubs, pk)
sigs = append(sigs, sk.Sign(mi))
msgs = append(msgs, mi)
}
asig, _ := blsDrv.Aggregate(sigs)
b.ResetTimer()
for i := 0; i < b.N; i++ {
blsDrv.VerifyAggregatedN(pubs, msgs, asig) //nolint:errcheck
}
}
package init
import (
_ "github.com/33cn/plugin/plugin/crypto/bls" //auto gen
_ "github.com/33cn/plugin/plugin/crypto/ecdsa" //auto gen
_ "github.com/33cn/plugin/plugin/crypto/sm2" //auto gen
)
......@@ -25,6 +25,7 @@ var (
strChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // 62 characters
genFile = "genesis_file.json"
pvFile = "priv_validator_"
AuthBLS = 259
)
// ValCmd valnode cmd register
......@@ -164,8 +165,9 @@ func CreateCmd() *cobra.Command {
}
func addCreateCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("num", "n", "", "Num of the keyfile to create")
cmd.Flags().StringP("num", "n", "", "num of the keyfile to create")
cmd.MarkFlagRequired("num")
cmd.Flags().StringP("type", "t", "ed25519", "sign type of the keyfile (secp256k1, ed25519, sm2, bls)")
}
// RandStr ...
......@@ -192,10 +194,11 @@ MAIN_LOOP:
return string(chars)
}
func initCryptoImpl() error {
cr, err := crypto.New(types.GetSignName("", types.ED25519))
func initCryptoImpl(signType int) error {
ttypes.CryptoName = types.GetSignName("", signType)
cr, err := crypto.New(ttypes.CryptoName)
if err != nil {
fmt.Printf("New crypto impl failed err: %v", err)
fmt.Printf("Init crypto fail: %v", err)
return err
}
ttypes.ConsensusCrypto = cr
......@@ -204,7 +207,13 @@ func initCryptoImpl() error {
func createFiles(cmd *cobra.Command, args []string) {
// init crypto instance
err := initCryptoImpl()
ty, _ := cmd.Flags().GetString("type")
signType, ok := ttypes.SignMap[ty]
if !ok {
fmt.Println("type parameter is not valid")
return
}
err := initCryptoImpl(signType)
if err != nil {
return
}
......@@ -232,7 +241,7 @@ func createFiles(cmd *cobra.Command, args []string) {
// create genesis validator by the pubkey of private validator
gv := ttypes.GenesisValidator{
PubKey: ttypes.KeyText{Kind: "ed25519", Data: privValidator.GetPubKey().KeyString()},
PubKey: ttypes.KeyText{Kind: ttypes.CryptoName, Data: privValidator.GetPubKey().KeyString()},
Power: 10,
}
genDoc.Validators = append(genDoc.Validators, gv)
......
......@@ -35,11 +35,22 @@ func (val *ValNode) Exec_BlockInfo(blockInfo *pty.TendermintBlockInfo, tx *types
return receipt, nil
}
func getConfigKey(key string, db dbm.KV) ([]byte, error) {
configKey := types.ConfigKey(key)
value, err := db.Get([]byte(configKey))
if err != nil {
clog.Error("getConfigKey not find", "configKey", configKey, "err", err)
return nil, err
}
return value, nil
}
func getManageKey(key string, db dbm.KV) ([]byte, error) {
manageKey := types.ManageKey(key)
value, err := db.Get([]byte(manageKey))
if err != nil {
return nil, err
clog.Info("getManageKey not find", "manageKey", manageKey, "err", err)
return getConfigKey(key, db)
}
return value, nil
}
......
......@@ -5,86 +5,88 @@ import "blockchain.proto";
package types;
message BlockID {
bytes Hash = 1;
bytes hash = 1;
}
message TendermintBitArray {
int32 Bits = 1;
repeated uint64 Elems = 2;
int32 bits = 1;
repeated uint64 elems = 2;
}
message Vote {
bytes ValidatorAddress = 1;
int32 ValidatorIndex = 2;
int64 Height = 3;
int32 Round = 4;
int64 Timestamp = 5;
uint32 Type = 6;
BlockID BlockID = 7;
bytes Signature = 8;
bytes validatorAddress = 1;
int32 validatorIndex = 2;
int64 height = 3;
int32 round = 4;
int64 timestamp = 5;
uint32 type = 6;
BlockID blockID = 7;
bytes signature = 8;
bool useAggSig = 9;
}
message TendermintCommit {
BlockID BlockID = 1;
repeated Vote Precommits = 2;
BlockID blockID = 1;
repeated Vote precommits = 2;
AggVote aggVote = 3;
}
message TendermintBlockInfo {
State State = 2;
Proposal Proposal = 3;
State state = 2;
Proposal proposal = 3;
TendermintBlock block = 4;
}
message BlockSize {
int32 MaxBytes = 1;
int32 MaxTxs = 2;
int64 MaxGas = 3;
int32 maxBytes = 1;
int32 maxTxs = 2;
int64 maxGas = 3;
}
message TxSize {
int32 MaxBytes = 1;
int64 MaxGas = 2;
int32 maxBytes = 1;
int64 maxGas = 2;
}
message BlockGossip {
int32 BlockPartSizeBytes = 1;
int32 blockPartSizeBytes = 1;
}
message EvidenceParams {
int64 MaxAge = 1;
int64 maxAge = 1;
}
message ConsensusParams {
BlockSize BlockSize = 1;
TxSize TxSize = 2;
BlockGossip BlockGossip = 3;
EvidenceParams EvidenceParams = 4;
BlockSize blockSize = 1;
TxSize txSize = 2;
BlockGossip blockGossip = 3;
EvidenceParams evidenceParams = 4;
}
message Validator {
bytes Address = 1;
bytes PubKey = 2;
int64 VotingPower = 3;
int64 Accum = 4;
bytes address = 1;
bytes pubKey = 2;
int64 votingPower = 3;
int64 accum = 4;
}
message ValidatorSet {
repeated Validator Validators = 1;
Validator Proposer = 2;
repeated Validator validators = 1;
Validator proposer = 2;
}
message State {
string ChainID = 1;
int64 LastBlockHeight = 2;
int64 LastBlockTotalTx = 3;
BlockID LastBlockID = 4;
int64 LastBlockTime = 5;
ValidatorSet Validators = 6;
ValidatorSet LastValidators = 7;
int64 LastHeightValidatorsChanged = 8;
ConsensusParams ConsensusParams = 9;
int64 LastHeightConsensusParamsChanged = 10;
bytes LastResultsHash = 11;
bytes AppHash = 12;
string chainID = 1;
int64 lastBlockHeight = 2;
int64 lastBlockTotalTx = 3;
BlockID lastBlockID = 4;
int64 lastBlockTime = 5;
ValidatorSet validators = 6;
ValidatorSet lastValidators = 7;
int64 lastHeightValidatorsChanged = 8;
ConsensusParams consensusParams = 9;
int64 lastHeightConsensusParamsChanged = 10;
bytes lastResultsHash = 11;
bytes appHash = 12;
}
message TendermintBlockHeader {
......@@ -173,4 +175,15 @@ message Heartbeat {
message IsHealthy {
bool isHealthy = 1;
}
message AggVote {
bytes validatorAddress = 1;
TendermintBitArray validatorArray = 2;
int64 height = 3;
int32 round = 4;
int64 timestamp = 5;
uint32 type = 6;
BlockID blockID = 7;
bytes signature = 8;
}
\ No newline at end of file
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment