mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-20 15:04:22 +00:00
Merge branch 'develop' into jae/aminoify
This commit is contained in:
+9
-5
@@ -156,9 +156,8 @@ func (b *Block) StringIndented(indent string) string {
|
||||
func (b *Block) StringShort() string {
|
||||
if b == nil {
|
||||
return "nil-Block"
|
||||
} else {
|
||||
return fmt.Sprintf("Block#%v", b.Hash())
|
||||
}
|
||||
return fmt.Sprintf("Block#%v", b.Hash())
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -192,9 +191,11 @@ type Header struct {
|
||||
}
|
||||
|
||||
// Hash returns the hash of the header.
|
||||
// Returns nil if ValidatorHash is missing.
|
||||
// Returns nil if ValidatorHash is missing,
|
||||
// since a Header is not valid unless there is
|
||||
// a ValidaotrsHash (corresponding to the validator set).
|
||||
func (h *Header) Hash() cmn.HexBytes {
|
||||
if len(h.ValidatorsHash) == 0 {
|
||||
if h == nil || len(h.ValidatorsHash) == 0 {
|
||||
return nil
|
||||
}
|
||||
return merkle.SimpleHashFromMap(map[string]merkle.Hasher{
|
||||
@@ -230,7 +231,7 @@ func (h *Header) StringIndented(indent string) string {
|
||||
%s Data: %v
|
||||
%s Validators: %v
|
||||
%s App: %v
|
||||
%s Conensus: %v
|
||||
%s Consensus: %v
|
||||
%s Results: %v
|
||||
%s Evidence: %v
|
||||
%s}#%v`,
|
||||
@@ -428,6 +429,9 @@ type Data struct {
|
||||
|
||||
// Hash returns the hash of the data
|
||||
func (data *Data) Hash() cmn.HexBytes {
|
||||
if data == nil {
|
||||
return (Txs{}).Hash()
|
||||
}
|
||||
if data.hash == nil {
|
||||
data.hash = data.Txs.Hash() // NOTE: leaves of merkle tree are TxIDs
|
||||
}
|
||||
|
||||
+15
-1
@@ -3,7 +3,9 @@ package types
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
@@ -26,7 +28,7 @@ func TestValidateBlock(t *testing.T) {
|
||||
|
||||
// tamper with NumTxs
|
||||
block = MakeBlock(h, txs, commit)
|
||||
block.NumTxs += 1
|
||||
block.NumTxs++
|
||||
err = block.ValidateBasic()
|
||||
require.Error(t, err)
|
||||
|
||||
@@ -72,3 +74,15 @@ func makeBlockID(hash string, partSetSize int, partSetHash string) BlockID {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var nilBytes []byte
|
||||
|
||||
func TestNilHeaderHashDoesntCrash(t *testing.T) {
|
||||
assert.Equal(t, []byte((*Header)(nil).Hash()), nilBytes)
|
||||
assert.Equal(t, []byte((new(Header)).Hash()), nilBytes)
|
||||
}
|
||||
|
||||
func TestNilDataHashDoesntCrash(t *testing.T) {
|
||||
assert.Equal(t, []byte((*Data)(nil).Hash()), nilBytes)
|
||||
assert.Equal(t, []byte(new(Data).Hash()), nilBytes)
|
||||
}
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ func (b *EventBus) PublishEventTx(event EventDataTx) error {
|
||||
b.Logger.Info("Got tag with an empty key (skipping)", "tag", tag, "tx", event.Tx)
|
||||
continue
|
||||
}
|
||||
tags[string(tag.Key)] = tag.Value
|
||||
tags[string(tag.Key)] = string(tag.Value)
|
||||
}
|
||||
|
||||
// add predefined tags
|
||||
|
||||
@@ -7,9 +7,58 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
tmpubsub "github.com/tendermint/tmlibs/pubsub"
|
||||
tmquery "github.com/tendermint/tmlibs/pubsub/query"
|
||||
)
|
||||
|
||||
func TestEventBusPublishEventTx(t *testing.T) {
|
||||
eventBus := NewEventBus()
|
||||
err := eventBus.Start()
|
||||
require.NoError(t, err)
|
||||
defer eventBus.Stop()
|
||||
|
||||
tx := Tx("foo")
|
||||
result := abci.ResponseDeliverTx{Data: []byte("bar"), Tags: []cmn.KVPair{}, Fee: cmn.KI64Pair{Key: []uint8{}, Value: 0}}
|
||||
|
||||
txEventsCh := make(chan interface{})
|
||||
|
||||
// PublishEventTx adds all these 3 tags, so the query below should work
|
||||
query := fmt.Sprintf("tm.event='Tx' AND tx.height=1 AND tx.hash='%X'", tx.Hash())
|
||||
err = eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query), txEventsCh)
|
||||
require.NoError(t, err)
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for e := range txEventsCh {
|
||||
edt := e.(TMEventData).Unwrap().(EventDataTx)
|
||||
assert.Equal(t, int64(1), edt.Height)
|
||||
assert.Equal(t, uint32(0), edt.Index)
|
||||
assert.Equal(t, tx, edt.Tx)
|
||||
assert.Equal(t, result, edt.Result)
|
||||
close(done)
|
||||
}
|
||||
}()
|
||||
|
||||
err = eventBus.PublishEventTx(EventDataTx{TxResult{
|
||||
Height: 1,
|
||||
Index: 0,
|
||||
Tx: tx,
|
||||
Result: result,
|
||||
}})
|
||||
assert.NoError(t, err)
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("did not receive a transaction after 1 sec.")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEventBus(b *testing.B) {
|
||||
benchmarks := []struct {
|
||||
name string
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ func (dve *DuplicateVoteEvidence) Verify(chainID string) error {
|
||||
|
||||
// BlockIDs must be different
|
||||
if dve.VoteA.BlockID.Equals(dve.VoteB.BlockID) {
|
||||
return fmt.Errorf("DuplicateVoteEvidence Error: BlockIDs are the same (%v) - not a real duplicate vote!", dve.VoteA.BlockID)
|
||||
return fmt.Errorf("DuplicateVoteEvidence Error: BlockIDs are the same (%v) - not a real duplicate vote", dve.VoteA.BlockID)
|
||||
}
|
||||
|
||||
// Signatures must be valid
|
||||
|
||||
+11
-1
@@ -26,7 +26,17 @@ type GenesisDoc struct {
|
||||
ConsensusParams *ConsensusParams `json:"consensus_params,omitempty"`
|
||||
Validators []GenesisValidator `json:"validators"`
|
||||
AppHash cmn.HexBytes `json:"app_hash"`
|
||||
AppState json.RawMessage `json:"app_state,omitempty"`
|
||||
AppStateJSON json.RawMessage `json:"app_state,omitempty"`
|
||||
AppOptions json.RawMessage `json:"app_options,omitempty"` // DEPRECATED
|
||||
}
|
||||
|
||||
// AppState returns raw application state.
|
||||
// TODO: replace with AppState field during next breaking release (0.18)
|
||||
func (genDoc *GenesisDoc) AppState() json.RawMessage {
|
||||
if len(genDoc.AppOptions) > 0 {
|
||||
return genDoc.AppOptions
|
||||
}
|
||||
return genDoc.AppStateJSON
|
||||
}
|
||||
|
||||
// SaveAs is a utility method for saving GenensisDoc as a JSON file.
|
||||
|
||||
+13
-17
@@ -30,12 +30,11 @@ type Part struct {
|
||||
func (part *Part) Hash() []byte {
|
||||
if part.hash != nil {
|
||||
return part.hash
|
||||
} else {
|
||||
hasher := ripemd160.New()
|
||||
hasher.Write(part.Bytes) // nolint: errcheck, gas
|
||||
part.hash = hasher.Sum(nil)
|
||||
return part.hash
|
||||
}
|
||||
hasher := ripemd160.New()
|
||||
hasher.Write(part.Bytes) // nolint: errcheck, gas
|
||||
part.hash = hasher.Sum(nil)
|
||||
return part.hash
|
||||
}
|
||||
|
||||
func (part *Part) String() string {
|
||||
@@ -129,20 +128,18 @@ func NewPartSetFromHeader(header PartSetHeader) *PartSet {
|
||||
func (ps *PartSet) Header() PartSetHeader {
|
||||
if ps == nil {
|
||||
return PartSetHeader{}
|
||||
} else {
|
||||
return PartSetHeader{
|
||||
Total: ps.total,
|
||||
Hash: ps.hash,
|
||||
}
|
||||
}
|
||||
return PartSetHeader{
|
||||
Total: ps.total,
|
||||
Hash: ps.hash,
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *PartSet) HasHeader(header PartSetHeader) bool {
|
||||
if ps == nil {
|
||||
return false
|
||||
} else {
|
||||
return ps.Header().Equals(header)
|
||||
}
|
||||
return ps.Header().Equals(header)
|
||||
}
|
||||
|
||||
func (ps *PartSet) BitArray() *cmn.BitArray {
|
||||
@@ -251,7 +248,7 @@ func (psr *PartSetReader) Read(p []byte) (n int, err error) {
|
||||
return n1 + n2, err
|
||||
}
|
||||
|
||||
psr.i += 1
|
||||
psr.i++
|
||||
if psr.i >= len(psr.parts) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
@@ -262,9 +259,8 @@ func (psr *PartSetReader) Read(p []byte) (n int, err error) {
|
||||
func (ps *PartSet) StringShort() string {
|
||||
if ps == nil {
|
||||
return "nil-PartSet"
|
||||
} else {
|
||||
ps.mtx.Lock()
|
||||
defer ps.mtx.Unlock()
|
||||
return fmt.Sprintf("(%v of %v)", ps.Count(), ps.Total())
|
||||
}
|
||||
ps.mtx.Lock()
|
||||
defer ps.mtx.Unlock()
|
||||
return fmt.Sprintf("(%v of %v)", ps.Count(), ps.Total())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
// TODO: type ?
|
||||
const (
|
||||
stepNone int8 = 0 // Used to distinguish the initial state
|
||||
stepPropose int8 = 1
|
||||
stepPrevote int8 = 2
|
||||
stepPrecommit int8 = 3
|
||||
)
|
||||
|
||||
func voteToStep(vote *types.Vote) int8 {
|
||||
switch vote.Type {
|
||||
case types.VoteTypePrevote:
|
||||
return stepPrevote
|
||||
case types.VoteTypePrecommit:
|
||||
return stepPrecommit
|
||||
default:
|
||||
panic("Unknown vote type")
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
// LastSignedInfo contains information about the latest
|
||||
// data signed by a validator to help prevent double signing.
|
||||
type LastSignedInfo struct {
|
||||
Height int64 `json:"height"`
|
||||
Round int `json:"round"`
|
||||
Step int8 `json:"step"`
|
||||
Signature crypto.Signature `json:"signature,omitempty"` // so we dont lose signatures
|
||||
SignBytes cmn.HexBytes `json:"signbytes,omitempty"` // so we dont lose signatures
|
||||
}
|
||||
|
||||
func NewLastSignedInfo() *LastSignedInfo {
|
||||
return &LastSignedInfo{
|
||||
Step: stepNone,
|
||||
}
|
||||
}
|
||||
|
||||
func (lsi *LastSignedInfo) String() string {
|
||||
return fmt.Sprintf("LH:%v, LR:%v, LS:%v", lsi.Height, lsi.Round, lsi.Step)
|
||||
}
|
||||
|
||||
// Verify returns an error if there is a height/round/step regression
|
||||
// or if the HRS matches but there are no LastSignBytes.
|
||||
// It returns true if HRS matches exactly and the LastSignature exists.
|
||||
// It panics if the HRS matches, the LastSignBytes are not empty, but the LastSignature is empty.
|
||||
func (lsi LastSignedInfo) Verify(height int64, round int, step int8) (bool, error) {
|
||||
if lsi.Height > height {
|
||||
return false, errors.New("Height regression")
|
||||
}
|
||||
|
||||
if lsi.Height == height {
|
||||
if lsi.Round > round {
|
||||
return false, errors.New("Round regression")
|
||||
}
|
||||
|
||||
if lsi.Round == round {
|
||||
if lsi.Step > step {
|
||||
return false, errors.New("Step regression")
|
||||
} else if lsi.Step == step {
|
||||
if lsi.SignBytes != nil {
|
||||
if lsi.Signature.Empty() {
|
||||
panic("info: LastSignature is nil but LastSignBytes is not!")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, errors.New("No LastSignature found")
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Set height/round/step and signature on the info
|
||||
func (lsi *LastSignedInfo) Set(height int64, round int, step int8,
|
||||
signBytes []byte, sig crypto.Signature) {
|
||||
|
||||
lsi.Height = height
|
||||
lsi.Round = round
|
||||
lsi.Step = step
|
||||
lsi.Signature = sig
|
||||
lsi.SignBytes = signBytes
|
||||
}
|
||||
|
||||
// Reset resets all the values.
|
||||
// XXX: Unsafe.
|
||||
func (lsi *LastSignedInfo) Reset() {
|
||||
lsi.Height = 0
|
||||
lsi.Round = 0
|
||||
lsi.Step = 0
|
||||
lsi.Signature = crypto.Signature{}
|
||||
lsi.SignBytes = nil
|
||||
}
|
||||
|
||||
// SignVote checks the height/round/step (HRS) are greater than the latest state of the LastSignedInfo.
|
||||
// If so, it signs the vote, updates the LastSignedInfo, and sets the signature on the vote.
|
||||
// If the HRS are equal and the only thing changed is the timestamp, it sets the vote.Timestamp to the previous
|
||||
// value and the Signature to the LastSignedInfo.Signature.
|
||||
// Else it returns an error.
|
||||
func (lsi *LastSignedInfo) SignVote(signer types.Signer, chainID string, vote *types.Vote) error {
|
||||
height, round, step := vote.Height, vote.Round, voteToStep(vote)
|
||||
signBytes := vote.SignBytes(chainID)
|
||||
|
||||
sameHRS, err := lsi.Verify(height, round, step)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We might crash before writing to the wal,
|
||||
// causing us to try to re-sign for the same HRS.
|
||||
// If signbytes are the same, use the last signature.
|
||||
// If they only differ by timestamp, use last timestamp and signature
|
||||
// Otherwise, return error
|
||||
if sameHRS {
|
||||
if bytes.Equal(signBytes, lsi.SignBytes) {
|
||||
vote.Signature = lsi.Signature
|
||||
} else if timestamp, ok := checkVotesOnlyDifferByTimestamp(lsi.SignBytes, signBytes); ok {
|
||||
vote.Timestamp = timestamp
|
||||
vote.Signature = lsi.Signature
|
||||
} else {
|
||||
err = fmt.Errorf("Conflicting data")
|
||||
}
|
||||
return err
|
||||
}
|
||||
sig, err := signer.Sign(signBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lsi.Set(height, round, step, signBytes, sig)
|
||||
vote.Signature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignProposal checks if the height/round/step (HRS) are greater than the latest state of the LastSignedInfo.
|
||||
// If so, it signs the proposal, updates the LastSignedInfo, and sets the signature on the proposal.
|
||||
// If the HRS are equal and the only thing changed is the timestamp, it sets the timestamp to the previous
|
||||
// value and the Signature to the LastSignedInfo.Signature.
|
||||
// Else it returns an error.
|
||||
func (lsi *LastSignedInfo) SignProposal(signer types.Signer, chainID string, proposal *types.Proposal) error {
|
||||
height, round, step := proposal.Height, proposal.Round, stepPropose
|
||||
signBytes := proposal.SignBytes(chainID)
|
||||
|
||||
sameHRS, err := lsi.Verify(height, round, step)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We might crash before writing to the wal,
|
||||
// causing us to try to re-sign for the same HRS.
|
||||
// If signbytes are the same, use the last signature.
|
||||
// If they only differ by timestamp, use last timestamp and signature
|
||||
// Otherwise, return error
|
||||
if sameHRS {
|
||||
if bytes.Equal(signBytes, lsi.SignBytes) {
|
||||
proposal.Signature = lsi.Signature
|
||||
} else if timestamp, ok := checkProposalsOnlyDifferByTimestamp(lsi.SignBytes, signBytes); ok {
|
||||
proposal.Timestamp = timestamp
|
||||
proposal.Signature = lsi.Signature
|
||||
} else {
|
||||
err = fmt.Errorf("Conflicting data")
|
||||
}
|
||||
return err
|
||||
}
|
||||
sig, err := signer.Sign(signBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lsi.Set(height, round, step, signBytes, sig)
|
||||
proposal.Signature = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
// returns the timestamp from the lastSignBytes.
|
||||
// returns true if the only difference in the votes is their timestamp.
|
||||
func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
|
||||
var lastVote, newVote types.CanonicalJSONOnceVote
|
||||
if err := json.Unmarshal(lastSignBytes, &lastVote); err != nil {
|
||||
panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err))
|
||||
}
|
||||
if err := json.Unmarshal(newSignBytes, &newVote); err != nil {
|
||||
panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err))
|
||||
}
|
||||
|
||||
lastTime, err := time.Parse(types.TimeFormat, lastVote.Vote.Timestamp)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// set the times to the same value and check equality
|
||||
now := types.CanonicalTime(time.Now())
|
||||
lastVote.Vote.Timestamp = now
|
||||
newVote.Vote.Timestamp = now
|
||||
lastVoteBytes, _ := json.Marshal(lastVote)
|
||||
newVoteBytes, _ := json.Marshal(newVote)
|
||||
|
||||
return lastTime, bytes.Equal(newVoteBytes, lastVoteBytes)
|
||||
}
|
||||
|
||||
// returns the timestamp from the lastSignBytes.
|
||||
// returns true if the only difference in the proposals is their timestamp
|
||||
func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
|
||||
var lastProposal, newProposal types.CanonicalJSONOnceProposal
|
||||
if err := json.Unmarshal(lastSignBytes, &lastProposal); err != nil {
|
||||
panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into proposal: %v", err))
|
||||
}
|
||||
if err := json.Unmarshal(newSignBytes, &newProposal); err != nil {
|
||||
panic(fmt.Sprintf("signBytes cannot be unmarshalled into proposal: %v", err))
|
||||
}
|
||||
|
||||
lastTime, err := time.Parse(types.TimeFormat, lastProposal.Proposal.Timestamp)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// set the times to the same value and check equality
|
||||
now := types.CanonicalTime(time.Now())
|
||||
lastProposal.Proposal.Timestamp = now
|
||||
newProposal.Proposal.Timestamp = now
|
||||
lastProposalBytes, _ := json.Marshal(lastProposal)
|
||||
newProposalBytes, _ := json.Marshal(newProposal)
|
||||
|
||||
return lastTime, bytes.Equal(newProposalBytes, lastProposalBytes)
|
||||
}
|
||||
+3
-2
@@ -45,9 +45,10 @@ func (v *Validator) CompareAccum(other *Validator) *Validator {
|
||||
} else if v.Accum < other.Accum {
|
||||
return other
|
||||
} else {
|
||||
if bytes.Compare(v.Address, other.Address) < 0 {
|
||||
result := bytes.Compare(v.Address, other.Address)
|
||||
if result < 0 {
|
||||
return v
|
||||
} else if bytes.Compare(v.Address, other.Address) > 0 {
|
||||
} else if result > 0 {
|
||||
return other
|
||||
} else {
|
||||
cmn.PanicSanity("Cannot compare identical validators")
|
||||
|
||||
+45
-35
@@ -82,27 +82,30 @@ func (valSet *ValidatorSet) Copy() *ValidatorSet {
|
||||
}
|
||||
}
|
||||
|
||||
// HasAddress returns true if address given is in the validator set, false -
|
||||
// otherwise.
|
||||
func (valSet *ValidatorSet) HasAddress(address []byte) bool {
|
||||
idx := sort.Search(len(valSet.Validators), func(i int) bool {
|
||||
return bytes.Compare(address, valSet.Validators[i].Address) <= 0
|
||||
})
|
||||
return idx != len(valSet.Validators) && bytes.Equal(valSet.Validators[idx].Address, address)
|
||||
return idx < len(valSet.Validators) && bytes.Equal(valSet.Validators[idx].Address, address)
|
||||
}
|
||||
|
||||
// GetByAddress returns an index of the validator with address and validator
|
||||
// itself if found. Otherwise, -1 and nil are returned.
|
||||
func (valSet *ValidatorSet) GetByAddress(address []byte) (index int, val *Validator) {
|
||||
idx := sort.Search(len(valSet.Validators), func(i int) bool {
|
||||
return bytes.Compare(address, valSet.Validators[i].Address) <= 0
|
||||
})
|
||||
if idx != len(valSet.Validators) && bytes.Equal(valSet.Validators[idx].Address, address) {
|
||||
if idx < len(valSet.Validators) && bytes.Equal(valSet.Validators[idx].Address, address) {
|
||||
return idx, valSet.Validators[idx].Copy()
|
||||
} else {
|
||||
return 0, nil
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// GetByIndex returns the validator by index.
|
||||
// It returns nil values if index < 0 or
|
||||
// index >= len(ValidatorSet.Validators)
|
||||
// GetByIndex returns the validator's address and validator itself by index.
|
||||
// It returns nil values if index is less than 0 or greater or equal to
|
||||
// len(ValidatorSet.Validators).
|
||||
func (valSet *ValidatorSet) GetByIndex(index int) (address []byte, val *Validator) {
|
||||
if index < 0 || index >= len(valSet.Validators) {
|
||||
return nil, nil
|
||||
@@ -111,10 +114,12 @@ func (valSet *ValidatorSet) GetByIndex(index int) (address []byte, val *Validato
|
||||
return val.Address, val.Copy()
|
||||
}
|
||||
|
||||
// Size returns the length of the validator set.
|
||||
func (valSet *ValidatorSet) Size() int {
|
||||
return len(valSet.Validators)
|
||||
}
|
||||
|
||||
// TotalVotingPower returns the sum of the voting powers of all validators.
|
||||
func (valSet *ValidatorSet) TotalVotingPower() int64 {
|
||||
if valSet.totalVotingPower == 0 {
|
||||
for _, val := range valSet.Validators {
|
||||
@@ -125,6 +130,8 @@ func (valSet *ValidatorSet) TotalVotingPower() int64 {
|
||||
return valSet.totalVotingPower
|
||||
}
|
||||
|
||||
// GetProposer returns the current proposer. If the validator set is empty, nil
|
||||
// is returned.
|
||||
func (valSet *ValidatorSet) GetProposer() (proposer *Validator) {
|
||||
if len(valSet.Validators) == 0 {
|
||||
return nil
|
||||
@@ -145,6 +152,8 @@ func (valSet *ValidatorSet) findProposer() *Validator {
|
||||
return proposer
|
||||
}
|
||||
|
||||
// Hash returns the Merkle root hash build using validators (as leaves) in the
|
||||
// set.
|
||||
func (valSet *ValidatorSet) Hash() []byte {
|
||||
if len(valSet.Validators) == 0 {
|
||||
return nil
|
||||
@@ -156,12 +165,14 @@ func (valSet *ValidatorSet) Hash() []byte {
|
||||
return merkle.SimpleHashFromHashers(hashers)
|
||||
}
|
||||
|
||||
// Add adds val to the validator set and returns true. It returns false if val
|
||||
// is already in the set.
|
||||
func (valSet *ValidatorSet) Add(val *Validator) (added bool) {
|
||||
val = val.Copy()
|
||||
idx := sort.Search(len(valSet.Validators), func(i int) bool {
|
||||
return bytes.Compare(val.Address, valSet.Validators[i].Address) <= 0
|
||||
})
|
||||
if idx == len(valSet.Validators) {
|
||||
if idx >= len(valSet.Validators) {
|
||||
valSet.Validators = append(valSet.Validators, val)
|
||||
// Invalidate cache
|
||||
valSet.Proposer = nil
|
||||
@@ -182,39 +193,42 @@ func (valSet *ValidatorSet) Add(val *Validator) (added bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates val and returns true. It returns false if val is not present
|
||||
// in the set.
|
||||
func (valSet *ValidatorSet) Update(val *Validator) (updated bool) {
|
||||
index, sameVal := valSet.GetByAddress(val.Address)
|
||||
if sameVal == nil {
|
||||
return false
|
||||
} else {
|
||||
valSet.Validators[index] = val.Copy()
|
||||
// Invalidate cache
|
||||
valSet.Proposer = nil
|
||||
valSet.totalVotingPower = 0
|
||||
return true
|
||||
}
|
||||
valSet.Validators[index] = val.Copy()
|
||||
// Invalidate cache
|
||||
valSet.Proposer = nil
|
||||
valSet.totalVotingPower = 0
|
||||
return true
|
||||
}
|
||||
|
||||
// Remove deletes the validator with address. It returns the validator removed
|
||||
// and true. If returns nil and false if validator is not present in the set.
|
||||
func (valSet *ValidatorSet) Remove(address []byte) (val *Validator, removed bool) {
|
||||
idx := sort.Search(len(valSet.Validators), func(i int) bool {
|
||||
return bytes.Compare(address, valSet.Validators[i].Address) <= 0
|
||||
})
|
||||
if idx == len(valSet.Validators) || !bytes.Equal(valSet.Validators[idx].Address, address) {
|
||||
if idx >= len(valSet.Validators) || !bytes.Equal(valSet.Validators[idx].Address, address) {
|
||||
return nil, false
|
||||
} else {
|
||||
removedVal := valSet.Validators[idx]
|
||||
newValidators := valSet.Validators[:idx]
|
||||
if idx+1 < len(valSet.Validators) {
|
||||
newValidators = append(newValidators, valSet.Validators[idx+1:]...)
|
||||
}
|
||||
valSet.Validators = newValidators
|
||||
// Invalidate cache
|
||||
valSet.Proposer = nil
|
||||
valSet.totalVotingPower = 0
|
||||
return removedVal, true
|
||||
}
|
||||
removedVal := valSet.Validators[idx]
|
||||
newValidators := valSet.Validators[:idx]
|
||||
if idx+1 < len(valSet.Validators) {
|
||||
newValidators = append(newValidators, valSet.Validators[idx+1:]...)
|
||||
}
|
||||
valSet.Validators = newValidators
|
||||
// Invalidate cache
|
||||
valSet.Proposer = nil
|
||||
valSet.totalVotingPower = 0
|
||||
return removedVal, true
|
||||
}
|
||||
|
||||
// Iterate will run the given function over the set.
|
||||
func (valSet *ValidatorSet) Iterate(fn func(index int, val *Validator) bool) {
|
||||
for i, val := range valSet.Validators {
|
||||
stop := fn(i, val.Copy())
|
||||
@@ -265,10 +279,9 @@ func (valSet *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height
|
||||
|
||||
if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
|
||||
return nil
|
||||
} else {
|
||||
return fmt.Errorf("Invalid commit -- insufficient voting power: got %v, needed %v",
|
||||
talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
|
||||
}
|
||||
return fmt.Errorf("Invalid commit -- insufficient voting power: got %v, needed %v",
|
||||
talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
|
||||
}
|
||||
|
||||
// VerifyCommitAny will check to see if the set would
|
||||
@@ -471,9 +484,8 @@ func safeMulClip(a, b int64) int64 {
|
||||
if overflow {
|
||||
if (a < 0 || b < 0) && !(a < 0 && b < 0) {
|
||||
return math.MinInt64
|
||||
} else {
|
||||
return math.MaxInt64
|
||||
}
|
||||
return math.MaxInt64
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -483,9 +495,8 @@ func safeAddClip(a, b int64) int64 {
|
||||
if overflow {
|
||||
if b < 0 {
|
||||
return math.MinInt64
|
||||
} else {
|
||||
return math.MaxInt64
|
||||
}
|
||||
return math.MaxInt64
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -495,9 +506,8 @@ func safeSubClip(a, b int64) int64 {
|
||||
if overflow {
|
||||
if b > 0 {
|
||||
return math.MinInt64
|
||||
} else {
|
||||
return math.MaxInt64
|
||||
}
|
||||
return math.MaxInt64
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ func TestProposerSelection2(t *testing.T) {
|
||||
for i := 0; i < 120*N; i++ {
|
||||
prop := vals.GetProposer()
|
||||
ii := prop.Address[19]
|
||||
propCount[ii] += 1
|
||||
propCount[ii]++
|
||||
vals.IncrementAccum(1)
|
||||
}
|
||||
|
||||
|
||||
+19
-30
@@ -94,33 +94,29 @@ func (voteSet *VoteSet) ChainID() string {
|
||||
func (voteSet *VoteSet) Height() int64 {
|
||||
if voteSet == nil {
|
||||
return 0
|
||||
} else {
|
||||
return voteSet.height
|
||||
}
|
||||
return voteSet.height
|
||||
}
|
||||
|
||||
func (voteSet *VoteSet) Round() int {
|
||||
if voteSet == nil {
|
||||
return -1
|
||||
} else {
|
||||
return voteSet.round
|
||||
}
|
||||
return voteSet.round
|
||||
}
|
||||
|
||||
func (voteSet *VoteSet) Type() byte {
|
||||
if voteSet == nil {
|
||||
return 0x00
|
||||
} else {
|
||||
return voteSet.type_
|
||||
}
|
||||
return voteSet.type_
|
||||
}
|
||||
|
||||
func (voteSet *VoteSet) Size() int {
|
||||
if voteSet == nil {
|
||||
return 0
|
||||
} else {
|
||||
return voteSet.valSet.Size()
|
||||
}
|
||||
return voteSet.valSet.Size()
|
||||
}
|
||||
|
||||
// Returns added=true if vote is valid and new.
|
||||
@@ -185,9 +181,8 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
|
||||
if existing, ok := voteSet.getVote(valIndex, blockKey); ok {
|
||||
if existing.Signature.Equals(vote.Signature) {
|
||||
return false, nil // duplicate
|
||||
} else {
|
||||
return false, errors.Wrapf(ErrVoteNonDeterministicSignature, "Existing vote: %v; New vote: %v", existing, vote)
|
||||
}
|
||||
return false, errors.Wrapf(ErrVoteNonDeterministicSignature, "Existing vote: %v; New vote: %v", existing, vote)
|
||||
}
|
||||
|
||||
// Check signature.
|
||||
@@ -199,13 +194,11 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) {
|
||||
added, conflicting := voteSet.addVerifiedVote(vote, blockKey, val.VotingPower)
|
||||
if conflicting != nil {
|
||||
return added, NewConflictingVoteError(val, conflicting, vote)
|
||||
} else {
|
||||
if !added {
|
||||
cmn.PanicSanity("Expected to add non-conflicting vote")
|
||||
}
|
||||
return added, nil
|
||||
}
|
||||
|
||||
if !added {
|
||||
cmn.PanicSanity("Expected to add non-conflicting vote")
|
||||
}
|
||||
return added, nil
|
||||
}
|
||||
|
||||
// Returns (vote, true) if vote exists for valIndex and blockKey
|
||||
@@ -257,13 +250,12 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower
|
||||
// ... and there's a conflicting vote.
|
||||
// We're not even tracking this blockKey, so just forget it.
|
||||
return false, conflicting
|
||||
} else {
|
||||
// ... and there's no conflicting vote.
|
||||
// Start tracking this blockKey
|
||||
votesByBlock = newBlockVotes(false, voteSet.valSet.Size())
|
||||
voteSet.votesByBlock[blockKey] = votesByBlock
|
||||
// We'll add the vote in a bit.
|
||||
}
|
||||
// ... and there's no conflicting vote.
|
||||
// Start tracking this blockKey
|
||||
votesByBlock = newBlockVotes(false, voteSet.valSet.Size())
|
||||
voteSet.votesByBlock[blockKey] = votesByBlock
|
||||
// We'll add the vote in a bit.
|
||||
}
|
||||
|
||||
// Before adding to votesByBlock, see if we'll exceed quorum
|
||||
@@ -309,10 +301,9 @@ func (voteSet *VoteSet) SetPeerMaj23(peerID P2PID, blockID BlockID) error {
|
||||
if existing, ok := voteSet.peerMaj23s[peerID]; ok {
|
||||
if existing.Equals(blockID) {
|
||||
return nil // Nothing to do
|
||||
} else {
|
||||
return fmt.Errorf("SetPeerMaj23: Received conflicting blockID from peer %v. Got %v, expected %v",
|
||||
peerID, blockID, existing)
|
||||
}
|
||||
return fmt.Errorf("SetPeerMaj23: Received conflicting blockID from peer %v. Got %v, expected %v",
|
||||
peerID, blockID, existing)
|
||||
}
|
||||
voteSet.peerMaj23s[peerID] = blockID
|
||||
|
||||
@@ -321,10 +312,9 @@ func (voteSet *VoteSet) SetPeerMaj23(peerID P2PID, blockID BlockID) error {
|
||||
if ok {
|
||||
if votesByBlock.peerMaj23 {
|
||||
return nil // Nothing to do
|
||||
} else {
|
||||
votesByBlock.peerMaj23 = true
|
||||
// No need to copy votes, already there.
|
||||
}
|
||||
votesByBlock.peerMaj23 = true
|
||||
// No need to copy votes, already there.
|
||||
} else {
|
||||
votesByBlock = newBlockVotes(true, voteSet.valSet.Size())
|
||||
voteSet.votesByBlock[blockKey] = votesByBlock
|
||||
@@ -422,9 +412,8 @@ func (voteSet *VoteSet) TwoThirdsMajority() (blockID BlockID, ok bool) {
|
||||
defer voteSet.mtx.Unlock()
|
||||
if voteSet.maj23 != nil {
|
||||
return *voteSet.maj23, true
|
||||
} else {
|
||||
return BlockID{}, false
|
||||
}
|
||||
return BlockID{}, false
|
||||
}
|
||||
|
||||
func (voteSet *VoteSet) String() string {
|
||||
|
||||
Reference in New Issue
Block a user