mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-08 14:21:14 +00:00
refactor from Binary centric model to global method model
This commit is contained in:
127
state/state.go
Normal file
127
state/state.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/blocks"
|
||||
db_ "github.com/tendermint/tendermint/db"
|
||||
"github.com/tendermint/tendermint/merkle"
|
||||
)
|
||||
|
||||
var (
|
||||
stateKey = []byte("stateKey")
|
||||
)
|
||||
|
||||
type State struct {
|
||||
mtx sync.Mutex
|
||||
db db_.Db
|
||||
height uint32
|
||||
commitTime time.Time
|
||||
accounts merkle.Tree
|
||||
validators *ValidatorSet
|
||||
}
|
||||
|
||||
func LoadState(db db_.Db) *State {
|
||||
s := &State{}
|
||||
buf := db.Get(stateKey)
|
||||
if len(buf) == 0 {
|
||||
s.height = uint32(0)
|
||||
s.commitTime = time.Unix(0, 0) // XXX BOOTSTRAP
|
||||
s.accounts = merkle.NewIAVLTree(db) // XXX BOOTSTRAP
|
||||
s.validators = NewValidatorSet(nil) // XXX BOOTSTRAP
|
||||
} else {
|
||||
reader := bytes.NewReader(buf)
|
||||
var n int64
|
||||
var err error
|
||||
s.height = ReadUInt32(reader, &n, &err)
|
||||
s.commitTime = ReadTime(reader, &n, &err)
|
||||
accountsMerkleRoot := ReadByteSlice(reader, &n, &err)
|
||||
s.accounts = merkle.NewIAVLTreeFromHash(db, accountsMerkleRoot)
|
||||
s.validators = NewValidatorSet(nil)
|
||||
for reader.Len() > 0 {
|
||||
validator := ReadValidator(reader, &n, &err)
|
||||
s.validators.Add(validator)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *State) Save() {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
s.accounts.Save()
|
||||
var buf bytes.Buffer
|
||||
var n int64
|
||||
var err error
|
||||
WriteUInt32(&buf, s.height, &n, &err)
|
||||
WriteTime(&buf, s.commitTime, &n, &err)
|
||||
WriteByteSlice(&buf, s.accounts.Hash(), &n, &err)
|
||||
for _, validator := range s.validators.Map() {
|
||||
WriteBinary(&buf, validator, &n, &err)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.db.Set(stateKey, buf.Bytes())
|
||||
}
|
||||
|
||||
func (s *State) Copy() *State {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
return &State{
|
||||
db: s.db,
|
||||
height: s.height,
|
||||
commitTime: s.commitTime,
|
||||
accounts: s.accounts.Copy(),
|
||||
validators: s.validators.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) CommitTx(tx *Tx) error {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
// TODO commit the tx
|
||||
panic("Implement CommitTx()")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *State) CommitBlock(b *Block, commitTime time.Time) error {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
// TODO commit the txs
|
||||
// XXX also increment validator accum.
|
||||
panic("Implement CommitBlock()")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *State) Height() uint32 {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
return s.height
|
||||
}
|
||||
|
||||
func (s *State) CommitTime() time.Time {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
return s.commitTime
|
||||
}
|
||||
|
||||
// The returned ValidatorSet gets mutated upon s.Commit*().
|
||||
func (s *State) Validators() *ValidatorSet {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
return s.validators
|
||||
}
|
||||
|
||||
func (s *State) Account(accountId uint64) *Account {
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
// XXX: figure out a way to load an Account Binary type.
|
||||
return s.accounts.Get(accountId)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/blocks"
|
||||
//. "github.com/tendermint/tendermint/common"
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
db_ "github.com/tendermint/tendermint/db"
|
||||
)
|
||||
|
||||
@@ -20,15 +20,15 @@ type Validator struct {
|
||||
}
|
||||
|
||||
// Used to persist the state of ConsensusStateControl.
|
||||
func ReadValidator(r io.Reader) *Validator {
|
||||
func ReadValidator(r io.Reader, n *int64, err *error) *Validator {
|
||||
return &Validator{
|
||||
Account: Account{
|
||||
Id: Readuint64(r),
|
||||
PubKey: ReadByteSlice(r),
|
||||
Id: ReadUInt64(r, n, err),
|
||||
PubKey: ReadByteSlice(r, n, err),
|
||||
},
|
||||
BondHeight: Readuint32(r),
|
||||
VotingPower: Readuint64(r),
|
||||
Accum: Readint64(r),
|
||||
BondHeight: ReadUInt32(r, n, err),
|
||||
VotingPower: ReadUInt64(r, n, err),
|
||||
Accum: ReadInt64(r, n, err),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,11 +44,11 @@ func (v *Validator) Copy() *Validator {
|
||||
|
||||
// Used to persist the state of ConsensusStateControl.
|
||||
func (v *Validator) WriteTo(w io.Writer) (n int64, err error) {
|
||||
n, err = WriteTo(UInt64(v.Id), w, n, err)
|
||||
n, err = WriteTo(v.PubKey, w, n, err)
|
||||
n, err = WriteTo(UInt32(v.BondHeight), w, n, err)
|
||||
n, err = WriteTo(UInt64(v.VotingPower), w, n, err)
|
||||
n, err = WriteTo(Int64(v.Accum), w, n, err)
|
||||
WriteUInt64(w, v.Id, &n, &err)
|
||||
WriteByteSlice(w, v.PubKey, &n, &err)
|
||||
WriteUInt32(w, v.BondHeight, &n, &err)
|
||||
WriteUInt64(w, v.VotingPower, &n, &err)
|
||||
WriteInt64(w, v.Accum, &n, &err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func NewValidatorSet(validators map[uint64]*Validator) *ValidatorSet {
|
||||
validators = make(map[uint64]*Validator)
|
||||
}
|
||||
return &ValidatorSet{
|
||||
valdiators: validators,
|
||||
validators: validators,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ func (v *ValidatorSet) IncrementAccum() {
|
||||
|
||||
func (v *ValidatorSet) Copy() *ValidatorSet {
|
||||
mapCopy := map[uint64]*Validator{}
|
||||
for _, val := range validators {
|
||||
for _, val := range v.validators {
|
||||
mapCopy[val.Id] = val.Copy()
|
||||
}
|
||||
return &ValidatorSet{
|
||||
@@ -112,12 +112,12 @@ func (v *ValidatorSet) Copy() *ValidatorSet {
|
||||
}
|
||||
}
|
||||
|
||||
func (v *ValidatorSet) Add(validator *Valdaitor) {
|
||||
func (v *ValidatorSet) Add(validator *Validator) {
|
||||
v.validators[validator.Id] = validator
|
||||
}
|
||||
|
||||
func (v *ValidatorSet) Get(id uint64) *Validator {
|
||||
return v.validators[validator.Id]
|
||||
return v.validators[id]
|
||||
}
|
||||
|
||||
func (v *ValidatorSet) Map() map[uint64]*Validator {
|
||||
|
||||
207
state/vote.go
Normal file
207
state/vote.go
Normal file
@@ -0,0 +1,207 @@
|
||||
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