Add dump_consensus_state rpc command. Made it a little more secure

by moving the PrivValidator out of RoundState.
This commit is contained in:
Jae Kwon
2015-04-20 20:39:42 -07:00
parent df026f64fa
commit 733dfcf4ad
6 changed files with 88 additions and 18 deletions

View File

@ -187,7 +187,6 @@ type RoundState struct {
Precommits *VoteSet Precommits *VoteSet
Commits *VoteSet Commits *VoteSet
LastCommits *VoteSet LastCommits *VoteSet
PrivValidator *sm.PrivValidator
} }
func (rs *RoundState) String() string { func (rs *RoundState) String() string {
@ -241,6 +240,7 @@ type ConsensusState struct {
blockStore *bc.BlockStore blockStore *bc.BlockStore
mempoolReactor *mempl.MempoolReactor mempoolReactor *mempl.MempoolReactor
privValidator *sm.PrivValidator
runActionCh chan RoundAction runActionCh chan RoundAction
newStepCh chan *RoundState newStepCh chan *RoundState
@ -492,8 +492,12 @@ func (cs *ConsensusState) updateToState(state *sm.State, contiguous bool) {
cs.Round = 0 cs.Round = 0
cs.Step = RoundStepNewHeight cs.Step = RoundStepNewHeight
if cs.CommitTime.IsZero() { if cs.CommitTime.IsZero() {
// "Now" makes it easier to sync up dev nodes.
// We add newHeightDelta to allow transactions
// to be gathered for the first block.
// And alternative solution that relies on clocks:
// cs.StartTime = state.LastBlockTime.Add(newHeightDelta) // cs.StartTime = state.LastBlockTime.Add(newHeightDelta)
cs.StartTime = time.Now() // Makes it easier to sync up dev nodes. cs.StartTime = time.Now().Add(newHeightDelta)
} else { } else {
cs.StartTime = cs.CommitTime.Add(newHeightDelta) cs.StartTime = cs.CommitTime.Add(newHeightDelta)
} }
@ -523,12 +527,12 @@ func (cs *ConsensusState) updateToState(state *sm.State, contiguous bool) {
} }
// If we've timed out, then send rebond tx. // If we've timed out, then send rebond tx.
if cs.PrivValidator != nil && cs.state.UnbondingValidators.HasAddress(cs.PrivValidator.Address) { if cs.privValidator != nil && cs.state.UnbondingValidators.HasAddress(cs.privValidator.Address) {
rebondTx := &types.RebondTx{ rebondTx := &types.RebondTx{
Address: cs.PrivValidator.Address, Address: cs.privValidator.Address,
Height: cs.Height + 1, Height: cs.Height + 1,
} }
err := cs.PrivValidator.SignRebondTx(rebondTx) err := cs.privValidator.SignRebondTx(rebondTx)
if err == nil { if err == nil {
log.Info("Signed and broadcast RebondTx", "height", cs.Height, "round", cs.Round, "tx", rebondTx) log.Info("Signed and broadcast RebondTx", "height", cs.Height, "round", cs.Round, "tx", rebondTx)
cs.mempoolReactor.BroadcastTx(rebondTx) cs.mempoolReactor.BroadcastTx(rebondTx)
@ -567,7 +571,7 @@ func (cs *ConsensusState) setupNewRound(round uint) {
func (cs *ConsensusState) SetPrivValidator(priv *sm.PrivValidator) { func (cs *ConsensusState) SetPrivValidator(priv *sm.PrivValidator) {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
cs.PrivValidator = priv cs.privValidator = priv
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -600,14 +604,14 @@ func (cs *ConsensusState) RunActionPropose(height uint, round uint) {
}() }()
// Nothing to do if it's not our turn. // Nothing to do if it's not our turn.
if cs.PrivValidator == nil { if cs.privValidator == nil {
return return
} }
if !bytes.Equal(cs.Validators.Proposer().Address, cs.PrivValidator.Address) { if !bytes.Equal(cs.Validators.Proposer().Address, cs.privValidator.Address) {
log.Debug("Not our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.PrivValidator) log.Debug("Not our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.privValidator)
return return
} else { } else {
log.Debug("Our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.PrivValidator) log.Debug("Our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.privValidator)
} }
var block *types.Block var block *types.Block
@ -675,7 +679,7 @@ func (cs *ConsensusState) RunActionPropose(height uint, round uint) {
// Make proposal // Make proposal
proposal := NewProposal(cs.Height, cs.Round, blockParts.Header(), polParts.Header()) proposal := NewProposal(cs.Height, cs.Round, blockParts.Header(), polParts.Header())
err := cs.PrivValidator.SignProposal(proposal) err := cs.privValidator.SignProposal(proposal)
if err == nil { if err == nil {
log.Info("Signed and set proposal", "height", cs.Height, "round", cs.Round, "proposal", proposal) log.Info("Signed and set proposal", "height", cs.Height, "round", cs.Round, "proposal", proposal)
log.Debug(Fmt("Signed and set proposal block: %v", block)) log.Debug(Fmt("Signed and set proposal block: %v", block))
@ -1061,7 +1065,7 @@ func (cs *ConsensusState) stageBlock(block *types.Block, blockParts *types.PartS
} }
func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.PartSetHeader) *types.Vote { func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.PartSetHeader) *types.Vote {
if cs.PrivValidator == nil || !cs.Validators.HasAddress(cs.PrivValidator.Address) { if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.Address) {
return nil return nil
} }
vote := &types.Vote{ vote := &types.Vote{
@ -1071,10 +1075,10 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part
BlockHash: hash, BlockHash: hash,
BlockParts: header, BlockParts: header,
} }
err := cs.PrivValidator.SignVote(vote) err := cs.privValidator.SignVote(vote)
if err == nil { if err == nil {
log.Info("Signed and added vote", "height", cs.Height, "round", cs.Round, "vote", vote) log.Info("Signed and added vote", "height", cs.Height, "round", cs.Round, "vote", vote)
cs.addVote(cs.PrivValidator.Address, vote) cs.addVote(cs.privValidator.Address, vote)
return vote return vote
} else { } else {
log.Warn("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err) log.Warn("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err)

View File

@ -1,12 +1,11 @@
package core package core
import ( import (
"github.com/tendermint/tendermint/binary"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
) )
//-----------------------------------------------------------------------------
func ListValidators() (*ctypes.ResponseListValidators, error) { func ListValidators() (*ctypes.ResponseListValidators, error) {
var blockHeight uint var blockHeight uint
var bondedValidators []*sm.Validator var bondedValidators []*sm.Validator
@ -25,3 +24,8 @@ func ListValidators() (*ctypes.ResponseListValidators, error) {
return &ctypes.ResponseListValidators{blockHeight, bondedValidators, unbondingValidators}, nil return &ctypes.ResponseListValidators{blockHeight, bondedValidators, unbondingValidators}, nil
} }
func DumpConsensusState() (*ctypes.ResponseDumpConsensusState, error) {
jsonBytes := binary.JSONBytes(consensusState.GetRoundState())
return &ctypes.ResponseDumpConsensusState{string(jsonBytes)}, nil
}

View File

@ -2,7 +2,6 @@ package core
import ( import (
"fmt" "fmt"
. "github.com/tendermint/tendermint/common"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"

View File

@ -14,6 +14,7 @@ var Routes = map[string]*rpc.RPCFunc{
"call": rpc.NewRPCFunc(Call, []string{"address", "data"}), "call": rpc.NewRPCFunc(Call, []string{"address", "data"}),
"call_code": rpc.NewRPCFunc(CallCode, []string{"code", "data"}), "call_code": rpc.NewRPCFunc(CallCode, []string{"code", "data"}),
"list_validators": rpc.NewRPCFunc(ListValidators, []string{}), "list_validators": rpc.NewRPCFunc(ListValidators, []string{}),
"dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, []string{}),
"dump_storage": rpc.NewRPCFunc(DumpStorage, []string{"address"}), "dump_storage": rpc.NewRPCFunc(DumpStorage, []string{"address"}),
"broadcast_tx": rpc.NewRPCFunc(BroadcastTx, []string{"tx"}), "broadcast_tx": rpc.NewRPCFunc(BroadcastTx, []string{"tx"}),
"list_accounts": rpc.NewRPCFunc(ListAccounts, []string{}), "list_accounts": rpc.NewRPCFunc(ListAccounts, []string{}),

View File

@ -91,3 +91,7 @@ type ResponseListValidators struct {
BondedValidators []*sm.Validator BondedValidators []*sm.Validator
UnbondingValidators []*sm.Validator UnbondingValidators []*sm.Validator
} }
type ResponseDumpConsensusState struct {
ConsensusState string
}

View File

@ -18,6 +18,7 @@ type Client interface {
BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, error) BroadcastTx(tx types.Tx) (*ctypes.ResponseBroadcastTx, error)
Call(address []byte, data []byte) (*ctypes.ResponseCall, error) Call(address []byte, data []byte) (*ctypes.ResponseCall, error)
CallCode(code []byte, data []byte) (*ctypes.ResponseCall, error) CallCode(code []byte, data []byte) (*ctypes.ResponseCall, error)
DumpConsensusState() (*ctypes.ResponseDumpConsensusState, error)
DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, error) DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, error)
GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error) GenPrivAccount() (*ctypes.ResponseGenPrivAccount, error)
GetAccount(address []byte) (*ctypes.ResponseGetAccount, error) GetAccount(address []byte) (*ctypes.ResponseGetAccount, error)
@ -150,6 +151,36 @@ func (c *ClientHTTP) CallCode(code []byte, data []byte) (*ctypes.ResponseCall, e
return response.Result, nil return response.Result, nil
} }
func (c *ClientHTTP) DumpConsensusState() (*ctypes.ResponseDumpConsensusState, error) {
values, err := argsToURLValues(nil)
if err != nil {
return nil, err
}
resp, err := http.PostForm(c.addr+reverseFuncMap["DumpConsensusState"], values)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var response struct {
Result *ctypes.ResponseDumpConsensusState `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC string `json:"jsonrpc"`
}
binary.ReadJSON(&response, body, &err)
if err != nil {
return nil, err
}
if response.Error != "" {
return nil, fmt.Errorf(response.Error)
}
return response.Result, nil
}
func (c *ClientHTTP) DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, error) { func (c *ClientHTTP) DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, error) {
values, err := argsToURLValues([]string{"address"}, address) values, err := argsToURLValues([]string{"address"}, address)
if err != nil { if err != nil {
@ -558,6 +589,33 @@ func (c *ClientJSON) CallCode(code []byte, data []byte) (*ctypes.ResponseCall, e
return response.Result, nil return response.Result, nil
} }
func (c *ClientJSON) DumpConsensusState() (*ctypes.ResponseDumpConsensusState, error) {
request := rpc.RPCRequest{
JSONRPC: "2.0",
Method: reverseFuncMap["DumpConsensusState"],
Params: []interface{}{},
Id: 0,
}
body, err := c.RequestResponse(request)
if err != nil {
return nil, err
}
var response struct {
Result *ctypes.ResponseDumpConsensusState `json:"result"`
Error string `json:"error"`
Id string `json:"id"`
JSONRPC string `json:"jsonrpc"`
}
binary.ReadJSON(&response, body, &err)
if err != nil {
return nil, err
}
if response.Error != "" {
return nil, fmt.Errorf(response.Error)
}
return response.Result, nil
}
func (c *ClientJSON) DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, error) { func (c *ClientJSON) DumpStorage(address []byte) (*ctypes.ResponseDumpStorage, error) {
request := rpc.RPCRequest{ request := rpc.RPCRequest{
JSONRPC: "2.0", JSONRPC: "2.0",