mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-07 13:55:17 +00:00
draft of consensus+state code, compiles.
This commit is contained in:
53
state/account.go
Normal file
53
state/account.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/blocks"
|
||||
"io"
|
||||
)
|
||||
|
||||
// NOTE: consensus/Validator embeds this, so..
|
||||
type Account struct {
|
||||
Id uint64 // Numeric id of account, incrementing.
|
||||
PubKey []byte
|
||||
}
|
||||
|
||||
func ReadAccount(r io.Reader, n *int64, err *error) *Account {
|
||||
return &Account{
|
||||
Id: ReadUInt64(r, n, err),
|
||||
PubKey: ReadByteSlice(r, n, err),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Account) Verify(msg []byte, sig []byte) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
type PrivAccount struct {
|
||||
Account
|
||||
PrivKey []byte
|
||||
}
|
||||
|
||||
func (self *PrivAccount) Sign(msg []byte) Signature {
|
||||
return Signature{}
|
||||
}
|
||||
|
||||
/*
|
||||
// Signs the URI, which includes all data and metadata.
|
||||
// XXX implement or change
|
||||
func (bp *BlockPart) Sign(acc *PrivAccount) {
|
||||
// TODO: populate Signature
|
||||
}
|
||||
|
||||
// XXX maybe change.
|
||||
func (bp *BlockPart) ValidateWithSigner(signer *Account) error {
|
||||
// TODO: Sanity check height, index, total, bytes, etc.
|
||||
if !signer.Verify([]byte(bp.URI()), bp.Signature.Bytes) {
|
||||
return ErrInvalidBlockPartSignature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
*/
|
||||
@@ -119,9 +119,18 @@ func (s *State) Validators() *ValidatorSet {
|
||||
return s.validators
|
||||
}
|
||||
|
||||
func (s *State) Account(accountId uint64) *Account {
|
||||
func (s *State) Account(accountId uint64) (*Account, error) {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
// XXX: figure out a way to load an Account Binary type.
|
||||
return s.accounts.Get(accountId)
|
||||
idBytes, err := BasicCodec.Write(accountId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accountBytes := s.accounts.Get(idBytes)
|
||||
if accountBytes == nil {
|
||||
return nil, nil
|
||||
}
|
||||
n, err := int64(0), error(nil)
|
||||
account := ReadAccount(bytes.NewBuffer(accountBytes), &n, &err)
|
||||
return account, err
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import (
|
||||
"io"
|
||||
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/blocks"
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
db_ "github.com/tendermint/tendermint/db"
|
||||
)
|
||||
|
||||
// Holds state for a Validator at a given height+round.
|
||||
@@ -54,20 +52,6 @@ func (v *Validator) WriteTo(w io.Writer) (n int64, err error) {
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// TODO: Ensure that double signing never happens via an external persistent check.
|
||||
type PrivValidator struct {
|
||||
PrivAccount
|
||||
db *db_.LevelDB
|
||||
}
|
||||
|
||||
// Modifies the vote object in memory.
|
||||
// Double signing results in an error.
|
||||
func (pv *PrivValidator) SignVote(vote *Vote) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Not goroutine-safe.
|
||||
type ValidatorSet struct {
|
||||
validators map[uint64]*Validator
|
||||
@@ -124,6 +108,10 @@ func (v *ValidatorSet) Map() map[uint64]*Validator {
|
||||
return v.validators
|
||||
}
|
||||
|
||||
func (v *ValidatorSet) Size() int {
|
||||
return len(v.validators)
|
||||
}
|
||||
|
||||
// TODO: cache proposer. invalidate upon increment.
|
||||
func (v *ValidatorSet) GetProposer() (proposer *Validator) {
|
||||
highestAccum := int64(0)
|
||||
|
||||
207
state/vote.go
207
state/vote.go
@@ -1,207 +0,0 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/blocks"
|
||||
"github.com/tendermint/tendermint/config"
|
||||
)
|
||||
|
||||
const (
|
||||
VoteTypeBare = byte(0x00)
|
||||
VoteTypePrecommit = byte(0x01)
|
||||
VoteTypeCommit = byte(0x02)
|
||||
)
|
||||
|
||||
var (
|
||||
ErrVoteUnexpectedPhase = errors.New("Unexpected phase")
|
||||
ErrVoteInvalidAccount = errors.New("Invalid round vote account")
|
||||
ErrVoteInvalidSignature = errors.New("Invalid round vote signature")
|
||||
ErrVoteInvalidHash = errors.New("Invalid hash")
|
||||
ErrVoteConflictingSignature = errors.New("Conflicting round vote signature")
|
||||
)
|
||||
|
||||
// Represents a bare, precommit, or commit vote for proposals.
|
||||
type Vote struct {
|
||||
Height uint32
|
||||
Round uint16
|
||||
Type byte
|
||||
Hash []byte // empty if vote is nil.
|
||||
Signature
|
||||
}
|
||||
|
||||
func ReadVote(r io.Reader, n *int64, err *error) *Vote {
|
||||
return &Vote{
|
||||
Height: ReadUInt32(r, n, err),
|
||||
Round: ReadUInt16(r, n, err),
|
||||
Type: ReadByte(r, n, err),
|
||||
Hash: ReadByteSlice(r, n, err),
|
||||
Signature: ReadSignature(r, n, err),
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Vote) WriteTo(w io.Writer) (n int64, err error) {
|
||||
WriteUInt32(w, v.Height, &n, &err)
|
||||
WriteUInt16(w, v.Round, &n, &err)
|
||||
WriteByte(w, v.Type, &n, &err)
|
||||
WriteByteSlice(w, v.Hash, &n, &err)
|
||||
WriteBinary(w, v.Signature, &n, &err)
|
||||
return
|
||||
}
|
||||
|
||||
// This is the byteslice that validators should sign to signify a vote
|
||||
// for the given proposal at given height & round.
|
||||
// If hash is nil, the vote is a nil vote.
|
||||
func (v *Vote) GetDocument() []byte {
|
||||
switch v.Type {
|
||||
case VoteTypeBare:
|
||||
if len(v.Hash) == 0 {
|
||||
doc := fmt.Sprintf("%v://consensus/%v/%v/b\nnil",
|
||||
config.Config.Network, v.Height, v.Round)
|
||||
return []byte(doc)
|
||||
} else {
|
||||
doc := fmt.Sprintf("%v://consensus/%v/%v/b\n%v",
|
||||
config.Config.Network, v.Height, v.Round,
|
||||
CalcBlockURI(v.Height, v.Hash))
|
||||
return []byte(doc)
|
||||
}
|
||||
case VoteTypePrecommit:
|
||||
if len(v.Hash) == 0 {
|
||||
doc := fmt.Sprintf("%v://consensus/%v/%v/p\nnil",
|
||||
config.Config.Network, v.Height, v.Round)
|
||||
return []byte(doc)
|
||||
} else {
|
||||
doc := fmt.Sprintf("%v://consensus/%v/%v/p\n%v",
|
||||
config.Config.Network, v.Height, v.Round,
|
||||
CalcBlockURI(v.Height, v.Hash))
|
||||
return []byte(doc)
|
||||
}
|
||||
case VoteTypeCommit:
|
||||
if len(v.Hash) == 0 {
|
||||
panic("Commit hash cannot be nil")
|
||||
} else {
|
||||
doc := fmt.Sprintf("%v://consensus/%v/c\n%v",
|
||||
config.Config.Network, v.Height, // omit round info
|
||||
CalcBlockURI(v.Height, v.Hash))
|
||||
return []byte(doc)
|
||||
}
|
||||
default:
|
||||
panic("Unknown vote type")
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// VoteSet helps collect signatures from validators at each height+round
|
||||
// for a predefined vote type.
|
||||
type VoteSet struct {
|
||||
mtx sync.Mutex
|
||||
height uint32
|
||||
round uint16
|
||||
type_ byte
|
||||
validators map[uint64]*Validator
|
||||
votes map[uint64]*Vote
|
||||
votesByHash map[string]uint64
|
||||
totalVotes uint64
|
||||
totalVotingPower uint64
|
||||
}
|
||||
|
||||
// Constructs a new VoteSet struct used to accumulate votes for each round.
|
||||
func NewVoteSet(height uint32, round uint16, type_ byte, validators map[uint64]*Validator) *VoteSet {
|
||||
totalVotingPower := uint64(0)
|
||||
for _, val := range validators {
|
||||
totalVotingPower += val.VotingPower
|
||||
}
|
||||
return &VoteSet{
|
||||
height: height,
|
||||
round: round,
|
||||
type_: type_,
|
||||
validators: validators,
|
||||
votes: make(map[uint64]*Vote, len(validators)),
|
||||
votesByHash: make(map[string]uint64),
|
||||
totalVotes: 0,
|
||||
totalVotingPower: totalVotingPower,
|
||||
}
|
||||
}
|
||||
|
||||
// True if added, false if not.
|
||||
// Returns ErrVote[UnexpectedPhase|InvalidAccount|InvalidSignature|InvalidHash|ConflictingSignature]
|
||||
func (vs *VoteSet) AddVote(vote *Vote) (bool, error) {
|
||||
vs.mtx.Lock()
|
||||
defer vs.mtx.Unlock()
|
||||
|
||||
// Make sure the phase matches.
|
||||
if vote.Height != vs.height || vote.Round != vs.round || vote.Type != vs.type_ {
|
||||
return false, ErrVoteUnexpectedPhase
|
||||
}
|
||||
|
||||
val := vs.validators[vote.SignerId]
|
||||
// Ensure that signer is a validator.
|
||||
if val == nil {
|
||||
return false, ErrVoteInvalidAccount
|
||||
}
|
||||
// Check signature.
|
||||
if !val.Verify(vote.GetDocument(), vote.Signature.Bytes) {
|
||||
// Bad signature.
|
||||
return false, ErrVoteInvalidSignature
|
||||
}
|
||||
// If vote already exists, return false.
|
||||
if existingVote, ok := vs.votes[vote.SignerId]; ok {
|
||||
if bytes.Equal(existingVote.Hash, vote.Hash) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, ErrVoteConflictingSignature
|
||||
}
|
||||
}
|
||||
vs.votes[vote.SignerId] = vote
|
||||
vs.votesByHash[string(vote.Hash)] += val.VotingPower
|
||||
vs.totalVotes += val.VotingPower
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Returns either a blockhash (or nil) that received +2/3 majority.
|
||||
// If there exists no such majority, returns (nil, false).
|
||||
func (vs *VoteSet) TwoThirdsMajority() (hash []byte, ok bool) {
|
||||
vs.mtx.Lock()
|
||||
defer vs.mtx.Unlock()
|
||||
twoThirdsMajority := (vs.totalVotingPower*2 + 2) / 3
|
||||
if vs.totalVotes < twoThirdsMajority {
|
||||
return nil, false
|
||||
}
|
||||
for hash, votes := range vs.votesByHash {
|
||||
if votes >= twoThirdsMajority {
|
||||
if hash == "" {
|
||||
return nil, true
|
||||
} else {
|
||||
return []byte(hash), true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Returns blockhashes (or nil) that received a +1/3 majority.
|
||||
// If there exists no such majority, returns nil.
|
||||
func (vs *VoteSet) OneThirdMajority() (hashes []interface{}) {
|
||||
vs.mtx.Lock()
|
||||
defer vs.mtx.Unlock()
|
||||
oneThirdMajority := (vs.totalVotingPower + 2) / 3
|
||||
if vs.totalVotes < oneThirdMajority {
|
||||
return nil
|
||||
}
|
||||
for hash, votes := range vs.votesByHash {
|
||||
if votes >= oneThirdMajority {
|
||||
if hash == "" {
|
||||
hashes = append(hashes, nil)
|
||||
} else {
|
||||
hashes = append(hashes, []byte(hash))
|
||||
}
|
||||
}
|
||||
}
|
||||
return hashes
|
||||
}
|
||||
Reference in New Issue
Block a user