Unverified Commit 7ea03fb2 authored by vipwzw's avatar vipwzw Committed by GitHub

Merge pull request #674 from caopingcp/issue615_tendermint

tendermint save local state
parents a413a97a 152ba70e
......@@ -83,13 +83,13 @@ powLimitBits = "0x1f2fffff"
genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
genesisBlockTime=1514533394
timeoutTxAvail=1000
timeoutPropose=3000
timeoutPropose=1000
timeoutProposeDelta=500
timeoutPrevote=1000
timeoutPrevoteDelta=500
timeoutPrecommit=1000
timeoutPrecommitDelta=500
timeoutCommit=1000
timeoutCommit=500
skipTimeoutCommit=false
createEmptyBlocks=false
createEmptyBlocksInterval=0
......
This diff is collapsed.
......@@ -21,16 +21,14 @@ import (
// BlockExecutor provides the context and accessories for properly executing a block.
type BlockExecutor struct {
// save state, validators, consensus params, abci responses here
db *CSStateDB
evpool ttypes.EvidencePool
db *CSStateDB
}
// NewBlockExecutor returns a new BlockExecutor with a NopEventBus.
// Call SetEventBus to provide one.
func NewBlockExecutor(db *CSStateDB, evpool ttypes.EvidencePool) *BlockExecutor {
func NewBlockExecutor(db *CSStateDB) *BlockExecutor {
return &BlockExecutor{
db: db,
evpool: evpool,
db: db,
}
}
......@@ -59,12 +57,6 @@ func (blockExec *BlockExecutor) ApplyBlock(s State, blockID ttypes.BlockID, bloc
}
blockExec.db.SaveState(s)
// Update evpool now that state is saved
// TODO: handle the crash/recover scenario
// ie. (may need to call Update for last block)
blockExec.evpool.Update(block)
return s, nil
}
......@@ -191,7 +183,10 @@ func changeInVotingPowerMoreOrEqualToOneThird(currentSet *ttypes.ValidatorSet, u
}
func validateBlock(stateDB *CSStateDB, s State, b *ttypes.TendermintBlock) error {
newTxs := b.Header.NumTxs
// Validate internal consistency.
if err := b.ValidateBasic(); err != nil {
return err
}
// validate basic info
if b.Header.ChainID != s.ChainID {
......@@ -206,6 +201,7 @@ func validateBlock(stateDB *CSStateDB, s State, b *ttypes.TendermintBlock) error
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v", s.LastBlockID, b.Header.LastBlockID)
}
newTxs := b.Header.NumTxs
if b.Header.TotalTxs != s.LastBlockTotalTx+newTxs {
return fmt.Errorf("Wrong Block.Header.TotalTxs. Expected %v, got %v", s.LastBlockTotalTx+newTxs, b.Header.TotalTxs)
}
......@@ -242,13 +238,5 @@ func validateBlock(stateDB *CSStateDB, s State, b *ttypes.TendermintBlock) error
}
}
for _, ev := range b.Evidence.Evidence {
evidence := ttypes.EvidenceEnvelope2Evidence(ev)
if evidence != nil {
if err := VerifyEvidence(stateDB, s, evidence); err != nil {
return ttypes.NewEvidenceInvalidErr(evidence, err)
}
}
}
return nil
}
......@@ -17,8 +17,6 @@ import (
"github.com/33cn/chain33/common/crypto"
ttypes "github.com/33cn/plugin/plugin/consensus/tendermint/types"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/golang/protobuf/proto"
)
const (
......@@ -44,8 +42,6 @@ const (
minReadBufferSize = 1024
minWriteBufferSize = 65536
broadcastEvidenceIntervalS = 60 // broadcast uncommitted evidence this often
)
// Parallel method
......@@ -132,7 +128,6 @@ type Node struct {
lAddr string
state *ConsensusState
evpool *EvidencePool
broadcastChannel chan MsgInfo
started uint32 // atomic
stopped uint32 // atomic
......@@ -140,7 +135,7 @@ type Node struct {
}
// NewNode method
func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivKey, network string, version string, state *ConsensusState, evpool *EvidencePool) *Node {
func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivKey, network string, version string, state *ConsensusState) *Node {
address := GenAddressByPubKey(privKey.PubKey())
node := &Node{
......@@ -158,7 +153,6 @@ func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivK
reconnecting: NewMutexMap(),
broadcastChannel: make(chan MsgInfo, maxSendQueueSize),
state: state,
evpool: evpool,
localIPs: make(map[string]net.IP),
}
......@@ -219,7 +213,6 @@ func (node *Node) Start() {
go node.StartConsensusRoutine()
go node.BroadcastRoutine()
go node.evidenceBroadcastRoutine()
}
}
......@@ -234,7 +227,7 @@ func (node *Node) DialPeerWithAddress(addr string) error {
func (node *Node) addOutboundPeerWithConfig(addr string) error {
tendermintlog.Info("Dialing peer", "address", addr)
peerConn, err := newOutboundPeerConn(addr, node.privKey, node.StopPeerForError, node.state, node.evpool)
peerConn, err := newOutboundPeerConn(addr, node.privKey, node.StopPeerForError, node.state)
if err != nil {
go node.reconnectToPeer(addr)
return err
......@@ -307,66 +300,6 @@ func (node *Node) StartConsensusRoutine() {
}
}
func (node *Node) evidenceBroadcastRoutine() {
ticker := time.NewTicker(time.Second * broadcastEvidenceIntervalS)
for {
select {
case evidence := <-node.evpool.EvidenceChan():
// broadcast some new evidence
data, err := proto.Marshal(evidence.Child())
if err != nil {
msg := MsgInfo{TypeID: ttypes.EvidenceListID,
Msg: &tmtypes.EvidenceData{
Evidence: []*tmtypes.EvidenceEnvelope{
{
TypeName: evidence.TypeName(),
Data: data,
},
},
},
PeerID: node.ID, PeerIP: node.IP,
}
node.Broadcast(msg)
// TODO: Broadcast runs asynchronously, so this should wait on the successChan
// in another routine before marking to be proper.
node.evpool.evidenceStore.MarkEvidenceAsBroadcasted(evidence)
}
case <-ticker.C:
// broadcast all pending evidence
var eData tmtypes.EvidenceData
evidence := node.evpool.PendingEvidence()
for _, item := range evidence {
ev := item.Child()
if ev != nil {
data, err := proto.Marshal(ev)
if err != nil {
panic("AddEvidence marshal failed")
}
env := &tmtypes.EvidenceEnvelope{
TypeName: item.TypeName(),
Data: data,
}
eData.Evidence = append(eData.Evidence, env)
}
}
msg := MsgInfo{TypeID: ttypes.EvidenceListID,
Msg: &eData,
PeerID: node.ID,
PeerIP: node.IP,
}
node.Broadcast(msg)
case _, ok := <-node.quit:
if !ok {
node.quit = nil
tendermintlog.Info("evidenceBroadcastRoutine quit")
return
}
}
}
}
// BroadcastRoutine receive to broadcast
func (node *Node) BroadcastRoutine() {
for {
......@@ -413,7 +346,7 @@ func (node *Node) StopPeerForError(peer Peer, reason interface{}) {
}
func (node *Node) addInboundPeer(conn net.Conn) error {
peerConn, err := newInboundPeerConn(conn, node.privKey, node.StopPeerForError, node.state, node.evpool)
peerConn, err := newInboundPeerConn(conn, node.privKey, node.StopPeerForError, node.state)
if err != nil {
if er := conn.Close(); er != nil {
tendermintlog.Error("addInboundPeer close conn failed", "er", er)
......@@ -481,11 +414,6 @@ func (node *Node) addPeer(pc *peerConn) error {
return fmt.Errorf("get remote ip failed:%v", rErr)
}
// Filter peer against ID white list
//if err := node.FilterConnByID(peerID); err != nil {
//return err
//}
// Check version, chain id
if err := node.CompatibleWith(peerNodeInfo); err != nil {
return err
......@@ -507,7 +435,6 @@ func (node *Node) addPeer(pc *peerConn) error {
if err := node.peerSet.Add(pc); err != nil {
return err
}
//node.metrics.Peers.Add(float64(1))
tendermintlog.Info("Added peer", "peer", pc.ip)
return nil
......@@ -710,13 +637,13 @@ func dial(addr string) (net.Conn, error) {
return conn, nil
}
func newOutboundPeerConn(addr string, ourNodePrivKey crypto.PrivKey, onPeerError func(Peer, interface{}), state *ConsensusState, evpool *EvidencePool) (*peerConn, 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)
}
pc, err := newPeerConn(conn, true, true, ourNodePrivKey, onPeerError, state, evpool)
pc, err := newPeerConn(conn, true, true, ourNodePrivKey, onPeerError, state)
if err != nil {
if cerr := conn.Close(); cerr != nil {
return &peerConn{}, fmt.Errorf("newPeerConn failed:%v, connection close failed:%v", err, cerr)
......@@ -732,12 +659,11 @@ func newInboundPeerConn(
ourNodePrivKey crypto.PrivKey,
onPeerError func(Peer, interface{}),
state *ConsensusState,
evpool *EvidencePool,
) (*peerConn, error) {
// TODO: issue PoW challenge
return newPeerConn(conn, false, false, ourNodePrivKey, onPeerError, state, evpool)
return newPeerConn(conn, false, false, ourNodePrivKey, onPeerError, state)
}
func newPeerConn(
......@@ -746,7 +672,6 @@ func newPeerConn(
ourNodePrivKey crypto.PrivKey,
onPeerError func(Peer, interface{}),
state *ConsensusState,
evpool *EvidencePool,
) (pc *peerConn, err error) {
conn := rawConn
......@@ -769,6 +694,5 @@ func newPeerConn(
conn: conn,
onPeerError: onPeerError,
myState: state,
myevpool: evpool,
}, nil
}
package tendermint
import (
"encoding/hex"
"fmt"
"sync"
"testing"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
)
var (
secureConnCrypto crypto.Crypto
sum = 0
mutx sync.Mutex
privKey = "B3DC4C0725884EBB7264B92F1D8D37584A64ADE1799D997EC64B4FE3973E08DE220ACBE680DF2473A0CB48987A00FCC1812F106A7390BE6B8E2D31122C992A19"
expectAddress = "02A13174B92727C4902DB099E51A3339F48BD45E"
)
func init() {
cr2, err := crypto.New(types.GetSignName("", types.ED25519))
if err != nil {
fmt.Println("crypto.New failed for types.ED25519")
return
}
secureConnCrypto = cr2
}
func TestParallel(t *testing.T) {
Parallel(
func() {
mutx.Lock()
sum++
mutx.Unlock()
},
func() {
mutx.Lock()
sum += 2
mutx.Unlock()
},
func() {
mutx.Lock()
sum += 3
mutx.Unlock()
},
)
fmt.Println("TestParallel ok")
assert.Equal(t, 6, sum)
}
func TestGenAddressByPubKey(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())
strAddr := fmt.Sprintf("%X", addr)
assert.Equal(t, expectAddress, strAddr)
fmt.Println("TestGenAddressByPubKey ok")
}
func TestIP2IPPort(t *testing.T) {
testMap := NewMutexMap()
assert.Equal(t, false, testMap.Has("1.1.1.1"))
testMap.Set("1.1.1.1", "1.1.1.1:80")
assert.Equal(t, true, testMap.Has("1.1.1.1"))
testMap.Set("1.1.1.2", "1.1.1.2:80")
assert.Equal(t, true, testMap.Has("1.1.1.2"))
testMap.Delete("1.1.1.1")
assert.Equal(t, false, testMap.Has("1.1.1.1"))
fmt.Println("TestIP2IPPort ok")
}
func TestPeerSet(t *testing.T) {
testSet := NewPeerSet()
assert.Equal(t, false, testSet.Has("1"))
peer1 := &peerConn{id: "1", ip: []byte("1.1.1.1")}
testSet.Add(peer1)
assert.Equal(t, true, testSet.Has("1"))
assert.Equal(t, true, testSet.HasIP([]byte("1.1.1.1")))
err := testSet.Add(peer1)
assert.NotNil(t, err)
peer2 := &peerConn{id: "2", ip: []byte("1.1.1.2")}
testSet.Add(peer2)
assert.Equal(t, true, testSet.Has("2"))
assert.Equal(t, 2, testSet.Size())
testSet.Remove(peer1)
assert.Equal(t, 1, testSet.Size())
assert.Equal(t, false, testSet.Has("1"))
assert.Equal(t, false, testSet.HasIP([]byte("1.1.1.1")))
fmt.Println("TestPeerSet ok")
}
This diff is collapsed.
......@@ -133,7 +133,6 @@ func (sc *SecretConnection) Write(data []byte) (n int, err error) {
// encrypt the frame
var sealedFrame = make([]byte, sealedFrameSize)
secretbox.Seal(sealedFrame[:0], frame, sc.sendNonce, sc.shrSecret)
// fmt.Printf("secretbox.Seal(sealed:%X,sendNonce:%X,shrSecret:%X\n", sealedFrame, sc.sendNonce, sc.shrSecret)
incr2Nonce(sc.sendNonce)
// end encryption
......@@ -162,7 +161,6 @@ func (sc *SecretConnection) Read(data []byte) (n int, err error) {
// decrypt the frame
var frame = make([]byte, totalFrameSize)
// fmt.Printf("secretbox.Open(sealed:%X,recvNonce:%X,shrSecret:%X\n", sealedFrame, sc.recvNonce, sc.shrSecret)
_, ok := secretbox.Open(frame[:0], sealedFrame, sc.recvNonce, sc.shrSecret)
if !ok {
return n, errors.New("Failed to decrypt SecretConnection")
......@@ -298,9 +296,7 @@ func shareAuthSignature(sc io.ReadWriter, pubKey crypto.PubKey, signature crypto
if err2 != nil {
return
}
//n := int(0) // not used.
//recvMsg = wire.ReadBinary(authSigMessage{}, bytes.NewBuffer(readBuffer), authSigMsgSize, &n, &err2).(authSigMessage)
//secret.Info("shareAuthSignature", "readBuffer", readBuffer)
recvMsg.Key, err2 = types.ConsensusCrypto.PubKeyFromBytes(readBuffer[:32])
if err2 != nil {
return
......
......@@ -118,9 +118,9 @@ func (s State) GetValidators() (last *ttypes.ValidatorSet, current *ttypes.Valid
// Create a block from the latest state
// MakeBlock builds a block with the given txs and commit from the current state.
func (s State) MakeBlock(height int64, round int64, Txs []*types.Transaction, commit *tmtypes.TendermintCommit) *ttypes.TendermintBlock {
func (s State) MakeBlock(height int64, round int64, pblock *types.Block, commit *tmtypes.TendermintCommit, proposerAddr []byte) *ttypes.TendermintBlock {
// build base block
block := ttypes.MakeBlock(height, round, Txs, commit)
block := ttypes.MakeBlock(height, round, pblock, commit)
// fill header with state data
block.Header.ChainID = s.ChainID
......@@ -130,6 +130,7 @@ func (s State) MakeBlock(height int64, round int64, Txs []*types.Transaction, co
block.Header.AppHash = s.AppHash
block.Header.ConsensusHash = s.ConsensusParams.Hash()
block.Header.LastResultsHash = s.LastResultsHash
block.Header.ProposerAddr = proposerAddr
return block
}
......@@ -226,6 +227,7 @@ func LoadState(state *tmtypes.State) State {
ChainID: state.GetChainID(),
LastBlockHeight: state.GetLastBlockHeight(),
LastBlockTotalTx: state.GetLastBlockTotalTx(),
LastBlockID: ttypes.BlockID{BlockID: *state.LastBlockID},
LastBlockTime: state.LastBlockTime,
Validators: nil,
LastValidators: nil,
......@@ -307,7 +309,7 @@ func (csdb *CSStateDB) LoadValidators(height int64) (*ttypes.ValidatorSet, error
if height == 0 {
return nil, nil
}
if csdb.state.LastBlockHeight+1 == height {
if csdb.state.LastBlockHeight == height {
return csdb.state.Validators, nil
}
......@@ -374,6 +376,7 @@ func SaveState(state State) *tmtypes.State {
ChainID: state.ChainID,
LastBlockHeight: state.LastBlockHeight,
LastBlockTotalTx: state.LastBlockTotalTx,
LastBlockID: &state.LastBlockID.BlockID,
LastBlockTime: state.LastBlockTime,
Validators: &tmtypes.ValidatorSet{Validators: make([]*tmtypes.Validator, 0), Proposer: &tmtypes.Validator{}},
LastValidators: &tmtypes.ValidatorSet{Validators: make([]*tmtypes.Validator, 0), Proposer: &tmtypes.Validator{}},
......@@ -458,17 +461,14 @@ func LoadProposer(source *tmtypes.Validator) (*ttypes.Validator, error) {
}
// CreateBlockInfoTx make blockInfo to the first transaction of the block and execer is valnode
func CreateBlockInfoTx(pubkey string, lastCommit *tmtypes.TendermintCommit, seenCommit *tmtypes.TendermintCommit, state *tmtypes.State, proposal *tmtypes.Proposal, block *tmtypes.TendermintBlock) *types.Transaction {
blockNoTxs := *block
blockNoTxs.Txs = make([]*types.Transaction, 0)
func CreateBlockInfoTx(pubkey string, state *tmtypes.State, block *tmtypes.TendermintBlock) *types.Transaction {
blockSave := *block
blockSave.Data = nil
blockInfo := &tmtypes.TendermintBlockInfo{
SeenCommit: seenCommit,
LastCommit: lastCommit,
State: state,
Proposal: proposal,
Block: &blockNoTxs,
State: state,
Block: &blockSave,
}
tendermintlog.Debug("CreateBlockInfoTx", "validators", blockInfo.State.Validators.Validators, "block", block, "block-notxs", blockNoTxs)
tendermintlog.Debug("CreateBlockInfoTx", "blockInfo", blockInfo)
nput := &tmtypes.ValNodeAction_BlockInfo{BlockInfo: blockInfo}
action := &tmtypes.ValNodeAction{Value: nput, Ty: tmtypes.ValNodeActionBlockInfo}
......
package tendermint
import (
"fmt"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/gogo/protobuf/proto"
)
var (
stateKey = []byte("stateKey")
)
type ConsensusStore struct {
db dbm.DB
}
// NewConsensusStore returns a new ConsensusStore with the given DB
func NewConsensusStore() *ConsensusStore {
db := DefaultDBProvider("state")
db.SetCacheSize(100)
return &ConsensusStore{
db: db,
}
}
// LoadStateFromStore
func (cs *ConsensusStore) LoadStateFromStore() *tmtypes.State {
buf, err := cs.db.Get(stateKey)
if err != nil {
tendermintlog.Error("LoadStateFromStore", "err", err)
return nil
}
state := &tmtypes.State{}
err = types.Decode(buf, state)
if err != nil {
panic(err)
}
return state
}
// LoadStateHeight
func (cs *ConsensusStore) LoadStateHeight() int64 {
state := cs.LoadStateFromStore()
if state == nil {
return int64(0)
}
return state.LastBlockHeight
}
// LoadSeenCommit by height
func (cs *ConsensusStore) LoadSeenCommit(height int64) *tmtypes.TendermintCommit {
buf, err := cs.db.Get(calcSeenCommitKey(height))
if err != nil {
tendermintlog.Error("LoadSeenCommit", "err", err)
return nil
}
commit := &tmtypes.TendermintCommit{}
err = types.Decode(buf, commit)
if err != nil {
panic(err)
}
return commit
}
// SaveConsensusState save state and seenCommit
func (cs *ConsensusStore) SaveConsensusState(height int64, state *tmtypes.State, sc proto.Message) error {
seenCommitBytes := types.Encode(sc)
stateBytes := types.Encode(state)
batch := cs.db.NewBatch(true)
batch.Set(calcSeenCommitKey(height), seenCommitBytes)
batch.Set(stateKey, stateBytes)
err := batch.Write()
if err != nil {
tendermintlog.Error("SaveConsensusState batch.Write", "err", err)
return err
}
return nil
}
func calcSeenCommitKey(height int64) []byte {
return []byte(fmt.Sprintf("SC:%v", height))
}
This diff is collapsed.
......@@ -7,6 +7,7 @@ package tendermint
import (
"context"
"encoding/binary"
"encoding/hex"
"errors"
"flag"
"fmt"
......@@ -15,6 +16,8 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/33cn/chain33/blockchain"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/limits"
......@@ -27,6 +30,7 @@ import (
"github.com/33cn/chain33/store"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/norm/types"
ty "github.com/33cn/plugin/plugin/dapp/valnode/types"
"google.golang.org/grpc"
_ "github.com/33cn/chain33/system"
......@@ -36,7 +40,7 @@ import (
var (
random *rand.Rand
loopCount = 10
loopCount = 3
conn *grpc.ClientConn
c types.Chain33Client
)
......@@ -50,12 +54,12 @@ func init() {
log.SetLogLevel("info")
}
func TestTendermintPerf(t *testing.T) {
TendermintPerf()
TendermintPerf(t)
fmt.Println("=======start clear test data!=======")
clearTestData()
}
func TendermintPerf() {
func TendermintPerf(t *testing.T) {
q, chain, s, mem, exec, cs, p2p := initEnvTendermint()
defer chain.Close()
defer mem.Close()
......@@ -68,12 +72,18 @@ func TendermintPerf() {
for err != nil {
err = createConn()
}
time.Sleep(10 * time.Second)
time.Sleep(2 * time.Second)
for i := 0; i < loopCount; i++ {
NormPut()
time.Sleep(time.Second)
}
time.Sleep(10 * time.Second)
CheckState(t, cs.(*Client))
AddNode()
for i := 0; i < loopCount*3; i++ {
NormPut()
time.Sleep(time.Second)
}
time.Sleep(2 * time.Second)
}
func initEnvTendermint() (queue.Queue, *blockchain.BlockChain, queue.Module, queue.Module, *executor.Executor, queue.Module, queue.Module) {
......@@ -177,3 +187,38 @@ func NormPut() {
return
}
}
func AddNode() {
pubkey := "788657125A5A547B499F8B74239092EBB6466E8A205348D9EA645D510235A671"
pubkeybyte, err := hex.DecodeString(pubkey)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
nput := &ty.ValNodeAction_Node{Node: &ty.ValNode{PubKey: pubkeybyte, Power: int64(2)}}
action := &ty.ValNodeAction{Value: nput, Ty: ty.ValNodeActionUpdate}
tx := &types.Transaction{Execer: []byte("valnode"), Payload: types.Encode(action), Fee: fee}
tx.To = address.ExecAddress("valnode")
tx.Nonce = random.Int63()
tx.Sign(types.SECP256K1, getprivkey("CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944"))
reply, err := c.SendTransaction(context.Background(), tx)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
if !reply.IsOk {
fmt.Fprintln(os.Stderr, errors.New(string(reply.GetMsg())))
return
}
}
func CheckState(t *testing.T, client *Client) {
msg1, err := client.Query_IsHealthy(&types.ReqNil{})
assert.Nil(t, err)
flag := msg1.(*ty.IsHealthy).IsHealthy
assert.Equal(t, true, flag)
_, err = client.Query_NodeInfo(&types.ReqNil{})
assert.Nil(t, err)
}
......@@ -17,7 +17,6 @@ import (
"github.com/33cn/chain33/common/merkle"
"github.com/33cn/chain33/types"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/golang/protobuf/proto"
)
var (
......@@ -58,67 +57,62 @@ type TendermintBlock struct {
// MakeBlock returns a new block with an empty header, except what can be computed from itself.
// It populates the same set of fields validated by ValidateBasic
func MakeBlock(height int64, round int64, Txs []*types.Transaction, commit *tmtypes.TendermintCommit) *TendermintBlock {
block := &TendermintBlock{&tmtypes.TendermintBlock{
Header: &tmtypes.TendermintBlockHeader{
Height: height,
Round: round,
Time: time.Now().UnixNano(),
NumTxs: int64(len(Txs)),
func MakeBlock(height int64, round int64, pblock *types.Block, commit *tmtypes.TendermintCommit) *TendermintBlock {
block := &TendermintBlock{
&tmtypes.TendermintBlock{
Header: &tmtypes.TendermintBlockHeader{
Height: height,
Round: round,
Time: pblock.BlockTime,
NumTxs: int64(len(pblock.Txs)),
},
Data: pblock,
LastCommit: commit,
},
Txs: Txs,
LastCommit: commit,
Evidence: &tmtypes.EvidenceData{Evidence: make([]*tmtypes.EvidenceEnvelope, 0)},
},
}
block.FillHeader()
return block
}
// AddEvidence appends the given evidence to the block
func (b *TendermintBlock) AddEvidence(evidence []Evidence) {
for _, item := range evidence {
ev := item.Child()
if ev != nil {
data, err := proto.Marshal(ev)
if err != nil {
blocklog.Error("AddEvidence marshal failed", "error", err)
panic("AddEvidence marshal failed")
}
env := &tmtypes.EvidenceEnvelope{
TypeName: item.TypeName(),
Data: data,
}
b.Evidence.Evidence = append(b.Evidence.Evidence, env)
}
}
}
// ValidateBasic performs basic validation that doesn't involve state data.
// It checks the internal consistency of the block.
func (b *TendermintBlock) ValidateBasic() (int64, error) {
newTxs := int64(len(b.Txs))
// Further validation is done using state#ValidateBlock.
func (b *TendermintBlock) ValidateBasic() error {
if b == nil {
return errors.New("nil block")
}
if b.Header.Height < 0 {
return errors.New("Negative Header.Height")
} else if b.Header.Height == 0 {
return errors.New("Zero Header.Height")
}
newTxs := int64(len(b.Data.Txs))
if b.Header.NumTxs != newTxs {
return 0, fmt.Errorf("Wrong Block.Header.NumTxs. Expected %v, got %v", newTxs, b.Header.NumTxs)
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")
}
lastCommit := Commit{
TendermintCommit: b.LastCommit,
}
if !bytes.Equal(b.Header.LastCommitHash, lastCommit.Hash()) {
return 0, fmt.Errorf("Wrong Block.Header.LastCommitHash. Expected %v, got %v", b.Header.LastCommitHash, lastCommit.Hash())
}
if b.Header.Height != 1 {
if b.Header.Height > 1 {
if b.LastCommit == nil {
return errors.New("nil LastCommit")
}
if err := lastCommit.ValidateBasic(); err != nil {
return 0, err
return err
}
}
evidence := &EvidenceData{EvidenceData: b.Evidence}
if !bytes.Equal(b.Header.EvidenceHash, evidence.Hash()) {
return 0, errors.New(Fmt("Wrong Block.Header.EvidenceHash. Expected %v, got %v", b.Header.EvidenceHash, evidence.Hash()))
if !bytes.Equal(b.Header.LastCommitHash, lastCommit.Hash()) {
return fmt.Errorf("Wrong Header.LastCommitHash. Expected %v, got %v", b.Header.LastCommitHash, lastCommit.Hash())
}
return newTxs, nil
return nil
}
// FillHeader fills in any remaining header fields that are a function of the block data
......@@ -129,10 +123,6 @@ func (b *TendermintBlock) FillHeader() {
}
b.Header.LastCommitHash = lastCommit.Hash()
}
if b.Header.EvidenceHash == nil {
evidence := &EvidenceData{EvidenceData: b.Evidence}
b.Header.EvidenceHash = evidence.Hash()
}
}
// Hash computes and returns the block hash.
......@@ -173,10 +163,8 @@ func (b *TendermintBlock) StringIndented(indent string) string {
return Fmt(`Block{
%s %v
%s %v
%s %v
%s}#%v`,
indent, header.StringIndented(indent+" "),
// indent, b.Evidence.StringIndented(indent+" "),
indent, lastCommit.StringIndented(indent+" "),
indent, b.Hash())
}
......@@ -227,7 +215,6 @@ func (h *Header) StringIndented(indent string) string {
%s App: %v
%s Conensus: %v
%s Results: %v
%s Evidence: %v
%s}#%v`,
indent, h.ChainID,
indent, h.Height,
......@@ -240,24 +227,19 @@ func (h *Header) StringIndented(indent string) string {
indent, h.AppHash,
indent, h.ConsensusHash,
indent, h.LastResultsHash,
indent, h.EvidenceHash,
indent, h.Hash())
}
// Commit struct
type Commit struct {
*tmtypes.TendermintCommit
firstPrecommit *tmtypes.Vote
hash []byte
bitArray *BitArray
firstPrecommit *tmtypes.Vote
}
// FirstPrecommit returns the first non-nil precommit in the commit
func (commit *Commit) FirstPrecommit() *tmtypes.Vote {
if len(commit.Precommits) == 0 {
return nil
}
if commit.firstPrecommit != nil {
return commit.firstPrecommit
}
......@@ -334,13 +316,14 @@ func (commit *Commit) ValidateBasic() error {
height, round := commit.Height(), commit.Round()
// validate the precommits
for _, precommit := range commit.Precommits {
// It's OK for precommits to be missing.
if precommit == nil {
for _, item := range commit.Precommits {
// may be nil if validator skipped.
if item == nil || len(item.Signature) == 0 {
continue
}
precommit := &Vote{Vote: item}
// Ensure that all votes are precommits
if byte(precommit.Type) != VoteTypePrecommit {
if precommit.Type != uint32(VoteTypePrecommit) {
return fmt.Errorf("Invalid commit vote. Expected precommit, got %v",
precommit.Type)
}
......@@ -394,113 +377,3 @@ type SignedHeader struct {
Header *Header `json:"header"`
Commit *Commit `json:"commit"`
}
// EvidenceEnvelope ...
type EvidenceEnvelope struct {
*tmtypes.EvidenceEnvelope
}
// EvidenceEnvelopeList contains any evidence of malicious wrong-doing by validators
type EvidenceEnvelopeList []EvidenceEnvelope
// Hash ...
func (env EvidenceEnvelope) Hash() []byte {
penv := env.EvidenceEnvelope
evidence := EvidenceEnvelope2Evidence(penv)
if evidence != nil {
return evidence.Hash()
}
return nil
}
func (env EvidenceEnvelope) String() string {
penv := env.EvidenceEnvelope
evidence := EvidenceEnvelope2Evidence(penv)
if evidence != nil {
return evidence.String()
}
return ""
}
// Hash returns the simple merkle root hash of the EvidenceList.
func (evl EvidenceEnvelopeList) Hash() []byte {
// Recursive impl.
// Copied from tmlibs/merkle to avoid allocations
switch len(evl) {
case 0:
return nil
case 1:
return evl[0].Hash()
default:
left := evl[:(len(evl)+1)/2].Hash()
right := evl[(len(evl)+1)/2:].Hash()
cache := make([]byte, len(left)+len(right))
return merkle.GetHashFromTwoHash(cache, left, right)
}
}
func (evl EvidenceEnvelopeList) String() string {
s := ""
for _, e := range evl {
s += Fmt("%s\t\t", e)
}
return s
}
// Has returns true if the evidence is in the EvidenceList.
func (evl EvidenceEnvelopeList) Has(evidence Evidence) bool {
for _, ev := range evl {
penv := ev.EvidenceEnvelope
tmp := EvidenceEnvelope2Evidence(penv)
if tmp != nil {
if tmp.Equal(evidence) {
return true
}
}
}
return false
}
// EvidenceData ...
type EvidenceData struct {
*tmtypes.EvidenceData
hash []byte
}
// Hash returns the hash of the data.
func (data *EvidenceData) Hash() []byte {
if data.hash == nil {
if data.EvidenceData == nil {
return nil
}
var evidence EvidenceEnvelopeList
for _, item := range data.Evidence {
elem := EvidenceEnvelope{
EvidenceEnvelope: item,
}
evidence = append(evidence, elem)
}
data.hash = evidence.Hash()
}
return data.hash
}
// StringIndented returns a string representation of the evidence.
func (data *EvidenceData) StringIndented(indent string) string {
if data == nil {
return "nil-Evidence"
}
evStrings := make([]string, MinInt(len(data.Evidence), 21))
for i, ev := range data.Evidence {
if i == 20 {
evStrings[i] = Fmt("... (%v total)", len(data.Evidence))
break
}
evStrings[i] = Fmt("Evidence:%v", ev)
}
return Fmt(`Data{
%s %v
%s}#%v`,
indent, strings.Join(evStrings, "\n"+indent+" "),
indent, data.hash)
}
// 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 types
import "errors"
var (
// ErrBaseTxType error type
ErrBaseTxType = errors.New("ErrBaseTxType")
// ErrBlockInfoTx error type
ErrBlockInfoTx = errors.New("ErrBlockInfoTx")
// ErrBaseExecErr error type
ErrBaseExecErr = errors.New("ErrBaseExecErr")
// ErrLastBlockID error type
ErrLastBlockID = errors.New("ErrLastBlockID")
)
This diff is collapsed.
......@@ -100,9 +100,8 @@ func (hvs *HeightVoteSet) SetRound(round int) {
func (hvs *HeightVoteSet) addRound(round int) {
if _, ok := hvs.roundVoteSets[round]; ok {
panic(Fmt("Panicked on a Sanity Check: %v", "addRound() for an existing round"))
panic("addRound() for an existing round")
}
// log.Debug("addRound(round)", "round", round)
prevotes := NewVoteSet(hvs.chainID, hvs.height, round, VoteTypePrevote, hvs.valSet)
precommits := NewVoteSet(hvs.chainID, hvs.height, round, VoteTypePrecommit, hvs.valSet)
hvs.roundVoteSets[round] = RoundVoteSet{
......@@ -179,7 +178,7 @@ func (hvs *HeightVoteSet) getVoteSet(round int, voteType byte) *VoteSet {
case VoteTypePrecommit:
return rvs.Precommits
default:
panic(Fmt("Panicked on a Sanity Check: %v", Fmt("Unexpected vote type %X", voteType)))
panic(Fmt("Unexpected vote type %X", voteType))
}
}
......
......@@ -440,7 +440,7 @@ func (pv *PrivValidatorImp) SignHeartbeat(chainID string, heartbeat *Heartbeat)
// String returns a string representation of the PrivValidatorImp.
func (pv *PrivValidatorImp) String() string {
return Fmt("PrivValidator{%v LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastHeight, pv.LastRound, pv.LastStep)
return Fmt("PrivValidator{%X LH:%v, LR:%v, LS:%v}", pv.GetAddress(), pv.LastHeight, pv.LastRound, pv.LastStep)
}
// GetLastHeight ...
......
......@@ -32,7 +32,6 @@ const (
RoundStepCommit = RoundStepType(0x08) // Entered commit state machine
// NOTE: RoundStepNewHeight acts as RoundStepCommitWait.
EvidenceListID = byte(0x01)
NewRoundStepID = byte(0x02)
CommitStepID = byte(0x03)
ProposalID = byte(0x04)
......@@ -43,6 +42,7 @@ const (
VoteSetBitsID = byte(0x09)
ProposalHeartbeatID = byte(0x0a)
ProposalBlockID = byte(0x0b)
ValidBlockID = byte(0x0c)
PacketTypePing = byte(0xff)
PacketTypePong = byte(0xfe)
......@@ -51,7 +51,6 @@ const (
// InitMessageMap ...
func InitMessageMap() {
MsgMap = map[byte]reflect.Type{
EvidenceListID: reflect.TypeOf(tmtypes.EvidenceData{}),
NewRoundStepID: reflect.TypeOf(tmtypes.NewRoundStepMsg{}),
CommitStepID: reflect.TypeOf(tmtypes.CommitStepMsg{}),
ProposalID: reflect.TypeOf(tmtypes.Proposal{}),
......@@ -62,6 +61,7 @@ func InitMessageMap() {
VoteSetBitsID: reflect.TypeOf(tmtypes.VoteSetBitsMsg{}),
ProposalHeartbeatID: reflect.TypeOf(tmtypes.Heartbeat{}),
ProposalBlockID: reflect.TypeOf(tmtypes.TendermintBlock{}),
ValidBlockID: reflect.TypeOf(tmtypes.ValidBlockMsg{}),
}
}
......@@ -108,6 +108,8 @@ type RoundState struct {
ProposalBlock *TendermintBlock
LockedRound int
LockedBlock *TendermintBlock
ValidRound int // Last known round with POL for non-nil valid block.
ValidBlock *TendermintBlock // Last known block of POL mentioned above.
Votes *HeightVoteSet
CommitRound int
LastCommit *VoteSet // Last precommits at Height-1
......@@ -141,6 +143,8 @@ func (rs *RoundState) StringIndented(indent string) string {
%s ProposalBlock: %v
%s LockedRound: %v
%s LockedBlock: %v
%s ValidRound: %v
%s ValidBlock: %v
%s Votes: %v
%s LastCommit: %v
%s LastValidators:%v
......@@ -153,6 +157,8 @@ func (rs *RoundState) StringIndented(indent string) string {
indent, rs.ProposalBlock.StringShort(),
indent, rs.LockedRound,
indent, rs.LockedBlock.StringShort(),
indent, rs.ValidRound,
indent, rs.ValidBlock.StringShort(),
indent, rs.Votes.StringIndented(indent+" "),
indent, rs.LastCommit.StringShort(),
indent, rs.LastValidators.StringIndented(indent+" "),
......@@ -173,14 +179,15 @@ type PeerRoundState struct {
StartTime time.Time // Estimated start of round 0 at this height
Proposal bool // True if peer has proposal for this round
ProposalBlock bool // True if peer has proposal block for this round
ProposalPOLRound int // Proposal's POL round. -1 if none.
ProposalPOL *BitArray // nil until ProposalPOLMessage received.
Prevotes *BitArray // All votes peer has for this round
Precommits *BitArray // All precommits peer has for this round
LastCommitRound int // Round of commit for last height. -1 if none.
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
ProposalBlockHash []byte
ProposalPOLRound int // Proposal's POL round. -1 if none.
ProposalPOL *BitArray // nil until ProposalPOLMessage received.
Prevotes *BitArray // All votes peer has for this round
Precommits *BitArray // All precommits peer has for this round
LastCommitRound int // Round of commit for last height. -1 if none.
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
}
// String returns a string representation of the PeerRoundState
......@@ -194,6 +201,7 @@ func (prs PeerRoundState) StringIndented(indent string) string {
%s %v/%v/%v @%v
%s Proposal %v
%s ProposalBlock %v
%s ProposalBlockHash %X
%s POL %v (round %v)
%s Prevotes %v
%s Precommits %v
......@@ -203,6 +211,7 @@ func (prs PeerRoundState) StringIndented(indent string) string {
indent, prs.Height, prs.Round, prs.Step, prs.StartTime,
indent, prs.Proposal,
indent, prs.ProposalBlock,
indent, prs.ProposalBlock,
indent, prs.ProposalPOL, prs.ProposalPOLRound,
indent, prs.Prevotes,
indent, prs.Precommits,
......
......@@ -25,6 +25,7 @@ var (
ErrVoteInvalidSignature = errors.New("Invalid signature")
ErrVoteInvalidBlockHash = errors.New("Invalid block hash")
ErrVoteNonDeterministicSignature = errors.New("Non-deterministic signature")
ErrVoteConflict = errors.New("Conflicting vote")
ErrVoteNil = errors.New("Nil vote")
votelog = log15.New("module", "tendermint-vote")
)
......@@ -70,7 +71,7 @@ func NewProposal(height int64, round int, blockhash []byte, polRound int, polBlo
// String returns a string representation of the Proposal.
func (p *Proposal) String() string {
return fmt.Sprintf("Proposal{%v/%v (%v,%v) %X %v @ %s}",
return fmt.Sprintf("Proposal{%v/%v (%v, %X) %X %X @ %s}",
p.Height, p.Round, p.POLRound, p.POLBlockID,
p.Blockhash, p.Signature, CanonicalTime(time.Unix(0, p.Timestamp)))
}
......@@ -119,34 +120,6 @@ func (heartbeat *Heartbeat) WriteSignBytes(chainID string, w io.Writer, n *int,
*err = writeErr
}
// ErrVoteConflictingVotes ...
type ErrVoteConflictingVotes struct {
*DuplicateVoteEvidence
}
func (err *ErrVoteConflictingVotes) Error() string {
pubkey, error := PubKeyFromString(err.PubKey)
if error != nil {
return fmt.Sprintf("Conflicting votes from validator PubKey:%v,error:%v", err.PubKey, error)
}
addr := GenAddressByPubKey(pubkey)
return fmt.Sprintf("Conflicting votes from validator %v", addr)
}
// NewConflictingVoteError ...
func NewConflictingVoteError(val *Validator, voteA, voteB *tmtypes.Vote) *ErrVoteConflictingVotes {
keyString := fmt.Sprintf("%X", val.PubKey)
return &ErrVoteConflictingVotes{
&DuplicateVoteEvidence{
&tmtypes.DuplicateVoteEvidence{
PubKey: keyString,
VoteA: voteA,
VoteB: voteB,
},
},
}
}
// Types of votes
// TODO Make a new type "VoteType"
const (
......@@ -211,7 +184,7 @@ func (vote *Vote) String() string {
PanicSanity("Unknown vote type")
}
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %v @ %s}",
return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}",
vote.ValidatorIndex, Fingerprint(vote.ValidatorAddress),
vote.Height, vote.Round, vote.Type, typeString,
Fingerprint(vote.BlockID.Hash), vote.Signature,
......@@ -240,7 +213,6 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
// Hash ...
func (vote *Vote) Hash() []byte {
if vote == nil {
//votelog.Error("vote hash is nil")
return nil
}
bytes, err := json.Marshal(vote)
......
......@@ -70,7 +70,7 @@ func (v *Validator) String() string {
if v == nil {
return "nil-Validator"
}
return Fmt("Validator{%v %v VP:%v A:%v}",
return Fmt("Validator{ADDR:%X PUB:%X VP:%v A:%v}",
v.Address,
v.PubKey,
v.VotingPower,
......
......@@ -200,7 +200,7 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
// Add vote and get conflicting vote if any
added, conflicting := voteSet.addVerifiedVote(vote, blockKey, val.VotingPower)
if conflicting != nil {
return added, NewConflictingVoteError(val, conflicting.Vote, vote.Vote)
return added, errors.Wrapf(ErrVoteConflict, "Conflicting vote: %v; New vote: %v", conflicting, vote)
}
if !added {
PanicSanity("Expected to add non-conflicting vote")
......
syntax = "proto3";
import "transaction.proto";
import "blockchain.proto";
package types;
......@@ -30,11 +30,9 @@ message TendermintCommit {
}
message TendermintBlockInfo {
TendermintCommit SeenCommit = 1;
TendermintCommit LastCommit = 2;
State State = 3;
Proposal Proposal = 4;
TendermintBlock block = 5;
State State = 2;
Proposal Proposal = 3;
TendermintBlock block = 4;
}
message BlockSize {
......@@ -89,20 +87,6 @@ message State {
bytes AppHash = 12;
}
message DuplicateVoteEvidence {
string pubKey = 1;
Vote voteA = 2;
Vote voteB = 3;
}
message EvidenceEnvelope {
string typeName = 1;
bytes data = 2;
}
message EvidenceData {
repeated EvidenceEnvelope evidence = 1;
}
message TendermintBlockHeader {
string chainID = 1;
......@@ -117,15 +101,13 @@ message TendermintBlockHeader {
bytes consensusHash = 10;
bytes appHash = 11;
bytes lastResultsHash = 12;
bytes evidenceHash = 13;
bytes proposerAddr = 13;
}
message TendermintBlock {
TendermintBlockHeader header = 1;
repeated Transaction txs = 2;
EvidenceData evidence = 3;
TendermintCommit lastCommit = 4;
bytes proposerAddr = 5;
TendermintBlockHeader header = 1;
Block data = 2;
TendermintCommit lastCommit = 4;
}
message Proposal {
......@@ -146,6 +128,13 @@ message NewRoundStepMsg {
int32 lastCommitRound = 5;
}
message ValidBlockMsg {
int64 height = 1;
int32 round = 2;
bytes blockhash = 3;
bool isCommit = 4;
}
message CommitStepMsg {
int64 height = 1;
}
......
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