Browse Source

refactor from Binary centric model to global method model

pull/9/head
Jae Kwon 10 years ago
parent
commit
e53b148acf
23 changed files with 622 additions and 690 deletions
  1. +0
    -18
      accounts/store.go
  2. +9
    -0
      binary/binary.go
  3. +10
    -10
      blocks/accounts.go
  4. +45
    -45
      blocks/adjustment.go
  5. +65
    -61
      blocks/block.go
  6. +4
    -2
      blocks/block_part_set.go
  7. +27
    -25
      blocks/block_test.go
  8. +11
    -9
      blocks/codec_test.go
  9. +2
    -6
      blocks/store.go
  10. +27
    -27
      blocks/tx.go
  11. +37
    -5
      consensus/README.md
  12. +96
    -81
      consensus/consensus.go
  13. +54
    -229
      consensus/state.go
  14. +0
    -66
      consensus/validator.go
  15. +39
    -50
      p2p/connection.go
  16. +2
    -3
      p2p/listener.go
  17. +9
    -9
      p2p/netaddress.go
  18. +2
    -1
      p2p/peer.go
  19. +15
    -13
      p2p/peer_manager.go
  20. +13
    -2
      p2p/switch_test.go
  21. +127
    -0
      state/state.go
  22. +16
    -16
      state/validator.go
  23. +12
    -12
      state/vote.go

+ 0
- 18
accounts/store.go View File

@ -1,18 +0,0 @@
package accounts
import (
. "github.com/tendermint/tendermint/blocks"
)
type AccountStore struct {
}
func (as *AccountStore) StageBlock(block *Block) error {
// XXX implement staging.
return nil
}
func (as *AccountStore) CommitBlock(block *Block) error {
// XXX implement staging.
return nil
}

+ 9
- 0
binary/binary.go View File

@ -6,6 +6,15 @@ type Binary interface {
WriteTo(w io.Writer) (int64, error) WriteTo(w io.Writer) (int64, error)
} }
func WriteBinary(w io.Writer, b Binary, n *int64, err *error) {
if *err != nil {
return
}
n_, err_ := b.WriteTo(w)
*n += int64(n_)
*err = err_
}
func WriteTo(w io.Writer, bz []byte, n *int64, err *error) { func WriteTo(w io.Writer, bz []byte, n *int64, err *error) {
if *err != nil { if *err != nil {
return return


+ 10
- 10
blocks/accounts.go View File

@ -8,10 +8,10 @@ import (
// NOTE: consensus/Validator embeds this, so.. // NOTE: consensus/Validator embeds this, so..
type Account struct { type Account struct {
Id uint64 // Numeric id of account, incrementing. Id uint64 // Numeric id of account, incrementing.
PubKey ByteSlice
PubKey []byte
} }
func (self *Account) Verify(msg ByteSlice, sig ByteSlice) bool {
func (self *Account) Verify(msg []byte, sig []byte) bool {
return false return false
} }
@ -19,10 +19,10 @@ func (self *Account) Verify(msg ByteSlice, sig ByteSlice) bool {
type PrivAccount struct { type PrivAccount struct {
Account Account
PrivKey ByteSlice
PrivKey []byte
} }
func (self *PrivAccount) Sign(msg ByteSlice) Signature {
func (self *PrivAccount) Sign(msg []byte) Signature {
return Signature{} return Signature{}
} }
@ -42,13 +42,13 @@ It usually follows the message to be signed.
type Signature struct { type Signature struct {
SignerId uint64 SignerId uint64
Bytes ByteSlice
Bytes []byte
} }
func ReadSignature(r io.Reader) Signature {
func ReadSignature(r io.Reader, n *int64, err *error) Signature {
return Signature{ return Signature{
SignerId: Readuint64(r),
Bytes: ReadByteSlice(r),
SignerId: ReadUInt64(r, n, err),
Bytes: ReadByteSlice(r, n, err),
} }
} }
@ -57,7 +57,7 @@ func (sig Signature) IsZero() bool {
} }
func (sig Signature) WriteTo(w io.Writer) (n int64, err error) { func (sig Signature) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(UInt64(sig.SignerId), w, n, err)
n, err = WriteTo(sig.Bytes, w, n, err)
WriteUInt64(w, sig.SignerId, &n, &err)
WriteByteSlice(w, sig.Bytes, &n, &err)
return return
} }

+ 45
- 45
blocks/adjustment.go View File

@ -16,41 +16,41 @@ import (
TODO: signing a bad checkpoint (block) TODO: signing a bad checkpoint (block)
*/ */
type Adjustment interface { type Adjustment interface {
Type() Byte
Type() byte
Binary Binary
} }
const ( const (
ADJ_TYPE_BOND = Byte(0x01)
ADJ_TYPE_UNBOND = Byte(0x02)
ADJ_TYPE_TIMEOUT = Byte(0x03)
ADJ_TYPE_DUPEOUT = Byte(0x04)
ADJ_TYPE_BOND = byte(0x01)
ADJ_TYPE_UNBOND = byte(0x02)
ADJ_TYPE_TIMEOUT = byte(0x03)
ADJ_TYPE_DUPEOUT = byte(0x04)
) )
func ReadAdjustment(r io.Reader) Adjustment {
switch t := ReadByte(r); t {
func ReadAdjustment(r io.Reader, n *int64, err *error) Adjustment {
switch t := ReadByte(r, n, err); t {
case ADJ_TYPE_BOND: case ADJ_TYPE_BOND:
return &Bond{ return &Bond{
Fee: Readuint64(r),
UnbondTo: Readuint64(r),
Amount: Readuint64(r),
Signature: ReadSignature(r),
Fee: ReadUInt64(r, n, err),
UnbondTo: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
Signature: ReadSignature(r, n, err),
} }
case ADJ_TYPE_UNBOND: case ADJ_TYPE_UNBOND:
return &Unbond{ return &Unbond{
Fee: Readuint64(r),
Amount: Readuint64(r),
Signature: ReadSignature(r),
Fee: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
Signature: ReadSignature(r, n, err),
} }
case ADJ_TYPE_TIMEOUT: case ADJ_TYPE_TIMEOUT:
return &Timeout{ return &Timeout{
AccountId: Readuint64(r),
Penalty: Readuint64(r),
AccountId: ReadUInt64(r, n, err),
Penalty: ReadUInt64(r, n, err),
} }
case ADJ_TYPE_DUPEOUT: case ADJ_TYPE_DUPEOUT:
return &Dupeout{ return &Dupeout{
VoteA: ReadBlockVote(r),
VoteB: ReadBlockVote(r),
VoteA: ReadBlockVote(r, n, err),
VoteB: ReadBlockVote(r, n, err),
} }
default: default:
Panicf("Unknown Adjustment type %x", t) Panicf("Unknown Adjustment type %x", t)
@ -68,16 +68,16 @@ type Bond struct {
Signature Signature
} }
func (self *Bond) Type() Byte {
func (self *Bond) Type() byte {
return ADJ_TYPE_BOND return ADJ_TYPE_BOND
} }
func (self *Bond) WriteTo(w io.Writer) (n int64, err error) { func (self *Bond) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(self.Type(), w, n, err)
n, err = WriteTo(UInt64(self.Fee), w, n, err)
n, err = WriteTo(UInt64(self.UnbondTo), w, n, err)
n, err = WriteTo(UInt64(self.Amount), w, n, err)
n, err = WriteTo(self.Signature, w, n, err)
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.Fee, &n, &err)
WriteUInt64(w, self.UnbondTo, &n, &err)
WriteUInt64(w, self.Amount, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return return
} }
@ -90,15 +90,15 @@ type Unbond struct {
Signature Signature
} }
func (self *Unbond) Type() Byte {
func (self *Unbond) Type() byte {
return ADJ_TYPE_UNBOND return ADJ_TYPE_UNBOND
} }
func (self *Unbond) WriteTo(w io.Writer) (n int64, err error) { func (self *Unbond) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(self.Type(), w, n, err)
n, err = WriteTo(UInt64(self.Fee), w, n, err)
n, err = WriteTo(UInt64(self.Amount), w, n, err)
n, err = WriteTo(self.Signature, w, n, err)
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.Fee, &n, &err)
WriteUInt64(w, self.Amount, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return return
} }
@ -110,14 +110,14 @@ type Timeout struct {
Penalty uint64 Penalty uint64
} }
func (self *Timeout) Type() Byte {
func (self *Timeout) Type() byte {
return ADJ_TYPE_TIMEOUT return ADJ_TYPE_TIMEOUT
} }
func (self *Timeout) WriteTo(w io.Writer) (n int64, err error) { func (self *Timeout) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(self.Type(), w, n, err)
n, err = WriteTo(UInt64(self.AccountId), w, n, err)
n, err = WriteTo(UInt64(self.Penalty), w, n, err)
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.AccountId, &n, &err)
WriteUInt64(w, self.Penalty, &n, &err)
return return
} }
@ -129,22 +129,22 @@ Typically only the signature is passed around, as the hash & height are implied.
*/ */
type BlockVote struct { type BlockVote struct {
Height uint64 Height uint64
BlockHash ByteSlice
BlockHash []byte
Signature Signature
} }
func ReadBlockVote(r io.Reader) BlockVote {
func ReadBlockVote(r io.Reader, n *int64, err *error) BlockVote {
return BlockVote{ return BlockVote{
Height: Readuint64(r),
BlockHash: ReadByteSlice(r),
Signature: ReadSignature(r),
Height: ReadUInt64(r, n, err),
BlockHash: ReadByteSlice(r, n, err),
Signature: ReadSignature(r, n, err),
} }
} }
func (self BlockVote) WriteTo(w io.Writer) (n int64, err error) { func (self BlockVote) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(UInt64(self.Height), w, n, err)
n, err = WriteTo(self.BlockHash, w, n, err)
n, err = WriteTo(self.Signature, w, n, err)
WriteUInt64(w, self.Height, &n, &err)
WriteByteSlice(w, self.BlockHash, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return return
} }
@ -154,13 +154,13 @@ type Dupeout struct {
VoteB BlockVote VoteB BlockVote
} }
func (self *Dupeout) Type() Byte {
func (self *Dupeout) Type() byte {
return ADJ_TYPE_DUPEOUT return ADJ_TYPE_DUPEOUT
} }
func (self *Dupeout) WriteTo(w io.Writer) (n int64, err error) { func (self *Dupeout) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(self.Type(), w, n, err)
n, err = WriteTo(self.VoteA, w, n, err)
n, err = WriteTo(self.VoteB, w, n, err)
WriteByte(w, self.Type(), &n, &err)
WriteBinary(w, self.VoteA, &n, &err)
WriteBinary(w, self.VoteB, &n, &err)
return return
} }

+ 65
- 61
blocks/block.go View File

@ -4,6 +4,7 @@ import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"io" "io"
"time"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
@ -32,18 +33,18 @@ type Block struct {
hash []byte hash []byte
} }
func ReadBlock(r io.Reader) *Block {
func ReadBlock(r io.Reader, n *int64, err *error) *Block {
return &Block{ return &Block{
Header: ReadHeader(r),
Validation: ReadValidation(r),
Txs: ReadTxs(r),
Header: ReadHeader(r, n, err),
Validation: ReadValidation(r, n, err),
Txs: ReadTxs(r, n, err),
} }
} }
func (b *Block) WriteTo(w io.Writer) (n int64, err error) { func (b *Block) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(&b.Header, w, n, err)
n, err = WriteTo(&b.Validation, w, n, err)
n, err = WriteTo(&b.Txs, w, n, err)
WriteBinary(w, &b.Header, &n, &err)
WriteBinary(w, &b.Validation, &n, &err)
WriteBinary(w, &b.Txs, &n, &err)
return return
} }
@ -54,20 +55,20 @@ func (b *Block) ValidateBasic() error {
} }
func (b *Block) URI() string { func (b *Block) URI() string {
return CalcBlockURI(uint32(b.Height), b.Hash())
return CalcBlockURI(b.Height, b.Hash())
} }
func (b *Block) Hash() []byte { func (b *Block) Hash() []byte {
if b.hash != nil { if b.hash != nil {
return b.hash return b.hash
} else { } else {
hashes := []Binary{
ByteSlice(b.Header.Hash()),
ByteSlice(b.Validation.Hash()),
ByteSlice(b.Txs.Hash()),
hashes := [][]byte{
b.Header.Hash(),
b.Validation.Hash(),
b.Txs.Hash(),
} }
// Merkle hash from sub-hashes. // Merkle hash from sub-hashes.
return merkle.HashFromBinarySlice(hashes)
return merkle.HashFromByteSlices(hashes)
} }
} }
@ -104,31 +105,31 @@ type BlockPart struct {
Round uint16 // Add Round? Well I need to know... Round uint16 // Add Round? Well I need to know...
Index uint16 Index uint16
Total uint16 Total uint16
Bytes ByteSlice
Bytes []byte
Signature Signature
// Volatile // Volatile
hash []byte hash []byte
} }
func ReadBlockPart(r io.Reader) *BlockPart {
func ReadBlockPart(r io.Reader, n *int64, err *error) *BlockPart {
return &BlockPart{ return &BlockPart{
Height: Readuint32(r),
Round: Readuint16(r),
Index: Readuint16(r),
Total: Readuint16(r),
Bytes: ReadByteSlice(r),
Signature: ReadSignature(r),
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
Index: ReadUInt16(r, n, err),
Total: ReadUInt16(r, n, err),
Bytes: ReadByteSlice(r, n, err),
Signature: ReadSignature(r, n, err),
} }
} }
func (bp *BlockPart) WriteTo(w io.Writer) (n int64, err error) { func (bp *BlockPart) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(UInt32(bp.Height), w, n, err)
n, err = WriteTo(UInt16(bp.Round), w, n, err)
n, err = WriteTo(UInt16(bp.Index), w, n, err)
n, err = WriteTo(UInt16(bp.Total), w, n, err)
n, err = WriteTo(bp.Bytes, w, n, err)
n, err = WriteTo(bp.Signature, w, n, err)
WriteUInt32(w, bp.Height, &n, &err)
WriteUInt16(w, bp.Round, &n, &err)
WriteUInt16(w, bp.Index, &n, &err)
WriteUInt16(w, bp.Total, &n, &err)
WriteByteSlice(w, bp.Bytes, &n, &err)
WriteBinary(w, bp.Signature, &n, &err)
return return
} }
@ -173,38 +174,41 @@ func (bp *BlockPart) ValidateWithSigner(signer *Account) error {
/* Header is part of a Block */ /* Header is part of a Block */
type Header struct { type Header struct {
Name String
Name string
Height uint32 Height uint32
Fees uint64 Fees uint64
Time Time
PrevHash ByteSlice
ValidationHash ByteSlice
TxsHash ByteSlice
Time time.Time
PrevHash []byte
ValidationHash []byte
TxsHash []byte
// Volatile // Volatile
hash []byte hash []byte
} }
func ReadHeader(r io.Reader) Header {
func ReadHeader(r io.Reader, n *int64, err *error) (h Header) {
if *err != nil {
return Header{}
}
return Header{ return Header{
Name: ReadString(r),
Height: Readuint32(r),
Fees: Readuint64(r),
Time: ReadTime(r),
PrevHash: ReadByteSlice(r),
ValidationHash: ReadByteSlice(r),
TxsHash: ReadByteSlice(r),
Name: ReadString(r, n, err),
Height: ReadUInt32(r, n, err),
Fees: ReadUInt64(r, n, err),
Time: ReadTime(r, n, err),
PrevHash: ReadByteSlice(r, n, err),
ValidationHash: ReadByteSlice(r, n, err),
TxsHash: ReadByteSlice(r, n, err),
} }
} }
func (h *Header) WriteTo(w io.Writer) (n int64, err error) { func (h *Header) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(h.Name, w, n, err)
n, err = WriteTo(UInt32(h.Height), w, n, err)
n, err = WriteTo(UInt64(h.Fees), w, n, err)
n, err = WriteTo(h.Time, w, n, err)
n, err = WriteTo(h.PrevHash, w, n, err)
n, err = WriteTo(h.ValidationHash, w, n, err)
n, err = WriteTo(h.TxsHash, w, n, err)
WriteString(w, h.Name, &n, &err)
WriteUInt32(w, h.Height, &n, &err)
WriteUInt64(w, h.Fees, &n, &err)
WriteTime(w, h.Time, &n, &err)
WriteByteSlice(w, h.PrevHash, &n, &err)
WriteByteSlice(w, h.ValidationHash, &n, &err)
WriteByteSlice(w, h.TxsHash, &n, &err)
return return
} }
@ -231,16 +235,16 @@ type Validation struct {
hash []byte hash []byte
} }
func ReadValidation(r io.Reader) Validation {
numSigs := Readuint32(r)
numAdjs := Readuint32(r)
func ReadValidation(r io.Reader, n *int64, err *error) Validation {
numSigs := ReadUInt32(r, n, err)
numAdjs := ReadUInt32(r, n, err)
sigs := make([]Signature, 0, numSigs) sigs := make([]Signature, 0, numSigs)
for i := uint32(0); i < numSigs; i++ { for i := uint32(0); i < numSigs; i++ {
sigs = append(sigs, ReadSignature(r))
sigs = append(sigs, ReadSignature(r, n, err))
} }
adjs := make([]Adjustment, 0, numAdjs) adjs := make([]Adjustment, 0, numAdjs)
for i := uint32(0); i < numAdjs; i++ { for i := uint32(0); i < numAdjs; i++ {
adjs = append(adjs, ReadAdjustment(r))
adjs = append(adjs, ReadAdjustment(r, n, err))
} }
return Validation{ return Validation{
Signatures: sigs, Signatures: sigs,
@ -249,13 +253,13 @@ func ReadValidation(r io.Reader) Validation {
} }
func (v *Validation) WriteTo(w io.Writer) (n int64, err error) { func (v *Validation) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(UInt32(len(v.Signatures)), w, n, err)
n, err = WriteTo(UInt32(len(v.Adjustments)), w, n, err)
WriteUInt32(w, uint32(len(v.Signatures)), &n, &err)
WriteUInt32(w, uint32(len(v.Adjustments)), &n, &err)
for _, sig := range v.Signatures { for _, sig := range v.Signatures {
n, err = WriteTo(sig, w, n, err)
WriteBinary(w, sig, &n, &err)
} }
for _, adj := range v.Adjustments { for _, adj := range v.Adjustments {
n, err = WriteTo(adj, w, n, err)
WriteBinary(w, adj, &n, &err)
} }
return return
} }
@ -282,19 +286,19 @@ type Txs struct {
hash []byte hash []byte
} }
func ReadTxs(r io.Reader) Txs {
numTxs := Readuint32(r)
func ReadTxs(r io.Reader, n *int64, err *error) Txs {
numTxs := ReadUInt32(r, n, err)
txs := make([]Tx, 0, numTxs) txs := make([]Tx, 0, numTxs)
for i := uint32(0); i < numTxs; i++ { for i := uint32(0); i < numTxs; i++ {
txs = append(txs, ReadTx(r))
txs = append(txs, ReadTx(r, n, err))
} }
return Txs{Txs: txs} return Txs{Txs: txs}
} }
func (txs *Txs) WriteTo(w io.Writer) (n int64, err error) { func (txs *Txs) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(UInt32(len(txs.Txs)), w, n, err)
WriteUInt32(w, uint32(len(txs.Txs)), &n, &err)
for _, tx := range txs.Txs { for _, tx := range txs.Txs {
n, err = WriteTo(tx, w, n, err)
WriteBinary(w, tx, &n, &err)
} }
return return
} }


+ 4
- 2
blocks/block_part_set.go View File

@ -95,7 +95,7 @@ func (bps *BlockPartSet) AddBlockPart(part *BlockPart) (added bool, err error) {
// Check for existing parts. // Check for existing parts.
existing := bps.parts[part.Index] existing := bps.parts[part.Index]
if existing != nil { if existing != nil {
if existing.Bytes.Equals(part.Bytes) {
if bytes.Equal(existing.Bytes, part.Bytes) {
// Ignore duplicate // Ignore duplicate
return false, nil return false, nil
} else { } else {
@ -127,7 +127,9 @@ func (bps *BlockPartSet) Block() *Block {
for _, part := range bps.parts { for _, part := range bps.parts {
blockBytes = append(blockBytes, part.Bytes...) blockBytes = append(blockBytes, part.Bytes...)
} }
block := ReadBlock(bytes.NewReader(blockBytes))
var n int64
var err error
block := ReadBlock(bytes.NewReader(blockBytes), &n, &err)
bps._block = block bps._block = block
} }
return bps._block return bps._block


+ 27
- 25
blocks/block_test.go View File

@ -9,7 +9,7 @@ import (
) )
// Distributed pseudo-exponentially to test for various cases // Distributed pseudo-exponentially to test for various cases
func randuint64() uint64 {
func randUInt64() uint64 {
bits := rand.Uint32() % 64 bits := rand.Uint32() % 64
if bits == 0 { if bits == 0 {
return 0 return 0
@ -19,7 +19,7 @@ func randuint64() uint64 {
return n return n
} }
func randuint32() uint32 {
func randUInt32() uint32 {
bits := rand.Uint32() % 32 bits := rand.Uint32() % 32
if bits == 0 { if bits == 0 {
return 0 return 0
@ -29,18 +29,18 @@ func randuint32() uint32 {
return n return n
} }
func randTime() Time {
return Time{time.Unix(int64(randuint64()), 0)}
func randTime() time.Time {
return time.Unix(int64(randUInt64()), 0)
} }
func randAccount() Account { func randAccount() Account {
return Account{ return Account{
Id: randuint64(),
Id: randUInt64(),
PubKey: randBytes(32), PubKey: randBytes(32),
} }
} }
func randBytes(n int) ByteSlice {
func randBytes(n int) []byte {
bs := make([]byte, n) bs := make([]byte, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
bs[i] = byte(rand.Intn(256)) bs[i] = byte(rand.Intn(256))
@ -49,7 +49,7 @@ func randBytes(n int) ByteSlice {
} }
func randSig() Signature { func randSig() Signature {
return Signature{randuint64(), randBytes(32)}
return Signature{randUInt64(), randBytes(32)}
} }
func TestBlock(t *testing.T) { func TestBlock(t *testing.T) {
@ -58,15 +58,15 @@ func TestBlock(t *testing.T) {
sendTx := &SendTx{ sendTx := &SendTx{
Signature: randSig(), Signature: randSig(),
Fee: randuint64(),
To: randuint64(),
Amount: randuint64(),
Fee: randUInt64(),
To: randUInt64(),
Amount: randUInt64(),
} }
nameTx := &NameTx{ nameTx := &NameTx{
Signature: randSig(), Signature: randSig(),
Fee: randuint64(),
Name: String(randBytes(12)),
Fee: randUInt64(),
Name: string(randBytes(12)),
PubKey: randBytes(32), PubKey: randBytes(32),
} }
@ -74,30 +74,30 @@ func TestBlock(t *testing.T) {
bond := &Bond{ bond := &Bond{
Signature: randSig(), Signature: randSig(),
Fee: randuint64(),
UnbondTo: randuint64(),
Amount: randuint64(),
Fee: randUInt64(),
UnbondTo: randUInt64(),
Amount: randUInt64(),
} }
unbond := &Unbond{ unbond := &Unbond{
Signature: randSig(), Signature: randSig(),
Fee: randuint64(),
Amount: randuint64(),
Fee: randUInt64(),
Amount: randUInt64(),
} }
timeout := &Timeout{ timeout := &Timeout{
AccountId: randuint64(),
Penalty: randuint64(),
AccountId: randUInt64(),
Penalty: randUInt64(),
} }
dupeout := &Dupeout{ dupeout := &Dupeout{
VoteA: BlockVote{ VoteA: BlockVote{
Height: randuint64(),
Height: randUInt64(),
BlockHash: randBytes(32), BlockHash: randBytes(32),
Signature: randSig(), Signature: randSig(),
}, },
VoteB: BlockVote{ VoteB: BlockVote{
Height: randuint64(),
Height: randUInt64(),
BlockHash: randBytes(32), BlockHash: randBytes(32),
Signature: randSig(), Signature: randSig(),
}, },
@ -108,8 +108,8 @@ func TestBlock(t *testing.T) {
block := &Block{ block := &Block{
Header: Header{ Header: Header{
Name: "Tendermint", Name: "Tendermint",
Height: randuint32(),
Fees: randuint64(),
Height: randUInt32(),
Fees: randUInt64(),
Time: randTime(), Time: randTime(),
PrevHash: randBytes(32), PrevHash: randBytes(32),
ValidationHash: randBytes(32), ValidationHash: randBytes(32),
@ -129,10 +129,12 @@ func TestBlock(t *testing.T) {
// Then, compare. // Then, compare.
blockBytes := BinaryBytes(block) blockBytes := BinaryBytes(block)
block2 := ReadBlock(bytes.NewReader(blockBytes))
var n int64
var err error
block2 := ReadBlock(bytes.NewReader(blockBytes), &n, &err)
blockBytes2 := BinaryBytes(block2) blockBytes2 := BinaryBytes(block2)
if !BinaryEqual(blockBytes, blockBytes2) {
if !bytes.Equal(blockBytes, blockBytes2) {
t.Fatal("Write->Read of block failed.") t.Fatal("Write->Read of block failed.")
} }
} }

+ 11
- 9
blocks/codec_test.go View File

@ -5,8 +5,8 @@ import (
"encoding/gob" "encoding/gob"
"encoding/json" "encoding/json"
"testing" "testing"
"time"
. "github.com/tendermint/tendermint/binary"
"github.com/ugorji/go/codec" "github.com/ugorji/go/codec"
"github.com/vmihailenco/msgpack" "github.com/vmihailenco/msgpack"
) )
@ -18,10 +18,10 @@ func BenchmarkTestCustom(b *testing.B) {
Name: "Header", Name: "Header",
Height: 123, Height: 123,
Fees: 123, Fees: 123,
Time: TimeFromUnix(123),
PrevHash: ByteSlice("prevhash"),
ValidationHash: ByteSlice("validationhash"),
TxsHash: ByteSlice("txshash"),
Time: time.Unix(123, 0),
PrevHash: []byte("prevhash"),
ValidationHash: []byte("validationhash"),
TxsHash: []byte("txshash"),
} }
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
@ -30,7 +30,9 @@ func BenchmarkTestCustom(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
buf.Reset() buf.Reset()
h.WriteTo(buf) h.WriteTo(buf)
h2 := ReadHeader(buf)
var n int64
var err error
h2 := ReadHeader(buf, &n, &err)
if h2.Name != "Header" { if h2.Name != "Header" {
b.Fatalf("wrong name") b.Fatalf("wrong name")
} }
@ -83,7 +85,7 @@ func BenchmarkTestGob(b *testing.B) {
Name: "Header", Name: "Header",
Height: 123, Height: 123,
Fees: 123, Fees: 123,
Time: TimeFromUnix(123),
Time: time.Unix(123, 0),
PrevHash: []byte("prevhash"), PrevHash: []byte("prevhash"),
ValidationHash: []byte("validationhash"), ValidationHash: []byte("validationhash"),
TxsHash: []byte("txshash"), TxsHash: []byte("txshash"),
@ -112,7 +114,7 @@ func BenchmarkTestMsgPack(b *testing.B) {
Name: "Header", Name: "Header",
Height: 123, Height: 123,
Fees: 123, Fees: 123,
Time: TimeFromUnix(123),
Time: time.Unix(123, 0),
PrevHash: []byte("prevhash"), PrevHash: []byte("prevhash"),
ValidationHash: []byte("validationhash"), ValidationHash: []byte("validationhash"),
TxsHash: []byte("txshash"), TxsHash: []byte("txshash"),
@ -141,7 +143,7 @@ func BenchmarkTestMsgPack2(b *testing.B) {
Name: "Header", Name: "Header",
Height: 123, Height: 123,
Fees: 123, Fees: 123,
Time: TimeFromUnix(123),
Time: time.Unix(123, 0),
PrevHash: []byte("prevhash"), PrevHash: []byte("prevhash"),
ValidationHash: []byte("validationhash"), ValidationHash: []byte("validationhash"),
TxsHash: []byte("txshash"), TxsHash: []byte("txshash"),


+ 2
- 6
blocks/store.go View File

@ -79,7 +79,8 @@ func (bs *BlockStore) LoadBlockPart(height uint32, index uint16) *BlockPart {
if partBytes == nil { if partBytes == nil {
return nil return nil
} }
return ReadBlockPart(bytes.NewReader(partBytes))
var n int64
return ReadBlockPart(bytes.NewReader(partBytes), &n, &err)
} }
// Convenience method for loading block parts and merging to a block. // Convenience method for loading block parts and merging to a block.
@ -93,11 +94,6 @@ func (bs *BlockStore) LoadBlock(height uint32) *Block {
panic("TODO: Not implemented") panic("TODO: Not implemented")
} }
func (bs *BlockStore) StageBlockAndParts(block *Block, parts []*BlockPart) error {
// XXX validate
return nil
}
// NOTE: Assumes that parts as well as the block are valid. See StageBlockParts(). // NOTE: Assumes that parts as well as the block are valid. See StageBlockParts().
// Writes are synchronous and atomic. // Writes are synchronous and atomic.
func (bs *BlockStore) SaveBlockParts(height uint32, parts []*BlockPart) error { func (bs *BlockStore) SaveBlockParts(height uint32, parts []*BlockPart) error {


+ 27
- 27
blocks/tx.go View File

@ -21,30 +21,30 @@ Tx wire format:
*/ */
type Tx interface { type Tx interface {
Type() Byte
Type() byte
Binary Binary
} }
const ( const (
TX_TYPE_SEND = Byte(0x01)
TX_TYPE_NAME = Byte(0x02)
TX_TYPE_SEND = byte(0x01)
TX_TYPE_NAME = byte(0x02)
) )
func ReadTx(r io.Reader) Tx {
switch t := ReadByte(r); t {
func ReadTx(r io.Reader, n *int64, err *error) Tx {
switch t := ReadByte(r, n, err); t {
case TX_TYPE_SEND: case TX_TYPE_SEND:
return &SendTx{ return &SendTx{
Fee: Readuint64(r),
To: Readuint64(r),
Amount: Readuint64(r),
Signature: ReadSignature(r),
Fee: ReadUInt64(r, n, err),
To: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
Signature: ReadSignature(r, n, err),
} }
case TX_TYPE_NAME: case TX_TYPE_NAME:
return &NameTx{ return &NameTx{
Fee: Readuint64(r),
Name: ReadString(r),
PubKey: ReadByteSlice(r),
Signature: ReadSignature(r),
Fee: ReadUInt64(r, n, err),
Name: ReadString(r, n, err),
PubKey: ReadByteSlice(r, n, err),
Signature: ReadSignature(r, n, err),
} }
default: default:
Panicf("Unknown Tx type %x", t) Panicf("Unknown Tx type %x", t)
@ -61,16 +61,16 @@ type SendTx struct {
Signature Signature
} }
func (self *SendTx) Type() Byte {
func (self *SendTx) Type() byte {
return TX_TYPE_SEND return TX_TYPE_SEND
} }
func (self *SendTx) WriteTo(w io.Writer) (n int64, err error) { func (self *SendTx) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(self.Type(), w, n, err)
n, err = WriteTo(UInt64(self.Fee), w, n, err)
n, err = WriteTo(UInt64(self.To), w, n, err)
n, err = WriteTo(UInt64(self.Amount), w, n, err)
n, err = WriteTo(self.Signature, w, n, err)
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.Fee, &n, &err)
WriteUInt64(w, self.To, &n, &err)
WriteUInt64(w, self.Amount, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return return
} }
@ -78,20 +78,20 @@ func (self *SendTx) WriteTo(w io.Writer) (n int64, err error) {
type NameTx struct { type NameTx struct {
Fee uint64 Fee uint64
Name String
PubKey ByteSlice
Name string
PubKey []byte
Signature Signature
} }
func (self *NameTx) Type() Byte {
func (self *NameTx) Type() byte {
return TX_TYPE_NAME return TX_TYPE_NAME
} }
func (self *NameTx) WriteTo(w io.Writer) (n int64, err error) { func (self *NameTx) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(self.Type(), w, n, err)
n, err = WriteTo(UInt64(self.Fee), w, n, err)
n, err = WriteTo(self.Name, w, n, err)
n, err = WriteTo(self.PubKey, w, n, err)
n, err = WriteTo(self.Signature, w, n, err)
WriteByte(w, self.Type(), &n, &err)
WriteUInt64(w, self.Fee, &n, &err)
WriteString(w, self.Name, &n, &err)
WriteByteSlice(w, self.PubKey, &n, &err)
WriteBinary(w, self.Signature, &n, &err)
return return
} }

+ 37
- 5
consensus/README.md View File

@ -1,16 +1,48 @@
## [ZombieValidators]
## Determining the order of proposers at height h:
The most likely scenario may be during an upgrade.
```
Determining the order of proposers at height h:
A B C All validators A, B, and C
[+10, +5, +2] (+17) Voting power
We'll see some validators that fail to upgrade while most have. Then, some updated validator will propose a block that appears invalid to the outdated validators. What is the outdated validator to do?
[ 0, 0, 0] Genesis?
[ 10, 5, 2] (+17)
A [ -7, 5, 2] (-17) Round 0 proposer: A
[ 3, 10, 4] (+17)
B [ 3, -7, 4] (-17) Round 1 proposer: B
[ 13, -2, 6] (+17)
A [ -4, -2, 6] (-17) Round 2 proposer: A
[ 6, 3, 8] (+17)
C [ 6, 3, -9] (-17) Round 3 proposer: C
[ 16, 8, -7] (+17)
A [ -1, 8, -7] (-17) Round 4 proposer: A
[ 9, 13, -5] (+17)
B [ 9, -4, -5] (-17) Round 5 proposer: B
[ 19, 1, -3] (+17)
A [ 2, 1, -3] (-17) Round 6 proposer: A
........... ...
For a node, once consensus has been reached at some round R,
the moment the node sees +2/3 in votes for a proposal is when
the consensus rounds for the *next* height h+1 begins.
The right thing to do is to stop participating, because you have no idea what is going on, and prompt the administrator to upgrade the daemon. (Now this could be a security issue if not handled properly, so in the future we should think about upgrade security best practices). Yet say you don't, and you continue to sign blocks without really participating in the consensus rounds -- maybe voting nil each time and then signing whatever is decided on. Well for one, you've lost all ability to validate any blocks. It's a problem because if there are too many of these zombies, the network might accidentally commit a bad block -- in effect, crashing the network. So, the network wants to weed the zombies out.
Round R+1 in the consensus rounds at height h+1 is the same as
round R in the consensus rounds at height h (the parent block).
We omit details of dealing with membership changes.
```
It's hard catching the zombie. It can mimick whatever other validator is doing, perhaps mimicking the first one to vote during the rounds and waiting just a little longer for the final block vote. Based on my extensive experience with zombie flicks, it appears that the best way to identify a zombie is to make it perform some action that only non-zombies would understand. That's easy! Just make each version of the protocol have a special algorithm that selects a small but sufficiently large fraction of the validator population at each block, and make them perform an action (intuitively, make them raise their hadns). Eventually, it'll become the zombie's turn to do something but it won't know what to do. Or it will act out of turn. Gotcha.
## Zombie Validators
The most likely scenario may be during an upgrade.
We'll see some validators that fail to upgrade while most have. Then, some updated validator will propose a block that appears invalid to the outdated validators. What is the outdated validator to do?
The right thing to do is to stop participating, because you have no idea what is going on, and prompt the administrator to upgrade the daemon. (Now this could be a security issue if not handled properly, so in the future we should think about upgrade security best practices). Yet say you don't, and you continue to sign blocks without really participating in the consensus rounds -- maybe voting nil each time and then signing whatever is decided on. Well for one, you've lost all ability to validate any blocks. It's a problem because if there are too many of these zombies, the network might accidentally commit a bad block -- in effect, crashing the network. So, the network wants to weed the zombies out.
It's hard catching the zombie. It can mimick whatever other validator is doing, perhaps mimicking the first one to vote during the rounds and waiting just a little longer for the final block vote. Based on my extensive experience with zombie flicks, it appears that the best way to identify a zombie is to make it perform some action that only non-zombies would understand. That's easy! Just make each version of the protocol have a special algorithm that selects a small but sufficiently large fraction of the validator population at each block, and make them perform an action (intuitively, make them raise their hadns). Eventually, it'll become the zombie's turn to do something but it won't know what to do. Or it will act out of turn. Gotcha.
The algorithm could even depend on state data, such that validators are required to keep it updated, which is a hair away from full validation. I suspect that there are more complete ways to enforce validation, but such measures may not be necessary in practice. The algorithm could even depend on state data, such that validators are required to keep it updated, which is a hair away from full validation. I suspect that there are more complete ways to enforce validation, but such measures may not be necessary in practice.


+ 96
- 81
consensus/consensus.go View File

@ -10,11 +10,11 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
. "github.com/tendermint/tendermint/accounts"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p"
. "github.com/tendermint/tendermint/state"
) )
const ( const (
@ -89,31 +89,44 @@ type ConsensusManager struct {
started uint32 started uint32
stopped uint32 stopped uint32
csc *ConsensusStateControl
blockStore *BlockStore
accountStore *AccountStore
mtx sync.Mutex
peerStates map[string]*PeerState
doActionCh chan RoundAction
cs *ConsensusState
blockStore *BlockStore
doActionCh chan RoundAction
mtx sync.Mutex
state *State
privValidator *PrivValidator
peerStates map[string]*PeerState
stagedProposal *BlockPartSet
stagedState *State
} }
func NewConsensusManager(sw *p2p.Switch, csc *ConsensusStateControl, blockStore *BlockStore, accountStore *AccountStore) *ConsensusManager {
func NewConsensusManager(sw *p2p.Switch, state *State, blockStore *BlockStore) *ConsensusManager {
swEvents := make(chan interface{}) swEvents := make(chan interface{})
sw.AddEventListener("ConsensusManager.swEvents", swEvents) sw.AddEventListener("ConsensusManager.swEvents", swEvents)
csc.Update(blockStore) // Update csc with new blocks.
cs := NewConsensusState(state)
cm := &ConsensusManager{ cm := &ConsensusManager{
sw: sw,
swEvents: swEvents,
quit: make(chan struct{}),
csc: csc,
blockStore: blockStore,
accountStore: accountStore,
peerStates: make(map[string]*PeerState),
doActionCh: make(chan RoundAction, 1),
sw: sw,
swEvents: swEvents,
quit: make(chan struct{}),
cs: cs,
blockStore: blockStore,
doActionCh: make(chan RoundAction, 1),
state: state,
peerStates: make(map[string]*PeerState),
} }
return cm return cm
} }
// Sets our private validator account for signing votes.
func (cm *ConsensusManager) SetPrivValidator(priv *PrivValidator) {
cm.mtx.Lock()
defer cm.mtx.Unlock()
cm.privValidator = priv
}
func (cm *ConsensusManager) Start() { func (cm *ConsensusManager) Start() {
if atomic.CompareAndSwapUint32(&cm.started, 0, 1) { if atomic.CompareAndSwapUint32(&cm.started, 0, 1) {
log.Info("Starting ConsensusManager") log.Info("Starting ConsensusManager")
@ -166,7 +179,7 @@ func (cm *ConsensusManager) switchEventsRoutine() {
// Like, how large is it and how often can we send it? // Like, how large is it and how often can we send it?
func (cm *ConsensusManager) makeKnownBlockPartsMessage() *KnownBlockPartsMessage { func (cm *ConsensusManager) makeKnownBlockPartsMessage() *KnownBlockPartsMessage {
rs := cm.csc.RoundState()
rs := cm.cs.RoundState()
return &KnownBlockPartsMessage{ return &KnownBlockPartsMessage{
Height: rs.Height, Height: rs.Height,
SecondsSinceStartTime: uint32(time.Now().Sub(rs.StartTime).Seconds()), SecondsSinceStartTime: uint32(time.Now().Sub(rs.StartTime).Seconds()),
@ -188,7 +201,7 @@ func (cm *ConsensusManager) gossipProposalRoutine() {
OUTER_LOOP: OUTER_LOOP:
for { for {
// Get round state // Get round state
rs := cm.csc.RoundState()
rs := cm.cs.RoundState()
// Receive incoming message on ProposalCh // Receive incoming message on ProposalCh
inMsg, ok := cm.sw.Receive(ProposalCh) inMsg, ok := cm.sw.Receive(ProposalCh)
@ -282,9 +295,8 @@ OUTER_LOOP:
// Signs a vote document and broadcasts it. // Signs a vote document and broadcasts it.
// hash can be nil to vote "nil" // hash can be nil to vote "nil"
func (cm *ConsensusManager) signAndVote(vote *Vote) error { func (cm *ConsensusManager) signAndVote(vote *Vote) error {
privValidator := cm.csc.PrivValidator()
if privValidator != nil {
err := privValidator.SignVote(vote)
if cm.privValidator != nil {
err := cm.privValidator.SignVote(vote)
if err != nil { if err != nil {
return err return err
} }
@ -294,15 +306,43 @@ func (cm *ConsensusManager) signAndVote(vote *Vote) error {
return nil return nil
} }
func (cm *ConsensusManager) isProposalValid(rs *RoundState) bool {
if !rs.BlockPartSet.IsComplete() {
return false
func (cm *ConsensusManager) stageProposal(proposal *BlockPartSet) error {
// Already staged?
cm.mtx.Lock()
if cm.stagedProposal == proposal {
cm.mtx.Unlock()
return nil
} else {
cm.mtx.Unlock()
}
// Basic validation
if !proposal.IsComplete() {
return errors.New("Incomplete proposal BlockPartSet")
}
block, blockParts := blockPartSet.Block(), blockPartSet.BlockParts()
err := block.ValidateBasic()
if err != nil {
return err
} }
err := cm.stageBlock(rs.BlockPartSet)
// Create a copy of the state for staging
cm.mtx.Lock()
stateCopy := cm.state.Copy() // Deep copy the state before staging.
cm.mtx.Unlock()
// Commit block onto the copied state.
err := stateCopy.CommitBlock(block, block.Header.Time) // NOTE: fake commit time.
if err != nil { if err != nil {
return false
return err
} }
return true
// Looks good!
cm.mtx.Lock()
cm.stagedProposal = proposal
cm.stagedState = state
cm.mtx.Unlock()
return nil
} }
func (cm *ConsensusManager) constructProposal(rs *RoundState) (*Block, error) { func (cm *ConsensusManager) constructProposal(rs *RoundState) (*Block, error) {
@ -315,7 +355,7 @@ func (cm *ConsensusManager) constructProposal(rs *RoundState) (*Block, error) {
// We may not have received a full proposal. // We may not have received a full proposal.
func (cm *ConsensusManager) voteProposal(rs *RoundState) error { func (cm *ConsensusManager) voteProposal(rs *RoundState) error {
// If we're locked, must vote that. // If we're locked, must vote that.
locked := cm.csc.LockedProposal()
locked := cm.cs.LockedProposal()
if locked != nil { if locked != nil {
block := locked.Block() block := locked.Block()
err := cm.signAndVote(&Vote{ err := cm.signAndVote(&Vote{
@ -326,9 +366,10 @@ func (cm *ConsensusManager) voteProposal(rs *RoundState) error {
}) })
return err return err
} }
// If proposal is invalid
if !cm.isProposalValid(rs) {
// Vote for nil.
// Stage proposal
err := cm.stageProposal(rs.BlockPartSet)
if err != nil {
// Vote for nil, whatever the error.
err := cm.signAndVote(&Vote{ err := cm.signAndVote(&Vote{
Height: rs.Height, Height: rs.Height,
Round: rs.Round, Round: rs.Round,
@ -361,13 +402,13 @@ func (cm *ConsensusManager) precommitProposal(rs *RoundState) error {
// If proposal is invalid or unknown, do nothing. // If proposal is invalid or unknown, do nothing.
// See note on ZombieValidators to see why. // See note on ZombieValidators to see why.
if !cm.isProposalValid(rs) {
if cm.stageProposal(rs.BlockPartSet) != nil {
return nil return nil
} }
// Lock this proposal. // Lock this proposal.
// NOTE: we're unlocking any prior locks. // NOTE: we're unlocking any prior locks.
cm.csc.LockProposal(rs.BlockPartSet)
cm.cs.LockProposal(rs.BlockPartSet)
// Send precommit vote. // Send precommit vote.
err := cm.signAndVote(&Vote{ err := cm.signAndVote(&Vote{
@ -395,11 +436,11 @@ func (cm *ConsensusManager) commitOrUnlockProposal(rs *RoundState) error {
// do not commit. // do not commit.
// TODO If we were just late to receive the block, when // TODO If we were just late to receive the block, when
// do we actually get it? Document it. // do we actually get it? Document it.
if !cm.isProposalValid(rs) {
if cm.stageProposal(rs.BlockPartSet) != nil {
return nil return nil
} }
// TODO: Remove? // TODO: Remove?
cm.csc.LockProposal(rs.BlockPartSet)
cm.cs.LockProposal(rs.BlockPartSet)
// Vote commit. // Vote commit.
err := cm.signAndVote(&Vote{ err := cm.signAndVote(&Vote{
Height: rs.Height, Height: rs.Height,
@ -416,11 +457,11 @@ func (cm *ConsensusManager) commitOrUnlockProposal(rs *RoundState) error {
// time differences between nodes, so nodes end up drifting // time differences between nodes, so nodes end up drifting
// in time. // in time.
commitTime := time.Now() commitTime := time.Now()
cm.commitBlock(rs.BlockPartSet, commitTime)
cm.commitProposal(rs.BlockPartSet, commitTime)
return nil return nil
} else { } else {
// Otherwise, if a 1/3 majority if a block that isn't our locked one exists, unlock. // Otherwise, if a 1/3 majority if a block that isn't our locked one exists, unlock.
locked := cm.csc.LockedProposal()
locked := cm.cs.LockedProposal()
if locked != nil { if locked != nil {
for _, hashOrNil := range rs.RoundPrecommits.OneThirdMajority() { for _, hashOrNil := range rs.RoundPrecommits.OneThirdMajority() {
if hashOrNil == nil { if hashOrNil == nil {
@ -429,7 +470,7 @@ func (cm *ConsensusManager) commitOrUnlockProposal(rs *RoundState) error {
hash := hashOrNil.([]byte) hash := hashOrNil.([]byte)
if !bytes.Equal(hash, locked.Block().Hash()) { if !bytes.Equal(hash, locked.Block().Hash()) {
// Unlock our lock. // Unlock our lock.
cm.csc.LockProposal(nil)
cm.cs.LockProposal(nil)
} }
} }
} }
@ -437,52 +478,27 @@ func (cm *ConsensusManager) commitOrUnlockProposal(rs *RoundState) error {
} }
} }
// After stageBlock(), a call to commitBlock() with the same arguments must succeed.
func (cm *ConsensusManager) stageBlock(blockPartSet *BlockPartSet) error {
func (cm *ConsensusManager) commitProposal(blockPartSet *BlockPartSet, commitTime time.Time) error {
cm.mtx.Lock() cm.mtx.Lock()
defer cm.mtx.Unlock() defer cm.mtx.Unlock()
block, blockParts := blockPartSet.Block(), blockPartSet.BlockParts()
err := block.ValidateBasic()
if err != nil {
return err
}
err = cm.blockStore.StageBlockAndParts(block, blockParts)
if err != nil {
return err
if cm.stagedProposal != blockPartSet {
panic("Unexpected stagedProposal.") // Shouldn't happen.
} }
err = cm.csc.StageBlock(block)
if err != nil {
return err
}
err = cm.accountStore.StageBlock(block)
if err != nil {
return err
}
// NOTE: more stores may be added here for validation.
return nil
}
// after stageBlock(), a call to commitBlock() with the same arguments must succeed.
func (cm *ConsensusManager) commitBlock(blockPartSet *BlockPartSet, commitTime time.Time) error {
cm.mtx.Lock()
defer cm.mtx.Unlock()
// Save to blockStore
block, blockParts := blockPartSet.Block(), blockPartSet.BlockParts() block, blockParts := blockPartSet.Block(), blockPartSet.BlockParts()
err := cm.blockStore.SaveBlockParts(block.Height, blockParts) err := cm.blockStore.SaveBlockParts(block.Height, blockParts)
if err != nil { if err != nil {
return err return err
} }
err = cm.csc.CommitBlock(block, commitTime)
if err != nil {
return err
}
err = cm.accountStore.CommitBlock(block)
if err != nil {
return err
}
// What was staged becomes committed.
cm.state = cm.stagedState
cm.cs.Update(cm.state)
cm.stagedProposal = nil
cm.stagedState = nil
return nil return nil
} }
@ -490,7 +506,7 @@ func (cm *ConsensusManager) gossipVoteRoutine() {
OUTER_LOOP: OUTER_LOOP:
for { for {
// Get round state // Get round state
rs := cm.csc.RoundState()
rs := cm.cs.RoundState()
// Receive incoming message on VoteCh // Receive incoming message on VoteCh
inMsg, ok := cm.sw.Receive(VoteCh) inMsg, ok := cm.sw.Receive(VoteCh)
@ -569,7 +585,7 @@ func (cm *ConsensusManager) proposeAndVoteRoutine() {
// Figure out which height/round/step we're at, // Figure out which height/round/step we're at,
// then schedule an action for when it is due. // then schedule an action for when it is due.
rs := cm.csc.RoundState()
rs := cm.cs.RoundState()
_, _, roundDuration, _, elapsedRatio := calcRoundInfo(rs.StartTime) _, _, roundDuration, _, elapsedRatio := calcRoundInfo(rs.StartTime)
switch rs.Step() { switch rs.Step() {
case RoundStepStart: case RoundStepStart:
@ -607,15 +623,14 @@ func (cm *ConsensusManager) proposeAndVoteRoutine() {
// We only consider transitioning to given step. // We only consider transitioning to given step.
step := roundAction.XnToStep step := roundAction.XnToStep
// This is the current state. // This is the current state.
rs := cm.csc.RoundState()
rs := cm.cs.RoundState()
if height != rs.Height || round != rs.Round { if height != rs.Height || round != rs.Round {
return // Not relevant. return // Not relevant.
} }
if step == RoundStepProposal && rs.Step() == RoundStepStart { if step == RoundStepProposal && rs.Step() == RoundStepStart {
// Propose a block if I am the proposer. // Propose a block if I am the proposer.
privValidator := cm.csc.PrivValidator()
if privValidator != nil && rs.Proposer.Account.Id == privValidator.Id {
if cm.privValidator != nil && rs.Proposer.Account.Id == cm.privValidator.Id {
block, err := cm.constructProposal(rs) block, err := cm.constructProposal(rs)
if err != nil { if err != nil {
log.Error("Error attempting to construct a proposal: %v", err) log.Error("Error attempting to construct a proposal: %v", err)
@ -644,7 +659,7 @@ func (cm *ConsensusManager) proposeAndVoteRoutine() {
} }
// Round is over. This is a special case. // Round is over. This is a special case.
// Prepare a new RoundState for the next state. // Prepare a new RoundState for the next state.
cm.csc.SetupRound(rs.Round + 1)
cm.cs.SetupRound(rs.Round + 1)
return // setAlarm() takes care of the rest. return // setAlarm() takes care of the rest.
} else { } else {
return // Action is not relevant. return // Action is not relevant.


+ 54
- 229
consensus/state.go View File

@ -5,288 +5,113 @@ import (
"sync" "sync"
"time" "time"
. "github.com/tendermint/tendermint/accounts"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
db_ "github.com/tendermint/tendermint/db"
. "github.com/tendermint/tendermint/state"
) )
var ( var (
consensusStateKey = []byte("consensusState") consensusStateKey = []byte("consensusState")
) )
/*
Determining the order of proposers at height h:
A B C All validators A, B, and C
[+10, +5, +2] (+17) Voting power
[ 0, 0, 0] Genesis?
[ 10, 5, 2] (+17)
A [ -7, 5, 2] (-17) Round 0 proposer: A
[ 3, 10, 4] (+17)
B [ 3, -7, 4] (-17) Round 1 proposer: B
[ 13, -2, 6] (+17)
A [ -4, -2, 6] (-17) Round 2 proposer: A
[ 6, 3, 8] (+17)
C [ 6, 3, -9] (-17) Round 3 proposer: C
[ 16, 8, -7] (+17)
A [ -1, 8, -7] (-17) Round 4 proposer: A
[ 9, 13, -5] (+17)
B [ 9, -4, -5] (-17) Round 5 proposer: B
[ 19, 1, -3] (+17)
A [ 2, 1, -3] (-17) Round 6 proposer: A
........... ...
For a node, once consensus has been reached at some round R,
the moment the node sees +2/3 in votes for a proposal is when
the consensus rounds for the *next* height h+1 begins.
Round R+1 in the consensus rounds at height h+1 is the same as
round R in the consensus rounds at height h (the parent block).
We omit details of dealing with membership changes.
*/
func getProposer(validators map[uint64]*Validator) (proposer *Validator) {
highestAccum := int64(0)
for _, validator := range validators {
if validator.Accum > highestAccum {
highestAccum = validator.Accum
proposer = validator
} else if validator.Accum == highestAccum {
if validator.Id < proposer.Id { // Seniority
proposer = validator
}
}
}
return
}
func incrementAccum(validators map[uint64]*Validator) {
totalDelta := int64(0)
for _, validator := range validators {
validator.Accum += int64(validator.VotingPower)
totalDelta += int64(validator.VotingPower)
}
proposer := getProposer(validators)
proposer.Accum -= totalDelta
// NOTE: sum(validators) here should be zero.
if true {
totalAccum := int64(0)
for _, validator := range validators {
totalAccum += validator.Accum
}
if totalAccum != 0 {
Panicf("Total Accum of validators did not equal 0. Got: ", totalAccum)
}
}
}
// Creates a deep copy of validators.
// Caller can then modify the resulting validators' .Accum field without
// modifying the original *Validator's.
func copyValidators(validators map[uint64]*Validator) map[uint64]*Validator {
mapCopy := map[uint64]*Validator{}
for _, val := range validators {
mapCopy[val.Id] = val.Copy()
}
return mapCopy
}
//-----------------------------------------------------------------------------
// Handles consensus state tracking across block heights.
// NOTE: When adding more fields, also reset it in Load() and CommitBlock()
type ConsensusStateControl struct {
// Tracks consensus state across block heights and rounds.
type ConsensusState struct {
mtx sync.Mutex mtx sync.Mutex
db db_.Db // Where we store the validators list & other data.
validatorsR0 map[uint64]*Validator // A copy of the validators at round 0
privValidator *PrivValidator // PrivValidator used to participate, if present.
accountStore *AccountStore // Account storage
height uint32 // Height we are working on. height uint32 // Height we are working on.
validatorsR0 map[uint64]*Validator // A copy of the validators at round 0
lockedProposal *BlockPartSet // A BlockPartSet of the locked proposal. lockedProposal *BlockPartSet // A BlockPartSet of the locked proposal.
startTime time.Time // Start of round 0 for this height. startTime time.Time // Start of round 0 for this height.
roundState *RoundState // The RoundState object for the current round.
commits *VoteSet // Commits for this height. commits *VoteSet // Commits for this height.
roundState *RoundState // The RoundState object for the current round.
} }
func NewConsensusStateControl(db db_.Db, accountStore *AccountStore) *ConsensusStateControl {
csc := &ConsensusStateControl{
db: db,
accountStore: accountStore,
}
csc.Load()
return csc
}
// Load the current state from db.
func (csc *ConsensusStateControl) Load() {
csc.mtx.Lock()
defer csc.mtx.Unlock()
buf := csc.db.Get(consensusStateKey)
if len(buf) == 0 {
height := uint32(0)
validators := make(map[uint64]*Validator) // XXX BOOTSTRAP
startTime := time.Now() // XXX BOOTSTRAP
csc.setupHeight(height, validators, startTime)
} else {
reader := bytes.NewReader(buf)
height := Readuint32(reader)
validators := make(map[uint64]*Validator)
startTime := ReadTime(reader)
for reader.Len() > 0 {
validator := ReadValidator(reader)
validators[validator.Id] = validator
}
csc.setupHeight(height, validators, startTime.Time)
}
}
// Save the current state onto db.
// Doesn't save the round state, just initial state at round 0.
func (csc *ConsensusStateControl) Save() {
csc.mtx.Lock()
defer csc.mtx.Unlock()
var buf bytes.Buffer
UInt32(csc.height).WriteTo(&buf)
Time{csc.startTime}.WriteTo(&buf)
for _, validator := range csc.validatorsR0 {
validator.WriteTo(&buf)
}
csc.db.Set(consensusStateKey, buf.Bytes())
}
// Finds more blocks from blockStore and commits them.
func (csc *ConsensusStateControl) Update(blockStore *BlockStore) {
csc.mtx.Lock()
defer csc.mtx.Unlock()
for h := csc.height + 1; h <= blockStore.Height(); h++ {
block := blockStore.LoadBlock(h)
// TODO: would be better to be able to override
// the block commit time, but in the meantime,
// just use the block time as proposed by the proposer.
csc.CommitBlock(block, block.Header.Time.Time)
}
}
func (csc *ConsensusStateControl) PrivValidator() *PrivValidator {
csc.mtx.Lock()
defer csc.mtx.Unlock()
return csc.privValidator
}
func (csc *ConsensusStateControl) SetPrivValidator(privValidator *PrivValidator) error {
csc.mtx.Lock()
defer csc.mtx.Unlock()
if csc.privValidator != nil {
panic("ConsensusStateControl privValidator already set.")
}
csc.privValidator = privValidator
return nil
}
// Set blockPartSet to nil to unlock.
func (csc *ConsensusStateControl) LockProposal(blockPartSet *BlockPartSet) {
csc.mtx.Lock()
defer csc.mtx.Unlock()
csc.lockedProposal = blockPartSet
func NewConsensusState(state *State) *ConsensusState {
cs := &ConsensusState{}
cs.Update(state)
return cs
} }
func (csc *ConsensusStateControl) LockedProposal() *BlockPartSet {
csc.mtx.Lock()
defer csc.mtx.Unlock()
return csc.lockedProposal
func (cs *ConsensusState) LockProposal(blockPartSet *BlockPartSet) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
cs.lockedProposal = blockPartSet
} }
func (csc *ConsensusStateControl) StageBlock(block *Block) error {
// XXX implement staging.
return nil
func (cs *ConsensusState) UnlockProposal() {
cs.mtx.Lock()
defer cs.mtx.Unlock()
cs.lockedProposal = nil
} }
// NOTE: assumes that block is valid.
// NOTE: the block should be saved on the BlockStore before commiting here.
// commitTime is usually set to the system clock time (time.Now()).
func (csc *ConsensusStateControl) CommitBlock(block *Block, commitTime time.Time) error {
csc.mtx.Lock()
defer csc.mtx.Unlock()
// Ensure that block is the next block needed.
if block.Height != csc.height {
return Errorf("Cannot commit block %v to csc. Expected height %v", block, csc.height+1)
}
// Update validator.
validators := copyValidators(csc.validatorsR0)
incrementAccum(validators)
// TODO if there are new validators in the block, add them.
// XXX: it's not commitTime we want...
csc.setupHeight(block.Height+1, validators, commitTime)
// Save the state.
csc.Save()
return nil
func (cs *ConsensusState) LockedProposal() *BlockPartSet {
cs.mtx.Lock()
defer cs.mtx.Unlock()
return cs.lockedProposal
} }
func (csc *ConsensusStateControl) RoundState() *RoundState {
csc.mtx.Lock()
defer csc.mtx.Unlock()
return csc.roundState
func (cs *ConsensusState) RoundState() *RoundState {
cs.mtx.Lock()
defer cs.mtx.Unlock()
return cs.roundState
} }
func (csc *ConsensusStateControl) setupHeight(height uint32, validators map[uint64]*Validator, startTime time.Time) {
func (cs *ConsensusState) Update(state *State) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if height > 0 && height != csc.height+1 {
panic("setupHeight() cannot skip heights")
// Sanity check state.
stateHeight := state.Height()
if stateHeight > 0 && stateHeight != cs.height+1 {
Panicf("Update() expected state height of %v but found %v", cs.height+1, stateHeight)
} }
// Reset the state for the next height.
csc.validatorsR0 = validators
csc.height = height
csc.lockedProposal = nil
csc.startTime = startTime
csc.commits = NewVoteSet(height, 0, VoteTypeCommit, validators)
// Reset fields based on state.
cs.height = stateHeight
cs.validatorsR0 = state.Validators().Copy() // NOTE: immutable.
cs.lockedProposal = nil
cs.startTime = state.commitTime // XXX is this what we want?
cs.commits = NewVoteSet(stateHeight, 0, VoteTypeCommit, cs.validatorsR0)
// Setup the roundState // Setup the roundState
csc.roundState = nil
csc.setupRound(0)
cs.roundState = nil
cs.setupRound(0)
} }
// If csc.roundSTate isn't at round, set up new roundState at round.
func (csc *ConsensusStateControl) SetupRound(round uint16) {
csc.mtx.Lock()
defer csc.mtx.Unlock()
if csc.roundState != nil && csc.roundState.Round >= round {
// If cs.roundSTate isn't at round, set up new roundState at round.
func (cs *ConsensusState) SetupRound(round uint16) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if cs.roundState != nil && cs.roundState.Round >= round {
return return
} }
csc.setupRound(round)
cs.setupRound(round)
} }
// Initialize roundState for given round. // Initialize roundState for given round.
// Involves incrementing validators for each past rand. // Involves incrementing validators for each past rand.
func (csc *ConsensusStateControl) setupRound(round uint16) {
func (cs *ConsensusState) setupRound(round uint16) {
// Increment validator accums as necessary. // Increment validator accums as necessary.
// We need to start with csc.validatorsR0 or csc.roundState.Validators
// We need to start with cs.validatorsR0 or cs.roundState.Validators
var validators map[uint64]*Validator = nil var validators map[uint64]*Validator = nil
var validatorsRound uint16 var validatorsRound uint16
if csc.roundState == nil {
if cs.roundState == nil {
// We have no roundState so we start from validatorsR0 at round 0. // We have no roundState so we start from validatorsR0 at round 0.
validators = copyValidators(csc.validatorsR0)
validators = copyValidators(cs.validatorsR0)
validatorsRound = 0 validatorsRound = 0
} else { } else {
// We have a previous roundState so we start from that. // We have a previous roundState so we start from that.
validators = copyValidators(csc.roundState.Validators)
validatorsRound = csc.roundState.Round
validators = copyValidators(cs.roundState.Validators)
validatorsRound = cs.roundState.Round
} }
// Increment all the way to round. // Increment all the way to round.
for r := validatorsRound; r < round; r++ { for r := validatorsRound; r < round; r++ {
incrementAccum(validators) incrementAccum(validators)
} }
roundState := NewRoundState(csc.height, round, csc.startTime, validators, csc.commits)
csc.roundState = roundState
roundState := NewRoundState(cs.height, round, cs.startTime, validators, cs.commits)
cs.roundState = roundState
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------


+ 0
- 66
consensus/validator.go View File

@ -1,66 +0,0 @@
package consensus
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.
// Meant to be discarded every round of the consensus protocol.
type Validator struct {
Account
BondHeight uint32
VotingPower uint64
Accum int64
}
// Used to persist the state of ConsensusStateControl.
func ReadValidator(r io.Reader) *Validator {
return &Validator{
Account: Account{
Id: Readuint64(r),
PubKey: ReadByteSlice(r),
},
BondHeight: Readuint32(r),
VotingPower: Readuint64(r),
Accum: Readint64(r),
}
}
// Creates a new copy of the validator so we can mutate accum.
func (v *Validator) Copy() *Validator {
return &Validator{
Account: v.Account,
BondHeight: v.BondHeight,
VotingPower: v.VotingPower,
Accum: v.Accum,
}
}
// 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)
return
}
//-----------------------------------------------------------------------------
// 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
}

+ 39
- 50
p2p/connection.go View File

@ -30,7 +30,7 @@ const (
/* /*
A MConnection wraps a network connection and handles buffering and multiplexing. A MConnection wraps a network connection and handles buffering and multiplexing.
Binary messages are sent with ".Send(channelId, msg)". Binary messages are sent with ".Send(channelId, msg)".
Inbound ByteSlices are pushed to the designated chan<- InboundBytes.
Inbound byteslices are pushed to the designated chan<- InboundBytes.
*/ */
type MConnection struct { type MConnection struct {
conn net.Conn conn net.Conn
@ -217,6 +217,7 @@ func (c *MConnection) sendRoutine() {
FOR_LOOP: FOR_LOOP:
for { for {
var n int64
var err error var err error
select { select {
case <-c.flushTimer.Ch: case <-c.flushTimer.Ch:
@ -228,13 +229,11 @@ FOR_LOOP:
channel.updateStats() channel.updateStats()
} }
case <-c.pingTimer.Ch: case <-c.pingTimer.Ch:
var n int64
n, err = packetTypePing.WriteTo(c.bufWriter)
WriteByte(c.bufWriter, packetTypePing, &n, &err)
c.sendMonitor.Update(int(n)) c.sendMonitor.Update(int(n))
c.flush() c.flush()
case <-c.pong: case <-c.pong:
var n int64
n, err = packetTypePong.WriteTo(c.bufWriter)
WriteByte(c.bufWriter, packetTypePong, &n, &err)
c.sendMonitor.Update(int(n)) c.sendMonitor.Update(int(n))
c.flush() c.flush()
case <-c.quit: case <-c.quit:
@ -331,7 +330,9 @@ FOR_LOOP:
c.recvMonitor.Limit(maxPacketSize, atomic.LoadInt64(&c.recvRate), true) c.recvMonitor.Limit(maxPacketSize, atomic.LoadInt64(&c.recvRate), true)
// Read packet type // Read packet type
pktType, n, err := ReadUInt8Safe(c.bufReader)
var n int64
var err error
pktType := ReadByte(c.bufReader, &n, &err)
c.recvMonitor.Update(int(n)) c.recvMonitor.Update(int(n))
if err != nil { if err != nil {
if atomic.LoadUint32(&c.stopped) != 1 { if atomic.LoadUint32(&c.stopped) != 1 {
@ -409,10 +410,10 @@ type Channel struct {
desc *ChannelDescriptor desc *ChannelDescriptor
id byte id byte
recvQueue chan InboundBytes recvQueue chan InboundBytes
sendQueue chan ByteSlice
sendQueue chan []byte
sendQueueSize uint32 sendQueueSize uint32
recving ByteSlice
sending ByteSlice
recving []byte
sending []byte
priority uint priority uint
recentlySent int64 // exponential moving average recentlySent int64 // exponential moving average
} }
@ -426,7 +427,7 @@ func newChannel(conn *MConnection, desc *ChannelDescriptor) *Channel {
desc: desc, desc: desc,
id: desc.Id, id: desc.Id,
recvQueue: desc.recvQueue, recvQueue: desc.recvQueue,
sendQueue: make(chan ByteSlice, desc.SendQueueCapacity),
sendQueue: make(chan []byte, desc.SendQueueCapacity),
recving: make([]byte, 0, desc.RecvBufferSize), recving: make([]byte, 0, desc.RecvBufferSize),
priority: desc.DefaultPriority, priority: desc.DefaultPriority,
} }
@ -434,7 +435,7 @@ func newChannel(conn *MConnection, desc *ChannelDescriptor) *Channel {
// Queues message to send to this channel. // Queues message to send to this channel.
// Goroutine-safe // Goroutine-safe
func (ch *Channel) sendBytes(bytes ByteSlice) {
func (ch *Channel) sendBytes(bytes []byte) {
ch.sendQueue <- bytes ch.sendQueue <- bytes
atomic.AddUint32(&ch.sendQueueSize, 1) atomic.AddUint32(&ch.sendQueueSize, 1)
} }
@ -442,7 +443,7 @@ func (ch *Channel) sendBytes(bytes ByteSlice) {
// Queues message to send to this channel. // Queues message to send to this channel.
// Nonblocking, returns true if successful. // Nonblocking, returns true if successful.
// Goroutine-safe // Goroutine-safe
func (ch *Channel) trySendBytes(bytes ByteSlice) bool {
func (ch *Channel) trySendBytes(bytes []byte) bool {
select { select {
case ch.sendQueue <- bytes: case ch.sendQueue <- bytes:
atomic.AddUint32(&ch.sendQueueSize, 1) atomic.AddUint32(&ch.sendQueueSize, 1)
@ -480,14 +481,14 @@ func (ch *Channel) sendPending() bool {
// Not goroutine-safe // Not goroutine-safe
func (ch *Channel) nextPacket() packet { func (ch *Channel) nextPacket() packet {
packet := packet{} packet := packet{}
packet.ChannelId = Byte(ch.id)
packet.ChannelId = byte(ch.id)
packet.Bytes = ch.sending[:MinInt(maxPacketSize, len(ch.sending))] packet.Bytes = ch.sending[:MinInt(maxPacketSize, len(ch.sending))]
if len(ch.sending) <= maxPacketSize { if len(ch.sending) <= maxPacketSize {
packet.EOF = Byte(0x01)
packet.EOF = byte(0x01)
ch.sending = nil ch.sending = nil
atomic.AddUint32(&ch.sendQueueSize, ^uint32(0)) // decrement sendQueueSize atomic.AddUint32(&ch.sendQueueSize, ^uint32(0)) // decrement sendQueueSize
} else { } else {
packet.EOF = Byte(0x00)
packet.EOF = byte(0x00)
ch.sending = ch.sending[MinInt(maxPacketSize, len(ch.sending)):] ch.sending = ch.sending[MinInt(maxPacketSize, len(ch.sending)):]
} }
return packet return packet
@ -497,8 +498,8 @@ func (ch *Channel) nextPacket() packet {
// Not goroutine-safe // Not goroutine-safe
func (ch *Channel) writePacketTo(w io.Writer) (n int64, err error) { func (ch *Channel) writePacketTo(w io.Writer) (n int64, err error) {
packet := ch.nextPacket() packet := ch.nextPacket()
n, err = WriteTo(packetTypeMessage, w, n, err)
n, err = WriteTo(packet, w, n, err)
WriteByte(w, packetTypeMessage, &n, &err)
WriteBinary(w, packet, &n, &err)
if err != nil { if err != nil {
ch.recentlySent += n ch.recentlySent += n
} }
@ -509,7 +510,7 @@ func (ch *Channel) writePacketTo(w io.Writer) (n int64, err error) {
// Not goroutine-safe // Not goroutine-safe
func (ch *Channel) recvPacket(pkt packet) { func (ch *Channel) recvPacket(pkt packet) {
ch.recving = append(ch.recving, pkt.Bytes...) ch.recving = append(ch.recving, pkt.Bytes...)
if pkt.EOF == Byte(0x01) {
if pkt.EOF == byte(0x01) {
ch.recvQueue <- InboundBytes{ch.conn, ch.recving} ch.recvQueue <- InboundBytes{ch.conn, ch.recving}
ch.recving = make([]byte, 0, ch.desc.RecvBufferSize) ch.recving = make([]byte, 0, ch.desc.RecvBufferSize)
} }
@ -527,22 +528,22 @@ func (ch *Channel) updateStats() {
const ( const (
maxPacketSize = 1024 maxPacketSize = 1024
packetTypePing = UInt8(0x00)
packetTypePong = UInt8(0x01)
packetTypeMessage = UInt8(0x10)
packetTypePing = byte(0x00)
packetTypePong = byte(0x01)
packetTypeMessage = byte(0x10)
) )
// Messages in channels are chopped into smaller packets for multiplexing. // Messages in channels are chopped into smaller packets for multiplexing.
type packet struct { type packet struct {
ChannelId Byte
EOF Byte // 1 means message ends here.
Bytes ByteSlice
ChannelId byte
EOF byte // 1 means message ends here.
Bytes []byte
} }
func (p packet) WriteTo(w io.Writer) (n int64, err error) { func (p packet) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(p.ChannelId, w, n, err)
n, err = WriteTo(p.EOF, w, n, err)
n, err = WriteTo(p.Bytes, w, n, err)
WriteByte(w, p.ChannelId, &n, &err)
WriteByte(w, p.EOF, &n, &err)
WriteByteSlice(w, p.Bytes, &n, &err)
return return
} }
@ -551,44 +552,32 @@ func (p packet) String() string {
} }
func readPacketSafe(r io.Reader) (pkt packet, n int64, err error) { func readPacketSafe(r io.Reader) (pkt packet, n int64, err error) {
chId, n_, err := ReadByteSafe(r)
n += n_
if err != nil {
return
}
eof, n_, err := ReadByteSafe(r)
n += n_
if err != nil {
return
}
// TODO: packet length sanity check.
bytes, n_, err := ReadByteSliceSafe(r)
n += n_
if err != nil {
return
}
return packet{chId, eof, bytes}, n, nil
chId := ReadByte(r, &n, &err)
eof := ReadByte(r, &n, &err)
bytes := ReadByteSlice(r, &n, &err)
pkt = packet{chId, eof, bytes}
return
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type InboundBytes struct { type InboundBytes struct {
MConn *MConnection MConn *MConnection
Bytes ByteSlice
Bytes []byte
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Convenience struct for writing typed messages. // Convenience struct for writing typed messages.
// Reading requires a custom decoder that switches on the first type byte of a ByteSlice.
// Reading requires a custom decoder that switches on the first type byte of a byteslice.
type TypedMessage struct { type TypedMessage struct {
Type Byte
Type byte
Msg Binary Msg Binary
} }
func (tm TypedMessage) WriteTo(w io.Writer) (n int64, err error) { func (tm TypedMessage) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(tm.Type, w, n, err)
n, err = WriteTo(tm.Msg, w, n, err)
WriteByte(w, tm.Type, &n, &err)
WriteBinary(w, tm.Msg, &n, &err)
return return
} }
@ -596,6 +585,6 @@ func (tm TypedMessage) String() string {
return fmt.Sprintf("<%X:%v>", tm.Type, tm.Msg) return fmt.Sprintf("<%X:%v>", tm.Type, tm.Msg)
} }
func (tm TypedMessage) Bytes() ByteSlice {
func (tm TypedMessage) Bytes() []byte {
return BinaryBytes(tm) return BinaryBytes(tm)
} }

+ 2
- 3
p2p/listener.go View File

@ -6,7 +6,6 @@ import (
"strconv" "strconv"
"sync/atomic" "sync/atomic"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/p2p/upnp" "github.com/tendermint/tendermint/p2p/upnp"
) )
@ -159,7 +158,7 @@ func getUPNPExternalAddress(externalPort, internalPort int) *NetAddress {
} }
log.Debug("Got UPNP external address: %v", ext) log.Debug("Got UPNP external address: %v", ext)
return NewNetAddressIPPort(ext, UInt16(externalPort))
return NewNetAddressIPPort(ext, uint16(externalPort))
} }
// TODO: use syscalls: http://pastebin.com/9exZG4rh // TODO: use syscalls: http://pastebin.com/9exZG4rh
@ -178,7 +177,7 @@ func getNaiveExternalAddress(port int) *NetAddress {
if v4 == nil || v4[0] == 127 { if v4 == nil || v4[0] == 127 {
continue continue
} // loopback } // loopback
return NewNetAddressIPPort(ipnet.IP, UInt16(port))
return NewNetAddressIPPort(ipnet.IP, uint16(port))
} }
return nil return nil
} }

+ 9
- 9
p2p/netaddress.go View File

@ -18,7 +18,7 @@ import (
type NetAddress struct { type NetAddress struct {
IP net.IP IP net.IP
Port UInt16
Port uint16
str string str string
} }
@ -29,7 +29,7 @@ func NewNetAddress(addr net.Addr) *NetAddress {
Panicf("Only TCPAddrs are supported. Got: %v", addr) Panicf("Only TCPAddrs are supported. Got: %v", addr)
} }
ip := tcpAddr.IP ip := tcpAddr.IP
port := UInt16(tcpAddr.Port)
port := uint16(tcpAddr.Port)
return NewNetAddressIPPort(ip, port) return NewNetAddressIPPort(ip, port)
} }
@ -43,17 +43,17 @@ func NewNetAddressString(addr string) *NetAddress {
if err != nil { if err != nil {
panic(err) panic(err)
} }
na := NewNetAddressIPPort(ip, UInt16(port))
na := NewNetAddressIPPort(ip, uint16(port))
return na return na
} }
func ReadNetAddress(r io.Reader) *NetAddress {
ipBytes := ReadByteSlice(r)
port := ReadUInt16(r)
func ReadNetAddress(r io.Reader, n *int64, err *error) *NetAddress {
ipBytes := ReadByteSlice(r, n, err)
port := ReadUInt16(r, n, err)
return NewNetAddressIPPort(net.IP(ipBytes), port) return NewNetAddressIPPort(net.IP(ipBytes), port)
} }
func NewNetAddressIPPort(ip net.IP, port UInt16) *NetAddress {
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
na := &NetAddress{ na := &NetAddress{
IP: ip, IP: ip,
Port: port, Port: port,
@ -66,8 +66,8 @@ func NewNetAddressIPPort(ip net.IP, port UInt16) *NetAddress {
} }
func (na *NetAddress) WriteTo(w io.Writer) (n int64, err error) { func (na *NetAddress) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(ByteSlice(na.IP.To16()), w, n, err)
n, err = WriteTo(na.Port, w, n, err)
WriteByteSlice(w, na.IP.To16(), &n, &err)
WriteUInt16(w, na.Port, &n, &err)
return return
} }


+ 2
- 1
p2p/peer.go View File

@ -77,7 +77,8 @@ func (p *Peer) CanSend(chId byte) bool {
} }
func (p *Peer) WriteTo(w io.Writer) (n int64, err error) { func (p *Peer) WriteTo(w io.Writer) (n int64, err error) {
return String(p.Key).WriteTo(w)
WriteString(w, p.Key, &n, &err)
return
} }
func (p *Peer) String() string { func (p *Peer) String() string {


+ 15
- 13
p2p/peer_manager.go View File

@ -210,19 +210,21 @@ func (pm *PeerManager) requestRoutine() {
/* Messages */ /* Messages */
const ( const (
msgTypeUnknown = Byte(0x00)
msgTypeRequest = Byte(0x01)
msgTypeAddrs = Byte(0x02)
msgTypeUnknown = byte(0x00)
msgTypeRequest = byte(0x01)
msgTypeAddrs = byte(0x02)
) )
// TODO: check for unnecessary extra bytes at the end. // TODO: check for unnecessary extra bytes at the end.
func decodeMessage(bz ByteSlice) (msg interface{}) {
func decodeMessage(bz []byte) (msg interface{}) {
var n int64
var err error
// log.Debug("decoding msg bytes: %X", bz) // log.Debug("decoding msg bytes: %X", bz)
switch Byte(bz[0]) {
switch bz[0] {
case msgTypeRequest: case msgTypeRequest:
return &pexRequestMessage{} return &pexRequestMessage{}
case msgTypeAddrs: case msgTypeAddrs:
return readPexAddrsMessage(bytes.NewReader(bz[1:]))
return readPexAddrsMessage(bytes.NewReader(bz[1:]), &n, &err)
default: default:
return nil return nil
} }
@ -235,7 +237,7 @@ type pexRequestMessage struct {
} }
func (m *pexRequestMessage) WriteTo(w io.Writer) (n int64, err error) { func (m *pexRequestMessage) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(msgTypeRequest, w, n, err)
WriteByte(w, msgTypeRequest, &n, &err)
return return
} }
@ -250,11 +252,11 @@ type pexAddrsMessage struct {
Addrs []*NetAddress Addrs []*NetAddress
} }
func readPexAddrsMessage(r io.Reader) *pexAddrsMessage {
numAddrs := int(ReadUInt32(r))
func readPexAddrsMessage(r io.Reader, n *int64, err *error) *pexAddrsMessage {
numAddrs := int(ReadUInt32(r, n, err))
addrs := []*NetAddress{} addrs := []*NetAddress{}
for i := 0; i < numAddrs; i++ { for i := 0; i < numAddrs; i++ {
addr := ReadNetAddress(r)
addr := ReadNetAddress(r, n, err)
addrs = append(addrs, addr) addrs = append(addrs, addr)
} }
return &pexAddrsMessage{ return &pexAddrsMessage{
@ -263,10 +265,10 @@ func readPexAddrsMessage(r io.Reader) *pexAddrsMessage {
} }
func (m *pexAddrsMessage) WriteTo(w io.Writer) (n int64, err error) { func (m *pexAddrsMessage) WriteTo(w io.Writer) (n int64, err error) {
n, err = WriteTo(msgTypeAddrs, w, n, err)
n, err = WriteTo(UInt32(len(m.Addrs)), w, n, err)
WriteByte(w, msgTypeAddrs, &n, &err)
WriteUInt32(w, uint32(len(m.Addrs)), &n, &err)
for _, addr := range m.Addrs { for _, addr := range m.Addrs {
n, err = WriteTo(addr, w, n, err)
WriteBinary(w, addr, &n, &err)
} }
return return
} }


+ 13
- 2
p2p/switch_test.go View File

@ -1,13 +1,22 @@
package p2p package p2p
import ( import (
"bytes"
"encoding/hex" "encoding/hex"
"io"
"testing" "testing"
"time" "time"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
) )
type String string
func (s String) WriteTo(w io.Writer) (n int64, err error) {
WriteString(w, string(s), &n, &err)
return
}
// convenience method for creating two switches connected to each other. // convenience method for creating two switches connected to each other.
func makeSwitchPair(t testing.TB, numChannels int, sendQueueCapacity int, recvBufferSize int, recvQueueCapacity int) (*Switch, *Switch, []*ChannelDescriptor) { func makeSwitchPair(t testing.TB, numChannels int, sendQueueCapacity int, recvBufferSize int, recvQueueCapacity int) (*Switch, *Switch, []*ChannelDescriptor) {
@ -86,10 +95,12 @@ func TestSwitches(t *testing.T) {
// Receive message from channel 1 and check // Receive message from channel 1 and check
inMsg, ok := s2.Receive(byte(0x01)) inMsg, ok := s2.Receive(byte(0x01))
var n int64
var err error
if !ok { if !ok {
t.Errorf("Failed to receive from channel one") t.Errorf("Failed to receive from channel one")
} }
if ReadString(inMsg.Bytes.Reader()) != "channel one" {
if ReadString(bytes.NewBuffer(inMsg.Bytes), &n, &err) != "channel one" {
t.Errorf("Unexpected received message bytes:\n%v", hex.Dump(inMsg.Bytes)) t.Errorf("Unexpected received message bytes:\n%v", hex.Dump(inMsg.Bytes))
} }
@ -98,7 +109,7 @@ func TestSwitches(t *testing.T) {
if !ok { if !ok {
t.Errorf("Failed to receive from channel zero") t.Errorf("Failed to receive from channel zero")
} }
if ReadString(inMsg.Bytes.Reader()) != "channel zero" {
if ReadString(bytes.NewBuffer(inMsg.Bytes), &n, &err) != "channel zero" {
t.Errorf("Unexpected received message bytes:\n%v", hex.Dump(inMsg.Bytes)) t.Errorf("Unexpected received message bytes:\n%v", hex.Dump(inMsg.Bytes))
} }
} }


+ 127
- 0
state/state.go View 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)
}

+ 16
- 16
state/validator.go View File

@ -5,7 +5,7 @@ import (
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/blocks"
//. "github.com/tendermint/tendermint/common"
. "github.com/tendermint/tendermint/common"
db_ "github.com/tendermint/tendermint/db" db_ "github.com/tendermint/tendermint/db"
) )
@ -20,15 +20,15 @@ type Validator struct {
} }
// Used to persist the state of ConsensusStateControl. // 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{ return &Validator{
Account: Account{ 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. // Used to persist the state of ConsensusStateControl.
func (v *Validator) WriteTo(w io.Writer) (n int64, err error) { 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 return
} }
@ -78,7 +78,7 @@ func NewValidatorSet(validators map[uint64]*Validator) *ValidatorSet {
validators = make(map[uint64]*Validator) validators = make(map[uint64]*Validator)
} }
return &ValidatorSet{ return &ValidatorSet{
valdiators: validators,
validators: validators,
} }
} }
@ -104,7 +104,7 @@ func (v *ValidatorSet) IncrementAccum() {
func (v *ValidatorSet) Copy() *ValidatorSet { func (v *ValidatorSet) Copy() *ValidatorSet {
mapCopy := map[uint64]*Validator{} mapCopy := map[uint64]*Validator{}
for _, val := range validators {
for _, val := range v.validators {
mapCopy[val.Id] = val.Copy() mapCopy[val.Id] = val.Copy()
} }
return &ValidatorSet{ 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 v.validators[validator.Id] = validator
} }
func (v *ValidatorSet) Get(id uint64) *Validator { func (v *ValidatorSet) Get(id uint64) *Validator {
return v.validators[validator.Id]
return v.validators[id]
} }
func (v *ValidatorSet) Map() map[uint64]*Validator { func (v *ValidatorSet) Map() map[uint64]*Validator {


consensus/vote.go → state/vote.go View File


Loading…
Cancel
Save