Refactor Tx, Validator, and Account structure

This commit is contained in:
Jae Kwon
2014-12-09 18:49:04 -08:00
parent 4424a85fbd
commit 83d313cbe5
56 changed files with 1922 additions and 2027 deletions

19
account/account.go Normal file
View File

@ -0,0 +1,19 @@
package account
import (
"bytes"
"io"
)
type Signable interface {
WriteSignBytes(w io.Writer, n *int64, err *error)
}
func SignBytes(o Signable) []byte {
buf, n, err := new(bytes.Buffer), new(int64), new(error)
o.WriteSignBytes(buf, n, err)
if *err != nil {
panic(err)
}
return buf.Bytes()
}

62
account/privkey.go Normal file
View File

@ -0,0 +1,62 @@
package account
import (
"errors"
"io"
"reflect"
"github.com/tendermint/go-ed25519"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
)
/*
Each account has an PrivKey which determines access to funds of the account.
Transaction inputs include Signatures of the account's associated PrivKey.
*/
type PrivKey interface {
Sign(msg []byte) Signature
}
const (
PrivKeyTypeEd25519 = byte(0x01)
)
func PrivKeyDecoder(r io.Reader, n *int64, err *error) interface{} {
switch t := ReadByte(r, n, err); t {
case PrivKeyTypeEd25519:
return ReadBinary(&PrivKeyEd25519{}, r, n, err)
default:
*err = Errorf("Unknown PrivKey type %X", t)
return nil
}
}
var _ = RegisterType(&TypeInfo{
Type: reflect.TypeOf((*PrivKey)(nil)).Elem(),
Decoder: PrivKeyDecoder,
})
//-------------------------------------
type PrivKeyEd25519 struct {
PubKey []byte
PrivKey []byte
}
func (key PrivKeyEd25519) TypeByte() byte { return PrivKeyTypeEd25519 }
func (key PrivKeyEd25519) ValidateBasic() error {
if len(key.PubKey) != ed25519.PublicKeySize {
return errors.New("Invalid PrivKeyEd25519 pubkey size")
}
if len(key.PrivKey) != ed25519.PrivateKeySize {
return errors.New("Invalid PrivKeyEd25519 privkey size")
}
return nil
}
func (key PrivKeyEd25519) Sign(msg []byte) Signature {
signature := ed25519.SignMessage(msg, key.PrivKey, key.PubKey)
return SignatureEd25519{signature}
}

87
account/pubkey.go Normal file
View File

@ -0,0 +1,87 @@
package account
import (
"errors"
"io"
"reflect"
"github.com/tendermint/go-ed25519"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
)
/*
Each account has an PubKey which determines access to funds of the account.
Transaction inputs include Signatures of the account's associated PubKey.
*/
type PubKey interface {
Address() []byte
VerifyBytes(msg []byte, sig Signature) bool
}
const (
PubKeyTypeUnknown = byte(0x00)
PubKeyTypeEd25519 = byte(0x01)
)
func PubKeyDecoder(r io.Reader, n *int64, err *error) interface{} {
switch t := ReadByte(r, n, err); t {
case PubKeyTypeUnknown:
return PubKeyUnknown{}
case PubKeyTypeEd25519:
return ReadBinary(&PubKeyEd25519{}, r, n, err)
default:
*err = Errorf("Unknown PubKey type %X", t)
return nil
}
}
var _ = RegisterType(&TypeInfo{
Type: reflect.TypeOf((*PubKey)(nil)).Elem(),
Decoder: PubKeyDecoder,
})
//-------------------------------------
type PubKeyUnknown struct {
}
func (key PubKeyUnknown) TypeByte() byte { return PubKeyTypeUnknown }
func (key PubKeyUnknown) Address() []byte {
panic("PubKeyUnknown has no address")
}
func (key PubKeyUnknown) VerifyBytes(msg []byte, sig_ Signature) bool {
panic("PubKeyUnknown cannot verify messages")
}
//-------------------------------------
type PubKeyEd25519 struct {
PubKey []byte
}
func (key PubKeyEd25519) TypeByte() byte { return PubKeyTypeEd25519 }
func (key PubKeyEd25519) Address() []byte { return BinaryRipemd160(key.PubKey) }
func (key PubKeyEd25519) ValidateBasic() error {
if len(key.PubKey) != ed25519.PublicKeySize {
return errors.New("Invalid PubKeyEd25519 key size")
}
return nil
}
func (key PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool {
sig, ok := sig_.(SignatureEd25519)
if !ok {
panic("PubKeyEd25519 expects an SignatureEd25519 signature")
}
v1 := &ed25519.Verify{
Message: msg,
PubKey: key.PubKey,
Signature: []byte(sig.Bytes),
}
return ed25519.VerifyBatch([]*ed25519.Verify{v1})
}

52
account/signature.go Normal file
View File

@ -0,0 +1,52 @@
package account
import (
"errors"
"io"
"reflect"
"github.com/tendermint/go-ed25519"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
)
type Signature interface {
}
const (
SignatureTypeEd25519 = byte(0x01)
)
func SignatureDecoder(r io.Reader, n *int64, err *error) interface{} {
switch t := ReadByte(r, n, err); t {
case SignatureTypeEd25519:
return ReadBinary(&SignatureEd25519{}, r, n, err)
default:
*err = Errorf("Unknown Signature type %X", t)
return nil
}
}
var _ = RegisterType(&TypeInfo{
Type: reflect.TypeOf((*Signature)(nil)).Elem(),
Decoder: SignatureDecoder,
})
//-------------------------------------
type SignatureEd25519 struct {
Bytes []byte
}
func (sig SignatureEd25519) TypeByte() byte { return SignatureTypeEd25519 }
func (sig SignatureEd25519) ValidateBasic() error {
if len(sig.Bytes) != ed25519.SignatureSize {
return errors.New("Invalid SignatureEd25519 signature size")
}
return nil
}
func (sig SignatureEd25519) IsZero() bool {
return len(sig.Bytes) == 0
}

View File

@ -1,4 +1,4 @@
package state package account
import ( import (
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"

View File

@ -1,23 +1,31 @@
package binary package binary
import "io" import (
"io"
"reflect"
)
type Binary interface { func ReadBinary(o interface{}, r io.Reader, n *int64, err *error) interface{} {
WriteTo(w io.Writer) (int64, error) rv, rt := reflect.ValueOf(o), reflect.TypeOf(o)
if rv.Kind() == reflect.Ptr {
readReflect(rv.Elem(), rt.Elem(), r, n, err)
return o
} else {
ptrRv := reflect.New(rt)
readReflect(ptrRv.Elem(), rt, r, n, err)
return ptrRv.Elem()
}
} }
func WriteBinary(w io.Writer, b Binary, n *int64, err *error) { func WriteBinary(o interface{}, w io.Writer, n *int64, err *error) {
if *err != nil { rv := reflect.ValueOf(o)
return rt := reflect.TypeOf(o)
} writeReflect(rv, rt, w, n, err)
n_, err_ := b.WriteTo(w)
*n += int64(n_)
*err = err_
} }
// Write all of bz to w // Write all of bz to w
// Increment n and set err accordingly. // Increment n and set err accordingly.
func WriteTo(w io.Writer, bz []byte, n *int64, err *error) { func WriteTo(bz []byte, w io.Writer, n *int64, err *error) {
if *err != nil { if *err != nil {
return return
} }
@ -28,7 +36,7 @@ func WriteTo(w io.Writer, bz []byte, n *int64, err *error) {
// Read len(buf) from r // Read len(buf) from r
// Increment n and set err accordingly. // Increment n and set err accordingly.
func ReadFull(r io.Reader, buf []byte, n *int64, err *error) { func ReadFull(buf []byte, r io.Reader, n *int64, err *error) {
if *err != nil { if *err != nil {
return return
} }

View File

@ -4,27 +4,27 @@ import (
"io" "io"
) )
func WriteByteSlice(w io.Writer, bz []byte, n *int64, err *error) { func WriteByteSlice(bz []byte, w io.Writer, n *int64, err *error) {
WriteUInt32(w, uint32(len(bz)), n, err) WriteUVarInt(uint(len(bz)), w, n, err)
WriteTo(w, bz, n, err) WriteTo(bz, w, n, err)
} }
func ReadByteSlice(r io.Reader, n *int64, err *error) []byte { func ReadByteSlice(r io.Reader, n *int64, err *error) []byte {
length := ReadUInt32(r, n, err) length := ReadUVarInt(r, n, err)
if *err != nil { if *err != nil {
return nil return nil
} }
buf := make([]byte, int(length)) buf := make([]byte, int(length))
ReadFull(r, buf, n, err) ReadFull(buf, r, n, err)
return buf return buf
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func WriteByteSlices(w io.Writer, bzz [][]byte, n *int64, err *error) { func WriteByteSlices(bzz [][]byte, w io.Writer, n *int64, err *error) {
WriteUInt32(w, uint32(len(bzz)), n, err) WriteUVarInt(uint(len(bzz)), w, n, err)
for _, bz := range bzz { for _, bz := range bzz {
WriteByteSlice(w, bz, n, err) WriteByteSlice(bz, w, n, err)
if *err != nil { if *err != nil {
return return
} }
@ -32,12 +32,12 @@ func WriteByteSlices(w io.Writer, bzz [][]byte, n *int64, err *error) {
} }
func ReadByteSlices(r io.Reader, n *int64, err *error) [][]byte { func ReadByteSlices(r io.Reader, n *int64, err *error) [][]byte {
length := ReadUInt32(r, n, err) length := ReadUVarInt(r, n, err)
if *err != nil { if *err != nil {
return nil return nil
} }
bzz := make([][]byte, length) bzz := make([][]byte, length)
for i := uint32(0); i < length; i++ { for i := uint(0); i < length; i++ {
bz := ReadByteSlice(r, n, err) bz := ReadByteSlice(r, n, err)
if *err != nil { if *err != nil {
return nil return nil

View File

@ -6,10 +6,14 @@ import (
"time" "time"
) )
type Codec interface { type Encoder func(o interface{}, w io.Writer, n *int64, err *error)
Encode(o interface{}, w io.Writer, n *int64, err *error) type Decoder func(r io.Reader, n *int64, err *error) interface{}
Decode(r io.Reader, n *int64, err *error) interface{} type Comparator func(o1 interface{}, o2 interface{}) int
Compare(o1 interface{}, o2 interface{}) int
type Codec struct {
Encode Encoder
Decode Decoder
Compare Comparator
} }
const ( const (
@ -29,62 +33,58 @@ const (
typeTime = byte(0x20) typeTime = byte(0x20)
) )
var BasicCodec = basicCodec{} func BasicCodecEncoder(o interface{}, w io.Writer, n *int64, err *error) {
type basicCodec struct{}
func (bc basicCodec) Encode(o interface{}, w io.Writer, n *int64, err *error) {
switch o.(type) { switch o.(type) {
case nil: case nil:
panic("nil type unsupported") panic("nil type unsupported")
case byte: case byte:
WriteByte(w, typeByte, n, err) WriteByte(typeByte, w, n, err)
WriteByte(w, o.(byte), n, err) WriteByte(o.(byte), w, n, err)
case int8: case int8:
WriteByte(w, typeInt8, n, err) WriteByte(typeInt8, w, n, err)
WriteInt8(w, o.(int8), n, err) WriteInt8(o.(int8), w, n, err)
//case uint8: //case uint8:
// WriteByte(w, typeUInt8, n, err) // WriteByte( typeUInt8, w, n, err)
// WriteUInt8(w, o.(uint8), n, err) // WriteUInt8( o.(uint8), w, n, err)
case int16: case int16:
WriteByte(w, typeInt16, n, err) WriteByte(typeInt16, w, n, err)
WriteInt16(w, o.(int16), n, err) WriteInt16(o.(int16), w, n, err)
case uint16: case uint16:
WriteByte(w, typeUInt16, n, err) WriteByte(typeUInt16, w, n, err)
WriteUInt16(w, o.(uint16), n, err) WriteUInt16(o.(uint16), w, n, err)
case int32: case int32:
WriteByte(w, typeInt32, n, err) WriteByte(typeInt32, w, n, err)
WriteInt32(w, o.(int32), n, err) WriteInt32(o.(int32), w, n, err)
case uint32: case uint32:
WriteByte(w, typeUInt32, n, err) WriteByte(typeUInt32, w, n, err)
WriteUInt32(w, o.(uint32), n, err) WriteUInt32(o.(uint32), w, n, err)
case int64: case int64:
WriteByte(w, typeInt64, n, err) WriteByte(typeInt64, w, n, err)
WriteInt64(w, o.(int64), n, err) WriteInt64(o.(int64), w, n, err)
case uint64: case uint64:
WriteByte(w, typeUInt64, n, err) WriteByte(typeUInt64, w, n, err)
WriteUInt64(w, o.(uint64), n, err) WriteUInt64(o.(uint64), w, n, err)
case int: case int:
WriteByte(w, typeVarInt, n, err) WriteByte(typeVarInt, w, n, err)
WriteVarInt(w, o.(int), n, err) WriteVarInt(o.(int), w, n, err)
case uint: case uint:
WriteByte(w, typeUVarInt, n, err) WriteByte(typeUVarInt, w, n, err)
WriteUVarInt(w, o.(uint), n, err) WriteUVarInt(o.(uint), w, n, err)
case string: case string:
WriteByte(w, typeString, n, err) WriteByte(typeString, w, n, err)
WriteString(w, o.(string), n, err) WriteString(o.(string), w, n, err)
case []byte: case []byte:
WriteByte(w, typeByteSlice, n, err) WriteByte(typeByteSlice, w, n, err)
WriteByteSlice(w, o.([]byte), n, err) WriteByteSlice(o.([]byte), w, n, err)
case time.Time: case time.Time:
WriteByte(w, typeTime, n, err) WriteByte(typeTime, w, n, err)
WriteTime(w, o.(time.Time), n, err) WriteTime(o.(time.Time), w, n, err)
default: default:
panic("Unsupported type") panic("Unsupported type")
} }
} }
func (bc basicCodec) Decode(r io.Reader, n *int64, err *error) (o interface{}) { func BasicCodecDecoder(r io.Reader, n *int64, err *error) (o interface{}) {
type_ := ReadByte(r, n, err) type_ := ReadByte(r, n, err)
switch type_ { switch type_ {
case typeByte: case typeByte:
@ -121,7 +121,7 @@ func (bc basicCodec) Decode(r io.Reader, n *int64, err *error) (o interface{}) {
return o return o
} }
func (bc basicCodec) Compare(o1 interface{}, o2 interface{}) int { func BasicCodecComparator(o1 interface{}, o2 interface{}) int {
switch o1.(type) { switch o1.(type) {
case byte: case byte:
return int(o1.(byte) - o2.(byte)) return int(o1.(byte) - o2.(byte))
@ -154,3 +154,9 @@ func (bc basicCodec) Compare(o1 interface{}, o2 interface{}) int {
panic("Unsupported type") panic("Unsupported type")
} }
} }
var BasicCodec = Codec{
Encode: BasicCodecEncoder,
Decode: BasicCodecDecoder,
Compare: BasicCodecComparator,
}

View File

@ -8,20 +8,20 @@ import (
// Byte // Byte
func WriteByte(w io.Writer, b byte, n *int64, err *error) { func WriteByte(b byte, w io.Writer, n *int64, err *error) {
WriteTo(w, []byte{b}, n, err) WriteTo([]byte{b}, w, n, err)
} }
func ReadByte(r io.Reader, n *int64, err *error) byte { func ReadByte(r io.Reader, n *int64, err *error) byte {
buf := make([]byte, 1) buf := make([]byte, 1)
ReadFull(r, buf, n, err) ReadFull(buf, r, n, err)
return buf[0] return buf[0]
} }
// Int8 // Int8
func WriteInt8(w io.Writer, i int8, n *int64, err *error) { func WriteInt8(i int8, w io.Writer, n *int64, err *error) {
WriteByte(w, byte(i), n, err) WriteByte(byte(i), w, n, err)
} }
func ReadInt8(r io.Reader, n *int64, err *error) int8 { func ReadInt8(r io.Reader, n *int64, err *error) int8 {
@ -30,8 +30,8 @@ func ReadInt8(r io.Reader, n *int64, err *error) int8 {
// UInt8 // UInt8
func WriteUInt8(w io.Writer, i uint8, n *int64, err *error) { func WriteUInt8(i uint8, w io.Writer, n *int64, err *error) {
WriteByte(w, byte(i), n, err) WriteByte(byte(i), w, n, err)
} }
func ReadUInt8(r io.Reader, n *int64, err *error) uint8 { func ReadUInt8(r io.Reader, n *int64, err *error) uint8 {
@ -40,40 +40,40 @@ func ReadUInt8(r io.Reader, n *int64, err *error) uint8 {
// Int16 // Int16
func WriteInt16(w io.Writer, i int16, n *int64, err *error) { func WriteInt16(i int16, w io.Writer, n *int64, err *error) {
buf := make([]byte, 2) buf := make([]byte, 2)
binary.LittleEndian.PutUint16(buf, uint16(i)) binary.LittleEndian.PutUint16(buf, uint16(i))
*n += 2 *n += 2
WriteTo(w, buf, n, err) WriteTo(buf, w, n, err)
} }
func ReadInt16(r io.Reader, n *int64, err *error) int16 { func ReadInt16(r io.Reader, n *int64, err *error) int16 {
buf := make([]byte, 2) buf := make([]byte, 2)
ReadFull(r, buf, n, err) ReadFull(buf, r, n, err)
return int16(binary.LittleEndian.Uint16(buf)) return int16(binary.LittleEndian.Uint16(buf))
} }
// UInt16 // UInt16
func WriteUInt16(w io.Writer, i uint16, n *int64, err *error) { func WriteUInt16(i uint16, w io.Writer, n *int64, err *error) {
buf := make([]byte, 2) buf := make([]byte, 2)
binary.LittleEndian.PutUint16(buf, uint16(i)) binary.LittleEndian.PutUint16(buf, uint16(i))
*n += 2 *n += 2
WriteTo(w, buf, n, err) WriteTo(buf, w, n, err)
} }
func ReadUInt16(r io.Reader, n *int64, err *error) uint16 { func ReadUInt16(r io.Reader, n *int64, err *error) uint16 {
buf := make([]byte, 2) buf := make([]byte, 2)
ReadFull(r, buf, n, err) ReadFull(buf, r, n, err)
return uint16(binary.LittleEndian.Uint16(buf)) return uint16(binary.LittleEndian.Uint16(buf))
} }
// []UInt16 // []UInt16
func WriteUInt16s(w io.Writer, iz []uint16, n *int64, err *error) { func WriteUInt16s(iz []uint16, w io.Writer, n *int64, err *error) {
WriteUInt32(w, uint32(len(iz)), n, err) WriteUInt32(uint32(len(iz)), w, n, err)
for _, i := range iz { for _, i := range iz {
WriteUInt16(w, i, n, err) WriteUInt16(i, w, n, err)
if *err != nil { if *err != nil {
return return
} }
@ -98,71 +98,71 @@ func ReadUInt16s(r io.Reader, n *int64, err *error) []uint16 {
// Int32 // Int32
func WriteInt32(w io.Writer, i int32, n *int64, err *error) { func WriteInt32(i int32, w io.Writer, n *int64, err *error) {
buf := make([]byte, 4) buf := make([]byte, 4)
binary.LittleEndian.PutUint32(buf, uint32(i)) binary.LittleEndian.PutUint32(buf, uint32(i))
*n += 4 *n += 4
WriteTo(w, buf, n, err) WriteTo(buf, w, n, err)
} }
func ReadInt32(r io.Reader, n *int64, err *error) int32 { func ReadInt32(r io.Reader, n *int64, err *error) int32 {
buf := make([]byte, 4) buf := make([]byte, 4)
ReadFull(r, buf, n, err) ReadFull(buf, r, n, err)
return int32(binary.LittleEndian.Uint32(buf)) return int32(binary.LittleEndian.Uint32(buf))
} }
// UInt32 // UInt32
func WriteUInt32(w io.Writer, i uint32, n *int64, err *error) { func WriteUInt32(i uint32, w io.Writer, n *int64, err *error) {
buf := make([]byte, 4) buf := make([]byte, 4)
binary.LittleEndian.PutUint32(buf, uint32(i)) binary.LittleEndian.PutUint32(buf, uint32(i))
*n += 4 *n += 4
WriteTo(w, buf, n, err) WriteTo(buf, w, n, err)
} }
func ReadUInt32(r io.Reader, n *int64, err *error) uint32 { func ReadUInt32(r io.Reader, n *int64, err *error) uint32 {
buf := make([]byte, 4) buf := make([]byte, 4)
ReadFull(r, buf, n, err) ReadFull(buf, r, n, err)
return uint32(binary.LittleEndian.Uint32(buf)) return uint32(binary.LittleEndian.Uint32(buf))
} }
// Int64 // Int64
func WriteInt64(w io.Writer, i int64, n *int64, err *error) { func WriteInt64(i int64, w io.Writer, n *int64, err *error) {
buf := make([]byte, 8) buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, uint64(i)) binary.LittleEndian.PutUint64(buf, uint64(i))
*n += 8 *n += 8
WriteTo(w, buf, n, err) WriteTo(buf, w, n, err)
} }
func ReadInt64(r io.Reader, n *int64, err *error) int64 { func ReadInt64(r io.Reader, n *int64, err *error) int64 {
buf := make([]byte, 8) buf := make([]byte, 8)
ReadFull(r, buf, n, err) ReadFull(buf, r, n, err)
return int64(binary.LittleEndian.Uint64(buf)) return int64(binary.LittleEndian.Uint64(buf))
} }
// UInt64 // UInt64
func WriteUInt64(w io.Writer, i uint64, n *int64, err *error) { func WriteUInt64(i uint64, w io.Writer, n *int64, err *error) {
buf := make([]byte, 8) buf := make([]byte, 8)
binary.LittleEndian.PutUint64(buf, uint64(i)) binary.LittleEndian.PutUint64(buf, uint64(i))
*n += 8 *n += 8
WriteTo(w, buf, n, err) WriteTo(buf, w, n, err)
} }
func ReadUInt64(r io.Reader, n *int64, err *error) uint64 { func ReadUInt64(r io.Reader, n *int64, err *error) uint64 {
buf := make([]byte, 8) buf := make([]byte, 8)
ReadFull(r, buf, n, err) ReadFull(buf, r, n, err)
return uint64(binary.LittleEndian.Uint64(buf)) return uint64(binary.LittleEndian.Uint64(buf))
} }
// VarInt // VarInt
func WriteVarInt(w io.Writer, i int, n *int64, err *error) { func WriteVarInt(i int, w io.Writer, n *int64, err *error) {
buf := make([]byte, 9) buf := make([]byte, 9)
n_ := int64(binary.PutVarint(buf, int64(i))) n_ := int64(binary.PutVarint(buf, int64(i)))
*n += n_ *n += n_
WriteTo(w, buf[:n_], n, err) WriteTo(buf[:n_], w, n, err)
} }
func ReadVarInt(r io.Reader, n *int64, err *error) int { func ReadVarInt(r io.Reader, n *int64, err *error) int {
@ -174,11 +174,11 @@ func ReadVarInt(r io.Reader, n *int64, err *error) int {
// UVarInt // UVarInt
func WriteUVarInt(w io.Writer, i uint, n *int64, err *error) { func WriteUVarInt(i uint, w io.Writer, n *int64, err *error) {
buf := make([]byte, 9) buf := make([]byte, 9)
n_ := int64(binary.PutUvarint(buf, uint64(i))) n_ := int64(binary.PutUvarint(buf, uint64(i)))
*n += n_ *n += n_
WriteTo(w, buf[:n_], n, err) WriteTo(buf[:n_], w, n, err)
} }
func ReadUVarInt(r io.Reader, n *int64, err *error) uint { func ReadUVarInt(r io.Reader, n *int64, err *error) uint {

198
binary/reflect.go Normal file
View File

@ -0,0 +1,198 @@
package binary
import (
"fmt"
"io"
"reflect"
)
type TypeInfo struct {
Type reflect.Type // The type
Encoder Encoder // Optional custom encoder function
Decoder Decoder // Optional custom decoder function
}
// If a type implements TypeByte, the byte is included
// as the first byte for encoding. This is used to encode
// interfaces/union types. In this case the decoding should
// be done manually with a switch statement, and so the
// reflection-based decoder provided here does not expect this
// prefix byte.
// See the reactor implementations for use-cases.
type HasTypeByte interface {
TypeByte() byte
}
var typeInfos = map[reflect.Type]*TypeInfo{}
func RegisterType(info *TypeInfo) bool {
// Register the type info
typeInfos[info.Type] = info
// Also register the underlying struct's info, if info.Type is a pointer.
if info.Type.Kind() == reflect.Ptr {
rt := info.Type.Elem()
typeInfos[rt] = info
}
return true
}
func readReflect(rv reflect.Value, rt reflect.Type, r io.Reader, n *int64, err *error) {
// First, create a new struct if rv is nil pointer.
if rt.Kind() == reflect.Ptr && rv.IsNil() {
newRv := reflect.New(rt.Elem())
rv.Set(newRv)
rv = newRv
}
// Dereference pointer
// Still addressable, thus settable!
if rv.Kind() == reflect.Ptr {
rv, rt = rv.Elem(), rt.Elem()
}
// Custom decoder
typeInfo := typeInfos[rt]
if typeInfo != nil && typeInfo.Decoder != nil {
decoded := typeInfo.Decoder(r, n, err)
decodedRv := reflect.Indirect(reflect.ValueOf(decoded))
rv.Set(decodedRv)
return
}
switch rt.Kind() {
case reflect.Slice:
elemRt := rt.Elem()
if elemRt.Kind() == reflect.Uint8 {
// Special case: Byteslices
byteslice := ReadByteSlice(r, n, err)
rv.Set(reflect.ValueOf(byteslice))
} else {
// Read length
length := int(ReadUVarInt(r, n, err))
sliceRv := reflect.MakeSlice(rt, length, length)
// Read elems
for i := 0; i < length; i++ {
elemRv := sliceRv.Index(i)
readReflect(elemRv, elemRt, r, n, err)
}
rv.Set(sliceRv)
}
case reflect.Struct:
numFields := rt.NumField()
for i := 0; i < numFields; i++ {
field := rt.Field(i)
if field.Anonymous {
continue
}
fieldRv := rv.Field(i)
readReflect(fieldRv, field.Type, r, n, err)
}
case reflect.String:
str := ReadString(r, n, err)
rv.SetString(str)
case reflect.Uint64:
num := ReadUInt64(r, n, err)
rv.SetUint(uint64(num))
case reflect.Uint32:
num := ReadUInt32(r, n, err)
rv.SetUint(uint64(num))
case reflect.Uint16:
num := ReadUInt16(r, n, err)
rv.SetUint(uint64(num))
case reflect.Uint8:
num := ReadUInt8(r, n, err)
rv.SetUint(uint64(num))
case reflect.Uint:
num := ReadUVarInt(r, n, err)
rv.SetUint(uint64(num))
default:
panic(fmt.Sprintf("Unknown field type %v", rt.Kind()))
}
}
func writeReflect(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64, err *error) {
// Custom encoder
typeInfo := typeInfos[rt]
if typeInfo != nil && typeInfo.Encoder != nil {
typeInfo.Encoder(rv.Interface(), w, n, err)
return
}
// Dereference pointer or interface
if rt.Kind() == reflect.Ptr {
rt = rt.Elem()
rv = rv.Elem()
} else if rt.Kind() == reflect.Interface {
rv = rv.Elem()
rt = rv.Type()
}
// Write TypeByte prefix
if rt.Implements(reflect.TypeOf((*HasTypeByte)(nil)).Elem()) {
WriteByte(rv.Interface().(HasTypeByte).TypeByte(), w, n, err)
}
switch rt.Kind() {
case reflect.Slice:
elemRt := rt.Elem()
if elemRt.Kind() == reflect.Uint8 {
// Special case: Byteslices
byteslice := rv.Interface().([]byte)
WriteByteSlice(byteslice, w, n, err)
} else {
// Write length
length := rv.Len()
WriteUVarInt(uint(length), w, n, err)
// Write elems
for i := 0; i < length; i++ {
elemRv := rv.Index(i)
writeReflect(elemRv, elemRt, w, n, err)
}
}
case reflect.Struct:
numFields := rt.NumField()
for i := 0; i < numFields; i++ {
field := rt.Field(i)
if field.Anonymous {
continue
}
fieldRv := rv.Field(i)
writeReflect(fieldRv, field.Type, w, n, err)
}
case reflect.String:
WriteString(rv.String(), w, n, err)
case reflect.Uint64:
WriteUInt64(rv.Uint(), w, n, err)
case reflect.Uint32:
WriteUInt32(uint32(rv.Uint()), w, n, err)
case reflect.Uint16:
WriteUInt16(uint16(rv.Uint()), w, n, err)
case reflect.Uint8:
WriteUInt8(uint8(rv.Uint()), w, n, err)
case reflect.Uint:
WriteUVarInt(uint(rv.Uint()), w, n, err)
default:
panic(fmt.Sprintf("Unknown field type %v", rt.Kind()))
}
}

View File

@ -4,9 +4,9 @@ import "io"
// String // String
func WriteString(w io.Writer, s string, n *int64, err *error) { func WriteString(s string, w io.Writer, n *int64, err *error) {
WriteUInt32(w, uint32(len(s)), n, err) WriteUInt32(uint32(len(s)), w, n, err)
WriteTo(w, []byte(s), n, err) WriteTo([]byte(s), w, n, err)
} }
func ReadString(r io.Reader, n *int64, err *error) string { func ReadString(r io.Reader, n *int64, err *error) string {
@ -15,6 +15,6 @@ func ReadString(r io.Reader, n *int64, err *error) string {
return "" return ""
} }
buf := make([]byte, int(length)) buf := make([]byte, int(length))
ReadFull(r, buf, n, err) ReadFull(buf, r, n, err)
return string(buf) return string(buf)
} }

View File

@ -7,8 +7,8 @@ import (
// Time // Time
func WriteTime(w io.Writer, t time.Time, n *int64, err *error) { func WriteTime(t time.Time, w io.Writer, n *int64, err *error) {
WriteInt64(w, t.UnixNano(), n, err) WriteInt64(t.UnixNano(), w, n, err)
} }
func ReadTime(r io.Reader, n *int64, err *error) time.Time { func ReadTime(r io.Reader, n *int64, err *error) time.Time {

View File

@ -2,36 +2,46 @@ package binary
import ( import (
"bytes" "bytes"
"code.google.com/p/go.crypto/ripemd160"
"crypto/sha256" "crypto/sha256"
) )
func BinaryBytes(b Binary) []byte { func BinaryBytes(o interface{}) []byte {
buf := bytes.NewBuffer(nil) w, n, err := new(bytes.Buffer), new(int64), new(error)
_, err := b.WriteTo(buf) WriteBinary(o, w, n, err)
if err != nil { if *err != nil {
panic(err) panic(err)
} }
return buf.Bytes() return w.Bytes()
} }
// NOTE: does not care about the type, only the binary representation. // NOTE: does not care about the type, only the binary representation.
func BinaryEqual(a, b Binary) bool { func BinaryEqual(a, b interface{}) bool {
aBytes := BinaryBytes(a) aBytes := BinaryBytes(a)
bBytes := BinaryBytes(b) bBytes := BinaryBytes(b)
return bytes.Equal(aBytes, bBytes) return bytes.Equal(aBytes, bBytes)
} }
// NOTE: does not care about the type, only the binary representation. // NOTE: does not care about the type, only the binary representation.
func BinaryCompare(a, b Binary) int { func BinaryCompare(a, b interface{}) int {
aBytes := BinaryBytes(a) aBytes := BinaryBytes(a)
bBytes := BinaryBytes(b) bBytes := BinaryBytes(b)
return bytes.Compare(aBytes, bBytes) return bytes.Compare(aBytes, bBytes)
} }
func BinaryHash(b Binary) []byte { func BinarySha256(o interface{}) []byte {
hasher := sha256.New() hasher, n, err := sha256.New(), new(int64), new(error)
_, err := b.WriteTo(hasher) WriteBinary(o, hasher, n, err)
if err != nil { if *err != nil {
panic(err)
}
return hasher.Sum(nil)
}
func BinaryRipemd160(o interface{}) []byte {
hasher, n, err := ripemd160.New(), new(int64), new(error)
WriteBinary(o, hasher, n, err)
if *err != nil {
panic(err) panic(err)
} }
return hasher.Sum(nil) return hasher.Sum(nil)

View File

@ -5,10 +5,10 @@ import (
"crypto/sha256" "crypto/sha256"
"errors" "errors"
"fmt" "fmt"
"io"
"strings" "strings"
"time" "time"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
. "github.com/tendermint/tendermint/config" . "github.com/tendermint/tendermint/config"
@ -24,23 +24,8 @@ type Block struct {
hash []byte hash []byte
} }
func ReadBlock(r io.Reader, n *int64, err *error) *Block {
return &Block{
Header: ReadHeader(r, n, err),
Validation: ReadValidation(r, n, err),
Data: ReadData(r, n, err),
}
}
func (b *Block) WriteTo(w io.Writer) (n int64, err error) {
WriteBinary(w, b.Header, &n, &err)
WriteBinary(w, b.Validation, &n, &err)
WriteBinary(w, b.Data, &n, &err)
return
}
// Basic validation that doesn't involve state data. // Basic validation that doesn't involve state data.
func (b *Block) ValidateBasic(lastBlockHeight uint32, lastBlockHash []byte, func (b *Block) ValidateBasic(lastBlockHeight uint, lastBlockHash []byte,
lastBlockParts PartSetHeader, lastBlockTime time.Time) error { lastBlockParts PartSetHeader, lastBlockTime time.Time) error {
if b.Network != Config.Network { if b.Network != Config.Network {
return errors.New("Invalid block network") return errors.New("Invalid block network")
@ -115,7 +100,7 @@ func (b *Block) Description() string {
type Header struct { type Header struct {
Network string Network string
Height uint32 Height uint
Time time.Time Time time.Time
Fees uint64 Fees uint64
LastBlockHash []byte LastBlockHash []byte
@ -126,37 +111,11 @@ type Header struct {
hash []byte hash []byte
} }
func ReadHeader(r io.Reader, n *int64, err *error) *Header {
if *err != nil {
return nil
}
return &Header{
Network: ReadString(r, n, err),
Height: ReadUInt32(r, n, err),
Time: ReadTime(r, n, err),
Fees: ReadUInt64(r, n, err),
LastBlockHash: ReadByteSlice(r, n, err),
LastBlockParts: ReadPartSetHeader(r, n, err),
StateHash: ReadByteSlice(r, n, err),
}
}
func (h *Header) WriteTo(w io.Writer) (n int64, err error) {
WriteString(w, h.Network, &n, &err)
WriteUInt32(w, h.Height, &n, &err)
WriteTime(w, h.Time, &n, &err)
WriteUInt64(w, h.Fees, &n, &err)
WriteByteSlice(w, h.LastBlockHash, &n, &err)
WriteBinary(w, h.LastBlockParts, &n, &err)
WriteByteSlice(w, h.StateHash, &n, &err)
return
}
func (h *Header) Hash() []byte { func (h *Header) Hash() []byte {
if h.hash == nil { if h.hash == nil {
hasher := sha256.New() hasher, n, err := sha256.New(), new(int64), new(error)
_, err := h.WriteTo(hasher) WriteBinary(h, hasher, n, err)
if err != nil { if *err != nil {
panic(err) panic(err)
} }
h.hash = hasher.Sum(nil) h.hash = hasher.Sum(nil)
@ -186,30 +145,35 @@ func (h *Header) StringWithIndent(indent string) string {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type Commit struct {
// It's not strictly needed here, but consider adding address here for convenience
Round uint
Signature SignatureEd25519
}
func (commit Commit) IsZero() bool {
return commit.Round == 0 && commit.Signature.IsZero()
}
func (commit Commit) String() string {
return fmt.Sprintf("Commit{R:%v %X}", commit.Round, Fingerprint(commit.Signature.Bytes))
}
//-------------------------------------
type Validation struct { type Validation struct {
Commits []RoundSignature Commits []Commit
// Volatile // Volatile
hash []byte hash []byte
bitArray BitArray bitArray BitArray
} }
func ReadValidation(r io.Reader, n *int64, err *error) *Validation {
return &Validation{
Commits: ReadRoundSignatures(r, n, err),
}
}
func (v *Validation) WriteTo(w io.Writer) (n int64, err error) {
WriteRoundSignatures(w, v.Commits, &n, &err)
return
}
func (v *Validation) Hash() []byte { func (v *Validation) Hash() []byte {
if v.hash == nil { if v.hash == nil {
bs := make([]Binary, len(v.Commits)) bs := make([]interface{}, len(v.Commits))
for i, commit := range v.Commits { for i, commit := range v.Commits {
bs[i] = Binary(commit) bs[i] = commit
} }
v.hash = merkle.HashFromBinaries(bs) v.hash = merkle.HashFromBinaries(bs)
} }
@ -231,8 +195,8 @@ func (v *Validation) StringWithIndent(indent string) string {
func (v *Validation) BitArray() BitArray { func (v *Validation) BitArray() BitArray {
if v.bitArray.IsZero() { if v.bitArray.IsZero() {
v.bitArray = NewBitArray(uint(len(v.Commits))) v.bitArray = NewBitArray(uint(len(v.Commits)))
for i, rsig := range v.Commits { for i, commit := range v.Commits {
v.bitArray.SetIndex(uint(i), !rsig.IsZero()) v.bitArray.SetIndex(uint(i), !commit.IsZero())
} }
} }
return v.bitArray return v.bitArray
@ -247,28 +211,11 @@ type Data struct {
hash []byte hash []byte
} }
func ReadData(r io.Reader, n *int64, err *error) *Data {
numTxs := ReadUInt32(r, n, err)
txs := make([]Tx, 0, numTxs)
for i := uint32(0); i < numTxs; i++ {
txs = append(txs, ReadTx(r, n, err))
}
return &Data{Txs: txs}
}
func (data *Data) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt32(w, uint32(len(data.Txs)), &n, &err)
for _, tx := range data.Txs {
WriteBinary(w, tx, &n, &err)
}
return
}
func (data *Data) Hash() []byte { func (data *Data) Hash() []byte {
if data.hash == nil { if data.hash == nil {
bs := make([]Binary, len(data.Txs)) bs := make([]interface{}, len(data.Txs))
for i, tx := range data.Txs { for i, tx := range data.Txs {
bs[i] = Binary(tx) bs[i] = tx
} }
data.hash = merkle.HashFromBinaries(bs) data.hash = merkle.HashFromBinaries(bs)
} }
@ -278,7 +225,7 @@ func (data *Data) Hash() []byte {
func (data *Data) StringWithIndent(indent string) string { func (data *Data) StringWithIndent(indent string) string {
txStrings := make([]string, len(data.Txs)) txStrings := make([]string, len(data.Txs))
for i, tx := range data.Txs { for i, tx := range data.Txs {
txStrings[i] = tx.String() txStrings[i] = fmt.Sprintf("Tx:%v", tx)
} }
return fmt.Sprintf(`Data{ return fmt.Sprintf(`Data{
%s %v %s %v

View File

@ -1,126 +0,0 @@
package blocks
import (
"bytes"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
"testing"
)
func randSig() Signature {
return Signature{RandUInt64Exp(), RandBytes(32)}
}
func randRoundSig() RoundSignature {
return RoundSignature{RandUInt16(), randSig()}
}
func randBaseTx() BaseTx {
return BaseTx{0, RandUInt64Exp(), randSig()}
}
func randBlock() *Block {
// Account Txs
sendTx := &SendTx{
BaseTx: randBaseTx(),
To: RandUInt64Exp(),
Amount: RandUInt64Exp(),
}
nameTx := &NameTx{
BaseTx: randBaseTx(),
Name: string(RandBytes(12)),
PubKey: RandBytes(32),
}
// Validation Txs
bondTx := &BondTx{
BaseTx: randBaseTx(),
//UnbondTo: RandUInt64Exp(),
}
unbondTx := &UnbondTx{
BaseTx: randBaseTx(),
}
dupeoutTx := &DupeoutTx{
VoteA: Vote{
Height: RandUInt32Exp(),
Round: RandUInt16Exp(),
Type: VoteTypePrevote,
BlockHash: RandBytes(32),
Signature: randSig(),
},
VoteB: Vote{
Height: RandUInt32Exp(),
Round: RandUInt16Exp(),
Type: VoteTypePrevote,
BlockHash: RandBytes(32),
Signature: randSig(),
},
}
// Block
block := &Block{
Header: &Header{
Network: "Tendermint",
Height: RandUInt32Exp(),
Fees: RandUInt64Exp(),
Time: RandTime(),
LastBlockHash: RandBytes(32),
StateHash: RandBytes(32),
},
Validation: &Validation{
Commits: []RoundSignature{randRoundSig(), randRoundSig()},
},
Data: &Data{
Txs: []Tx{sendTx, nameTx, bondTx, unbondTx, dupeoutTx},
},
}
return block
}
func TestBlock(t *testing.T) {
block := randBlock()
// Mutate the block and ensure that the hash changed.
lastHash := block.Hash()
expectChange := func(mutateFn func(b *Block), message string) {
// mutate block
mutateFn(block)
// nuke hashes
block.hash = nil
block.Header.hash = nil
block.Validation.hash = nil
block.Data.hash = nil
// compare
if bytes.Equal(lastHash, block.Hash()) {
t.Error(message)
} else {
lastHash = block.Hash()
}
}
expectChange(func(b *Block) { b.Header.Network = "blah" }, "Expected hash to depend on Network")
expectChange(func(b *Block) { b.Header.Height += 1 }, "Expected hash to depend on Height")
expectChange(func(b *Block) { b.Header.Fees += 1 }, "Expected hash to depend on Fees")
expectChange(func(b *Block) { b.Header.Time = RandTime() }, "Expected hash to depend on Time")
expectChange(func(b *Block) { b.Header.LastBlockHash = RandBytes(32) }, "Expected hash to depend on LastBlockHash")
expectChange(func(b *Block) { b.Header.StateHash = RandBytes(32) }, "Expected hash to depend on StateHash")
expectChange(func(b *Block) { b.Validation.Commits[0].Round += 1 }, "Expected hash to depend on Validation Commit")
expectChange(func(b *Block) { b.Validation.Commits[0].SignerId += 1 }, "Expected hash to depend on Validation Commit")
expectChange(func(b *Block) { b.Validation.Commits[0].Bytes = RandBytes(32) }, "Expected hash to depend on Validation Commit")
expectChange(func(b *Block) { b.Data.Txs[0].(*SendTx).Signature.SignerId += 1 }, "Expected hash to depend on tx Signature")
expectChange(func(b *Block) { b.Data.Txs[0].(*SendTx).Amount += 1 }, "Expected hash to depend on send tx Amount")
// Write the block, read it in again, check hash.
block1 := randBlock()
block1Bytes := BinaryBytes(block1)
var n int64
var err error
block2 := ReadBlock(bytes.NewReader(block1Bytes), &n, &err)
if err != nil {
t.Errorf("Reading block failed: %v", err)
}
if !bytes.Equal(block1.Hash(), block2.Hash()) {
t.Errorf("Expected write/read to preserve original hash")
t.Logf("\nBlock1:\n%v", block1)
t.Logf("\nBlock2:\n%v", block2)
}
}

View File

@ -1,162 +0,0 @@
package blocks
import (
"bytes"
"encoding/gob"
"encoding/json"
"testing"
"time"
"github.com/ugorji/go/codec"
"github.com/vmihailenco/msgpack"
)
func BenchmarkTestCustom(b *testing.B) {
b.StopTimer()
h := &Header{
Network: "Header",
Height: 123,
Fees: 123,
Time: time.Unix(123, 0),
LastBlockHash: []byte("prevhash"),
StateHash: []byte("statehash"),
}
buf := bytes.NewBuffer(nil)
b.StartTimer()
for i := 0; i < b.N; i++ {
buf.Reset()
h.WriteTo(buf)
var n int64
var err error
h2 := ReadHeader(buf, &n, &err)
if h2.Network != "Header" {
b.Fatalf("wrong name")
}
}
}
type HHeader struct {
Network string `json:"N"`
Height uint64 `json:"H"`
Fees uint64 `json:"F"`
Time uint64 `json:"T"`
LastBlockHash []byte `json:"PH"`
StateHash []byte `json:"SH"`
}
func BenchmarkTestJSON(b *testing.B) {
b.StopTimer()
h := &HHeader{
Network: "Header",
Height: 123,
Fees: 123,
Time: 123,
LastBlockHash: []byte("prevhash"),
StateHash: []byte("statehash"),
}
h2 := &HHeader{}
buf := bytes.NewBuffer(nil)
enc := json.NewEncoder(buf)
dec := json.NewDecoder(buf)
b.StartTimer()
for i := 0; i < b.N; i++ {
buf.Reset()
enc.Encode(h)
dec.Decode(h2)
if h2.Network != "Header" {
b.Fatalf("wrong name")
}
}
}
func BenchmarkTestGob(b *testing.B) {
b.StopTimer()
h := &Header{
Network: "Header",
Height: 123,
Fees: 123,
Time: time.Unix(123, 0),
LastBlockHash: []byte("prevhash"),
StateHash: []byte("statehash"),
}
h2 := &Header{}
buf := bytes.NewBuffer(nil)
enc := gob.NewEncoder(buf)
dec := gob.NewDecoder(buf)
b.StartTimer()
for i := 0; i < b.N; i++ {
buf.Reset()
enc.Encode(h)
dec.Decode(h2)
if h2.Network != "Header" {
b.Fatalf("wrong name")
}
}
}
func BenchmarkTestMsgPack(b *testing.B) {
b.StopTimer()
h := &Header{
Network: "Header",
Height: 123,
Fees: 123,
Time: time.Unix(123, 0),
LastBlockHash: []byte("prevhash"),
StateHash: []byte("statehash"),
}
h2 := &Header{}
buf := bytes.NewBuffer(nil)
enc := msgpack.NewEncoder(buf)
dec := msgpack.NewDecoder(buf)
b.StartTimer()
for i := 0; i < b.N; i++ {
buf.Reset()
enc.Encode(h)
dec.Decode(h2)
if h2.Network != "Header" {
b.Fatalf("wrong name")
}
}
}
func BenchmarkTestMsgPack2(b *testing.B) {
b.StopTimer()
h := &Header{
Network: "Header",
Height: 123,
Fees: 123,
Time: time.Unix(123, 0),
LastBlockHash: []byte("prevhash"),
StateHash: []byte("statehash"),
}
h2 := &Header{}
var mh codec.MsgpackHandle
handle := &mh
buf := bytes.NewBuffer(nil)
enc := codec.NewEncoder(buf, handle)
dec := codec.NewDecoder(buf, handle)
b.StartTimer()
for i := 0; i < b.N; i++ {
buf.Reset()
enc.Encode(h)
dec.Decode(h2)
if h2.Network != "Header" {
b.Fatalf("wrong name")
}
}
}

View File

@ -9,7 +9,6 @@ import (
"strings" "strings"
"sync" "sync"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/merkle" "github.com/tendermint/tendermint/merkle"
) )
@ -24,7 +23,7 @@ var (
) )
type Part struct { type Part struct {
Index uint16 Index uint
Trail [][]byte Trail [][]byte
Bytes []byte Bytes []byte
@ -32,21 +31,6 @@ type Part struct {
hash []byte hash []byte
} }
func ReadPart(r io.Reader, n *int64, err *error) *Part {
return &Part{
Index: ReadUInt16(r, n, err),
Trail: ReadByteSlices(r, n, err),
Bytes: ReadByteSlice(r, n, err),
}
}
func (part *Part) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt16(w, part.Index, &n, &err)
WriteByteSlices(w, part.Trail, &n, &err)
WriteByteSlice(w, part.Bytes, &n, &err)
return
}
func (part *Part) Hash() []byte { func (part *Part) Hash() []byte {
if part.hash != nil { if part.hash != nil {
return part.hash return part.hash
@ -84,23 +68,10 @@ func (part *Part) StringWithIndent(indent string) string {
//------------------------------------- //-------------------------------------
type PartSetHeader struct { type PartSetHeader struct {
Total uint16 Total uint
Hash []byte Hash []byte
} }
func ReadPartSetHeader(r io.Reader, n *int64, err *error) PartSetHeader {
return PartSetHeader{
Total: ReadUInt16(r, n, err),
Hash: ReadByteSlice(r, n, err),
}
}
func (psh PartSetHeader) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt16(w, psh.Total, &n, &err)
WriteByteSlice(w, psh.Hash, &n, &err)
return
}
func (psh PartSetHeader) String() string { func (psh PartSetHeader) String() string {
return fmt.Sprintf("PartSet{T:%v %X}", psh.Total, Fingerprint(psh.Hash)) return fmt.Sprintf("PartSet{T:%v %X}", psh.Total, Fingerprint(psh.Hash))
} }
@ -116,13 +87,13 @@ func (psh PartSetHeader) Equals(other PartSetHeader) bool {
//------------------------------------- //-------------------------------------
type PartSet struct { type PartSet struct {
total uint16 total uint
hash []byte hash []byte
mtx sync.Mutex mtx sync.Mutex
parts []*Part parts []*Part
partsBitArray BitArray partsBitArray BitArray
count uint16 count uint
} }
// Returns an immutable, full PartSet. // Returns an immutable, full PartSet.
@ -135,7 +106,7 @@ func NewPartSetFromData(data []byte) *PartSet {
partsBitArray := NewBitArray(uint(total)) partsBitArray := NewBitArray(uint(total))
for i := 0; i < total; i++ { for i := 0; i < total; i++ {
part := &Part{ part := &Part{
Index: uint16(i), Index: uint(i),
Bytes: data[i*partSize : MinInt(len(data), (i+1)*partSize)], Bytes: data[i*partSize : MinInt(len(data), (i+1)*partSize)],
} }
parts[i] = part parts[i] = part
@ -148,11 +119,11 @@ func NewPartSetFromData(data []byte) *PartSet {
parts[i].Trail = trails[i].Flatten() parts[i].Trail = trails[i].Flatten()
} }
return &PartSet{ return &PartSet{
total: uint16(total), total: uint(total),
hash: rootTrail.Hash, hash: rootTrail.Hash,
parts: parts, parts: parts,
partsBitArray: partsBitArray, partsBitArray: partsBitArray,
count: uint16(total), count: uint(total),
} }
} }
@ -207,14 +178,14 @@ func (ps *PartSet) HashesTo(hash []byte) bool {
return bytes.Equal(ps.hash, hash) return bytes.Equal(ps.hash, hash)
} }
func (ps *PartSet) Count() uint16 { func (ps *PartSet) Count() uint {
if ps == nil { if ps == nil {
return 0 return 0
} }
return ps.count return ps.count
} }
func (ps *PartSet) Total() uint16 { func (ps *PartSet) Total() uint {
if ps == nil { if ps == nil {
return 0 return 0
} }
@ -247,7 +218,7 @@ func (ps *PartSet) AddPart(part *Part) (bool, error) {
return true, nil return true, nil
} }
func (ps *PartSet) GetPart(index uint16) *Part { func (ps *PartSet) GetPart(index uint) *Part {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
return ps.parts[index] return ps.parts[index]

View File

@ -27,7 +27,7 @@ func TestBasicPartSet(t *testing.T) {
// Test adding parts to a new partSet. // Test adding parts to a new partSet.
partSet2 := NewPartSetFromHeader(partSet.Header()) partSet2 := NewPartSetFromHeader(partSet.Header())
for i := uint16(0); i < partSet.Total(); i++ { for i := uint(0); i < partSet.Total(); i++ {
part := partSet.GetPart(i) part := partSet.GetPart(i)
//t.Logf("\n%v", part) //t.Logf("\n%v", part)
added, err := partSet2.AddPart(part) added, err := partSet2.AddPart(part)

View File

@ -1,111 +0,0 @@
package blocks
import (
"fmt"
"io"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
)
type Signable interface {
Binary
GetSignature() Signature
SetSignature(Signature)
}
//-----------------------------------------------------------------------------
type Signature struct {
SignerId uint64
Bytes []byte
}
func ReadSignature(r io.Reader, n *int64, err *error) Signature {
return Signature{
SignerId: ReadUInt64(r, n, err),
Bytes: ReadByteSlice(r, n, err),
}
}
func (sig Signature) IsZero() bool {
return len(sig.Bytes) == 0
}
func (sig Signature) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt64(w, sig.SignerId, &n, &err)
WriteByteSlice(w, sig.Bytes, &n, &err)
return
}
func (sig Signature) String() string {
return fmt.Sprintf("Signature{Id:%v %X}", sig.SignerId, Fingerprint(sig.Bytes))
}
//-------------------------------------
func ReadSignatures(r io.Reader, n *int64, err *error) (sigs []Signature) {
length := ReadUInt32(r, n, err)
for i := uint32(0); i < length; i++ {
sigs = append(sigs, ReadSignature(r, n, err))
}
return
}
func WriteSignatures(w io.Writer, sigs []Signature, n *int64, err *error) {
WriteUInt32(w, uint32(len(sigs)), n, err)
for _, sig := range sigs {
WriteBinary(w, sig, n, err)
if *err != nil {
return
}
}
}
//-----------------------------------------------------------------------------
type RoundSignature struct {
Round uint16
Signature
}
func ReadRoundSignature(r io.Reader, n *int64, err *error) RoundSignature {
return RoundSignature{
ReadUInt16(r, n, err),
ReadSignature(r, n, err),
}
}
func (rsig RoundSignature) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt16(w, rsig.Round, &n, &err)
WriteBinary(w, rsig.Signature, &n, &err)
return
}
func (rsig RoundSignature) IsZero() bool {
return rsig.Round == 0 && rsig.SignerId == 0 && len(rsig.Bytes) == 0
}
func (rsig RoundSignature) String() string {
return fmt.Sprintf("RoundSignature{R:%v Id:%v %X}", rsig.Round, rsig.SignerId, Fingerprint(rsig.Bytes))
}
//-------------------------------------
func ReadRoundSignatures(r io.Reader, n *int64, err *error) (rsigs []RoundSignature) {
length := ReadUInt32(r, n, err)
for i := uint32(0); i < length; i++ {
rsigs = append(rsigs, ReadRoundSignature(r, n, err))
}
return
}
func WriteRoundSignatures(w io.Writer, rsigs []RoundSignature, n *int64, err *error) {
WriteUInt32(w, uint32(len(rsigs)), n, err)
for _, rsig := range rsigs {
WriteBinary(w, rsig, n, err)
if *err != nil {
return
}
}
}

View File

@ -2,8 +2,8 @@ package blocks
import ( import (
"bytes" "bytes"
"encoding/binary"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
@ -17,7 +17,7 @@ import (
Simple low level store for blocks, which is actually stored as separte parts (wire format). Simple low level store for blocks, which is actually stored as separte parts (wire format).
*/ */
type BlockStore struct { type BlockStore struct {
height uint32 height uint
db db_.DB db db_.DB
} }
@ -30,7 +30,7 @@ func NewBlockStore(db db_.DB) *BlockStore {
} }
// Height() returns the last known contiguous block height. // Height() returns the last known contiguous block height.
func (bs *BlockStore) Height() uint32 { func (bs *BlockStore) Height() uint {
return bs.height return bs.height
} }
@ -42,49 +42,49 @@ func (bs *BlockStore) GetReader(key []byte) io.Reader {
return bytes.NewReader(bytez) return bytes.NewReader(bytez)
} }
func (bs *BlockStore) LoadBlock(height uint32) *Block { func (bs *BlockStore) LoadBlock(height uint) *Block {
var n int64 var n int64
var err error var err error
meta := ReadBlockMeta(bs.GetReader(calcBlockMetaKey(height)), &n, &err) meta := ReadBinary(&BlockMeta{}, bs.GetReader(calcBlockMetaKey(height)), &n, &err).(*BlockMeta)
if err != nil { if err != nil {
Panicf("Error reading block meta: %v", err) Panicf("Error reading block meta: %v", err)
} }
bytez := []byte{} bytez := []byte{}
for i := uint16(0); i < meta.Parts.Total; i++ { for i := uint(0); i < meta.Parts.Total; i++ {
part := bs.LoadBlockPart(height, i) part := bs.LoadBlockPart(height, i)
bytez = append(bytez, part.Bytes...) bytez = append(bytez, part.Bytes...)
} }
block := ReadBlock(bytes.NewReader(bytez), &n, &err) block := ReadBinary(&Block{}, bytes.NewReader(bytez), &n, &err).(*Block)
if err != nil { if err != nil {
Panicf("Error reading block: %v", err) Panicf("Error reading block: %v", err)
} }
return block return block
} }
func (bs *BlockStore) LoadBlockPart(height uint32, index uint16) *Part { func (bs *BlockStore) LoadBlockPart(height uint, index uint) *Part {
var n int64 var n int64
var err error var err error
part := ReadPart(bs.GetReader(calcBlockPartKey(height, index)), &n, &err) part := ReadBinary(&Part{}, bs.GetReader(calcBlockPartKey(height, index)), &n, &err).(*Part)
if err != nil { if err != nil {
Panicf("Error reading block part: %v", err) Panicf("Error reading block part: %v", err)
} }
return part return part
} }
func (bs *BlockStore) LoadBlockMeta(height uint32) *BlockMeta { func (bs *BlockStore) LoadBlockMeta(height uint) *BlockMeta {
var n int64 var n int64
var err error var err error
meta := ReadBlockMeta(bs.GetReader(calcBlockMetaKey(height)), &n, &err) meta := ReadBinary(&BlockMeta{}, bs.GetReader(calcBlockMetaKey(height)), &n, &err).(*BlockMeta)
if err != nil { if err != nil {
Panicf("Error reading block meta: %v", err) Panicf("Error reading block meta: %v", err)
} }
return meta return meta
} }
func (bs *BlockStore) LoadBlockValidation(height uint32) *Validation { func (bs *BlockStore) LoadBlockValidation(height uint) *Validation {
var n int64 var n int64
var err error var err error
validation := ReadValidation(bs.GetReader(calcBlockValidationKey(height)), &n, &err) validation := ReadBinary(&Validation{}, bs.GetReader(calcBlockValidationKey(height)), &n, &err).(*Validation)
if err != nil { if err != nil {
Panicf("Error reading validation: %v", err) Panicf("Error reading validation: %v", err)
} }
@ -108,7 +108,7 @@ func (bs *BlockStore) SaveBlock(block *Block, blockParts *PartSet) {
metaBytes := BinaryBytes(meta) metaBytes := BinaryBytes(meta)
bs.db.Set(calcBlockMetaKey(height), metaBytes) bs.db.Set(calcBlockMetaKey(height), metaBytes)
// Save block parts // Save block parts
for i := uint16(0); i < blockParts.Total(); i++ { for i := uint(0); i < blockParts.Total(); i++ {
bs.saveBlockPart(height, i, blockParts.GetPart(i)) bs.saveBlockPart(height, i, blockParts.GetPart(i))
} }
// Save block validation (duplicate and separate) // Save block validation (duplicate and separate)
@ -120,7 +120,7 @@ func (bs *BlockStore) SaveBlock(block *Block, blockParts *PartSet) {
bs.height = height bs.height = height
} }
func (bs *BlockStore) saveBlockPart(height uint32, index uint16, part *Part) { func (bs *BlockStore) saveBlockPart(height uint, index uint, part *Part) {
if height != bs.height+1 { if height != bs.height+1 {
Panicf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height) Panicf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height)
} }
@ -136,40 +136,18 @@ type BlockMeta struct {
Header *Header // The block's Header Header *Header // The block's Header
} }
func ReadBlockMeta(r io.Reader, n *int64, err *error) *BlockMeta {
return &BlockMeta{
Hash: ReadByteSlice(r, n, err),
Parts: ReadPartSetHeader(r, n, err),
Header: ReadHeader(r, n, err),
}
}
func (bm *BlockMeta) WriteTo(w io.Writer) (n int64, err error) {
WriteByteSlice(w, bm.Hash, &n, &err)
WriteBinary(w, bm.Parts, &n, &err)
WriteBinary(w, bm.Header, &n, &err)
return
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func calcBlockMetaKey(height uint32) []byte { func calcBlockMetaKey(height uint) []byte {
buf := [5]byte{'H'} return []byte(fmt.Sprintf("H:%v", height))
binary.BigEndian.PutUint32(buf[1:5], height)
return buf[:]
} }
func calcBlockPartKey(height uint32, partIndex uint16) []byte { func calcBlockPartKey(height uint, partIndex uint) []byte {
buf := [7]byte{'P'} return []byte(fmt.Sprintf("P:%v:%v", height, partIndex))
binary.BigEndian.PutUint32(buf[1:5], height)
binary.BigEndian.PutUint16(buf[5:7], partIndex)
return buf[:]
} }
func calcBlockValidationKey(height uint32) []byte { func calcBlockValidationKey(height uint) []byte {
buf := [5]byte{'V'} return []byte(fmt.Sprintf("V:%v", height))
binary.BigEndian.PutUint32(buf[1:5], height)
return buf[:]
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -177,7 +155,7 @@ func calcBlockValidationKey(height uint32) []byte {
var blockStoreKey = []byte("blockStore") var blockStoreKey = []byte("blockStore")
type BlockStoreJSON struct { type BlockStoreJSON struct {
Height uint32 Height uint
} }
func (bsj BlockStoreJSON) Save(db db_.DB) { func (bsj BlockStoreJSON) Save(db db_.DB) {

View File

@ -1,35 +1,33 @@
package blocks package blocks
import ( import (
"fmt" "errors"
"io" "io"
"reflect"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
) )
/* /*
Tx (Transaction) is an atomic operation on the ledger state.
Account Txs: Account Txs:
1. Send Send coins to account 1. SendTx Send coins to address
2. Name Associate account with a name
Validation Txs: Validation Txs:
3. Bond New validator posts a bond 1. BondTx New validator posts a bond
4. Unbond Validator leaves 2. UnbondTx Validator leaves
5. Dupeout Validator dupes out (signs twice) 3. DupeoutTx Validator dupes out (equivocates)
*/ */
type Tx interface { type Tx interface {
Signable WriteSignBytes(w io.Writer, n *int64, err *error)
GetSequence() uint
GetFee() uint64
String() string
} }
const ( const (
// Account transactions // Account transactions
TxTypeSend = byte(0x01) TxTypeSend = byte(0x01)
TxTypeName = byte(0x02)
// Validation transactions // Validation transactions
TxTypeBond = byte(0x11) TxTypeBond = byte(0x11)
@ -37,174 +35,150 @@ const (
TxTypeDupeout = byte(0x13) TxTypeDupeout = byte(0x13)
) )
func ReadTx(r io.Reader, n *int64, err *error) Tx { var (
ErrTxInvalidAddress = errors.New("Error invalid address")
ErrTxDuplicateAddress = errors.New("Error duplicate address")
ErrTxInvalidAmount = errors.New("Error invalid amount")
ErrTxInsufficientFunds = errors.New("Error insufficient funds")
ErrTxInvalidSignature = errors.New("Error invalid signature")
ErrTxInvalidSequence = errors.New("Error invalid sequence")
)
func TxDecoder(r io.Reader, n *int64, err *error) interface{} {
switch t := ReadByte(r, n, err); t { switch t := ReadByte(r, n, err); t {
case TxTypeSend: case TxTypeSend:
return &SendTx{ return ReadBinary(&SendTx{}, r, n, err)
BaseTx: ReadBaseTx(r, n, err),
To: ReadUInt64(r, n, err),
Amount: ReadUInt64(r, n, err),
}
case TxTypeName:
return &NameTx{
BaseTx: ReadBaseTx(r, n, err),
Name: ReadString(r, n, err),
PubKey: ReadByteSlice(r, n, err),
}
case TxTypeBond: case TxTypeBond:
return &BondTx{ return ReadBinary(&BondTx{}, r, n, err)
BaseTx: ReadBaseTx(r, n, err),
//UnbondTo: ReadUInt64(r, n, err),
}
case TxTypeUnbond: case TxTypeUnbond:
return &UnbondTx{ return ReadBinary(&UnbondTx{}, r, n, err)
BaseTx: ReadBaseTx(r, n, err),
}
case TxTypeDupeout: case TxTypeDupeout:
return &DupeoutTx{ return ReadBinary(&DupeoutTx{}, r, n, err)
BaseTx: ReadBaseTx(r, n, err),
VoteA: *ReadVote(r, n, err),
VoteB: *ReadVote(r, n, err),
}
default: default:
*err = Errorf("Unknown Tx type %X", t) *err = Errorf("Unknown Tx type %X", t)
return nil return nil
} }
} }
var _ = RegisterType(&TypeInfo{
Type: reflect.TypeOf((*Tx)(nil)).Elem(),
Decoder: TxDecoder,
})
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type BaseTx struct { type TxInput struct {
Sequence uint Address []byte // Hash of the PubKey
Fee uint64 Amount uint64 // Must not exceed account balance
Signature Signature Sequence uint // Must be 1 greater than the last committed TxInput
Signature Signature // Depends on the PubKey type and the whole Tx
} }
func ReadBaseTx(r io.Reader, n *int64, err *error) BaseTx { func (txIn *TxInput) ValidateBasic() error {
return BaseTx{ if len(txIn.Address) != 20 {
Sequence: ReadUVarInt(r, n, err), return ErrTxInvalidAddress
Fee: ReadUInt64(r, n, err),
Signature: ReadSignature(r, n, err),
} }
if txIn.Amount == 0 {
return ErrTxInvalidAmount
}
return nil
} }
func (tx BaseTx) WriteTo(w io.Writer) (n int64, err error) { func (txIn *TxInput) WriteSignBytes(w io.Writer, n *int64, err *error) {
WriteUVarInt(w, tx.Sequence, &n, &err) WriteByteSlice(txIn.Address, w, n, err)
WriteUInt64(w, tx.Fee, &n, &err) WriteUInt64(txIn.Amount, w, n, err)
WriteBinary(w, tx.Signature, &n, &err) WriteUVarInt(txIn.Sequence, w, n, err)
return
} }
func (tx *BaseTx) GetSequence() uint { //-----------------------------------------------------------------------------
return tx.Sequence
type TxOutput struct {
Address []byte // Hash of the PubKey
Amount uint64 // The sum of all outputs must not exceed the inputs.
} }
func (tx *BaseTx) GetSignature() Signature { func (txOut *TxOutput) ValidateBasic() error {
return tx.Signature if len(txOut.Address) != 20 {
return ErrTxInvalidAddress
}
if txOut.Amount == 0 {
return ErrTxInvalidAmount
}
return nil
} }
func (tx *BaseTx) GetFee() uint64 { func (txOut *TxOutput) WriteSignBytes(w io.Writer, n *int64, err *error) {
return tx.Fee WriteByteSlice(txOut.Address, w, n, err)
} WriteUInt64(txOut.Amount, w, n, err)
func (tx *BaseTx) SetSignature(sig Signature) {
tx.Signature = sig
}
func (tx *BaseTx) String() string {
return fmt.Sprintf("{S:%v F:%v Sig:%X}", tx.Sequence, tx.Fee, tx.Signature)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type SendTx struct { type SendTx struct {
BaseTx Inputs []*TxInput
To uint64 Outputs []*TxOutput
Amount uint64
} }
func (tx *SendTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *SendTx) TypeByte() byte { return TxTypeSend }
WriteByte(w, TxTypeSend, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err)
WriteUInt64(w, tx.To, &n, &err)
WriteUInt64(w, tx.Amount, &n, &err)
return
}
func (tx *SendTx) String() string { func (tx *SendTx) WriteSignBytes(w io.Writer, n *int64, err *error) {
return fmt.Sprintf("SendTx{%v To:%v Amount:%v}", tx.BaseTx, tx.To, tx.Amount) WriteUVarInt(uint(len(tx.Inputs)), w, n, err)
} for _, in := range tx.Inputs {
in.WriteSignBytes(w, n, err)
//----------------------------------------------------------------------------- }
WriteUVarInt(uint(len(tx.Outputs)), w, n, err)
type NameTx struct { for _, out := range tx.Outputs {
BaseTx out.WriteSignBytes(w, n, err)
Name string }
PubKey []byte
}
func (tx *NameTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, TxTypeName, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err)
WriteString(w, tx.Name, &n, &err)
WriteByteSlice(w, tx.PubKey, &n, &err)
return
}
func (tx *NameTx) String() string {
return fmt.Sprintf("NameTx{%v Name:%v PubKey:%X}", tx.BaseTx, tx.Name, tx.PubKey)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type BondTx struct { type BondTx struct {
BaseTx PubKey PubKeyEd25519
//UnbondTo uint64 Inputs []*TxInput
UnbondTo []*TxOutput
} }
func (tx *BondTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *BondTx) TypeByte() byte { return TxTypeBond }
WriteByte(w, TxTypeBond, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err)
//WriteUInt64(w, tx.UnbondTo, &n, &err)
return
}
func (tx *BondTx) String() string { func (tx *BondTx) WriteSignBytes(w io.Writer, n *int64, err *error) {
return fmt.Sprintf("BondTx{%v}", tx.BaseTx) WriteBinary(tx.PubKey, w, n, err)
WriteUVarInt(uint(len(tx.Inputs)), w, n, err)
for _, in := range tx.Inputs {
in.WriteSignBytes(w, n, err)
}
WriteUVarInt(uint(len(tx.UnbondTo)), w, n, err)
for _, out := range tx.UnbondTo {
out.WriteSignBytes(w, n, err)
}
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type UnbondTx struct { type UnbondTx struct {
BaseTx Address []byte
Height uint
Signature SignatureEd25519
} }
func (tx *UnbondTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *UnbondTx) TypeByte() byte { return TxTypeUnbond }
WriteByte(w, TxTypeUnbond, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err)
return
}
func (tx *UnbondTx) String() string { func (tx *UnbondTx) WriteSignBytes(w io.Writer, n *int64, err *error) {
return fmt.Sprintf("UnbondTx{%v}", tx.BaseTx) WriteByteSlice(tx.Address, w, n, err)
WriteUVarInt(tx.Height, w, n, err)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type DupeoutTx struct { type DupeoutTx struct {
BaseTx Address []byte
VoteA Vote VoteA Vote
VoteB Vote VoteB Vote
} }
func (tx *DupeoutTx) WriteTo(w io.Writer) (n int64, err error) { func (tx *DupeoutTx) TypeByte() byte { return TxTypeDupeout }
WriteByte(w, TxTypeDupeout, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err)
WriteBinary(w, &tx.VoteA, &n, &err)
WriteBinary(w, &tx.VoteB, &n, &err)
return
}
func (tx *DupeoutTx) String() string { func (tx *DupeoutTx) WriteSignBytes(w io.Writer, n *int64, err *error) {
return fmt.Sprintf("DupeoutTx{%v VoteA:%v VoteB:%v}", tx.BaseTx, tx.VoteA, tx.VoteB) panic("DupeoutTx has no sign bytes")
} }

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
) )
@ -23,53 +24,34 @@ var (
ErrVoteConflictingSignature = errors.New("Conflicting round vote signature") ErrVoteConflictingSignature = errors.New("Conflicting round vote signature")
) )
// Represents a prevote, precommit, or commit vote for proposals. // Represents a prevote, precommit, or commit vote for proposals from validators.
// Commit votes get aggregated into the next block's Validaiton.
// See the whitepaper for details.
type Vote struct { type Vote struct {
Height uint32 Height uint
Round uint16 Round uint
Type byte Type byte
BlockHash []byte // empty if vote is nil. BlockHash []byte // empty if vote is nil.
BlockParts PartSetHeader // zero if vote is nil. BlockParts PartSetHeader // zero if vote is nil.
Signature Signature SignatureEd25519
} }
func ReadVote(r io.Reader, n *int64, err *error) *Vote { func (vote *Vote) WriteSignBytes(w io.Writer, n *int64, err *error) {
return &Vote{ WriteUVarInt(vote.Height, w, n, err)
Height: ReadUInt32(r, n, err), WriteUVarInt(vote.Round, w, n, err)
Round: ReadUInt16(r, n, err), WriteByte(vote.Type, w, n, err)
Type: ReadByte(r, n, err), WriteByteSlice(vote.BlockHash, w, n, err)
BlockHash: ReadByteSlice(r, n, err), WriteBinary(vote.BlockParts, w, n, err)
BlockParts: ReadPartSetHeader(r, n, err),
Signature: ReadSignature(r, n, err),
}
} }
func (v *Vote) WriteTo(w io.Writer) (n int64, err error) { func (vote *Vote) Copy() *Vote {
WriteUInt32(w, v.Height, &n, &err) voteCopy := *vote
WriteUInt16(w, v.Round, &n, &err) return &voteCopy
WriteByte(w, v.Type, &n, &err)
WriteByteSlice(w, v.BlockHash, &n, &err)
WriteBinary(w, v.BlockParts, &n, &err)
WriteBinary(w, v.Signature, &n, &err)
return
} }
func (v *Vote) GetSignature() Signature { func (vote *Vote) String() string {
return v.Signature
}
func (v *Vote) SetSignature(sig Signature) {
v.Signature = sig
}
func (v *Vote) Copy() *Vote {
vCopy := *v
return &vCopy
}
func (v *Vote) String() string {
var typeString string var typeString string
switch v.Type { switch vote.Type {
case VoteTypePrevote: case VoteTypePrevote:
typeString = "Prevote" typeString = "Prevote"
case VoteTypePrecommit: case VoteTypePrecommit:
@ -80,5 +62,5 @@ func (v *Vote) String() string {
panic("Unknown vote type") panic("Unknown vote type")
} }
return fmt.Sprintf("%v{%v/%v %X#%v %v}", typeString, v.Height, v.Round, Fingerprint(v.BlockHash), v.BlockParts, v.Signature) return fmt.Sprintf("%v{%v/%v %X#%v %v}", typeString, vote.Height, vote.Round, Fingerprint(vote.BlockHash), vote.BlockParts, vote.Signature)
} }

View File

@ -33,20 +33,18 @@ func NewNode() *Node {
stateDB := db_.NewMemDB() // TODO configurable db. stateDB := db_.NewMemDB() // TODO configurable db.
state := state_.LoadState(stateDB) state := state_.LoadState(stateDB)
if state == nil { if state == nil {
state = state_.GenesisStateFromFile(stateDB, config.RootDir+"/genesis.json") state = state_.GenesisStateFromFile(stateDB, config.GenesisFile())
state.Save() state.Save()
} }
// Get PrivAccount // Get PrivValidator
var privValidator *consensus.PrivValidator var privValidator *consensus.PrivValidator
if _, err := os.Stat(config.RootDir + "/private.json"); err == nil { if _, err := os.Stat(config.PrivValidatorFile()); err == nil {
privAccount := state_.PrivAccountFromFile(config.RootDir + "/private.json") privValidator = consensus.LoadPrivValidator()
privValidatorDB := db_.NewMemDB() // TODO configurable db.
privValidator = consensus.NewPrivValidator(privValidatorDB, privAccount)
} }
// Get PEXReactor // Get PEXReactor
book := p2p.NewAddrBook(config.RootDir + "/addrbook.json") book := p2p.NewAddrBook(config.AddrBookFile())
pexReactor := p2p.NewPEXReactor(book) pexReactor := p2p.NewPEXReactor(book)
// Get MempoolReactor // Get MempoolReactor

View File

@ -4,13 +4,15 @@ import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"github.com/tendermint/tendermint/state" . "github.com/tendermint/tendermint/binary"
"github.com/tendermint/tendermint/wallet"
) )
func gen_account() { func gen_account() {
// TODO: uh, write better logic.
// Generate private account // Generate private account
privAccount := state.GenPrivAccount() privAccount := wallet.GenPrivAccount()
fmt.Printf(`Generated account: fmt.Printf(`Generated account:
Account Public Key: %X Account Public Key: %X
@ -19,7 +21,7 @@ Account Private Key: %X
(base64) %v (base64) %v
`, `,
privAccount.PubKey, privAccount.PubKey,
base64.StdEncoding.EncodeToString(privAccount.PubKey), base64.StdEncoding.EncodeToString(BinaryBytes(privAccount.PubKey)),
privAccount.PrivKey, privAccount.PrivKey,
base64.StdEncoding.EncodeToString(privAccount.PrivKey)) base64.StdEncoding.EncodeToString(BinaryBytes(privAccount.PrivKey)))
} }

23
cmd/gen_validator.go Normal file
View File

@ -0,0 +1,23 @@
package main
import (
"fmt"
"os"
"github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/consensus"
)
func gen_validator() {
// If already exists, bail out.
filename := config.PrivValidatorFile()
if _, err := os.Stat(filename); !os.IsNotExist(err) {
fmt.Printf("Cannot generate new validator, file already exists at %v\n", filename)
}
// Generate private validator
privValidator := consensus.GenPrivValidator()
privValidator.Save()
fmt.Printf("Generated a new validator at %v\n", filename)
}

View File

@ -19,6 +19,7 @@ func main() {
Commands: Commands:
daemon Run the tendermint node daemon daemon Run the tendermint node daemon
gen_account Generate new account keypair gen_account Generate new account keypair
gen_validator Generate new validator keypair
tendermint --help for command options`) tendermint --help for command options`)
return return
@ -29,5 +30,7 @@ tendermint --help for command options`)
daemon() daemon()
case "gen_account": case "gen_account":
gen_account() gen_account()
case "gen_validator":
gen_validator()
} }
} }

View File

@ -2,11 +2,8 @@ package common
import ( import (
"fmt" "fmt"
"io"
"math/rand" "math/rand"
"strings" "strings"
. "github.com/tendermint/tendermint/binary"
) )
// Not goroutine safe // Not goroutine safe
@ -19,47 +16,6 @@ func NewBitArray(bits uint) BitArray {
return BitArray{bits, make([]uint64, (bits+63)/64)} return BitArray{bits, make([]uint64, (bits+63)/64)}
} }
func ReadBitArray(r io.Reader, n *int64, err *error) BitArray {
bits := ReadUVarInt(r, n, err)
if bits == 0 {
return BitArray{}
}
elemsWritten := ReadUVarInt(r, n, err)
if *err != nil {
return BitArray{}
}
bA := NewBitArray(bits)
for i := uint(0); i < elemsWritten; i++ {
bA.elems[i] = ReadUInt64(r, n, err)
if *err != nil {
return BitArray{}
}
}
return bA
}
func (bA BitArray) WriteTo(w io.Writer) (n int64, err error) {
WriteUVarInt(w, bA.bits, &n, &err)
if bA.bits == 0 {
return
}
// Count the last element > 0.
elemsToWrite := 0
for i, elem := range bA.elems {
if elem > 0 {
elemsToWrite = i + 1
}
}
WriteUVarInt(w, uint(elemsToWrite), &n, &err)
for i, elem := range bA.elems {
if i >= elemsToWrite {
break
}
WriteUInt64(w, elem, &n, &err)
}
return
}
func (bA BitArray) Size() uint { func (bA BitArray) Size() uint {
return bA.bits return bA.bits
} }

View File

@ -12,8 +12,22 @@ import (
"strings" "strings"
) )
var RootDir string var rootDir string
var Config Config_
func init() {
rootDir = os.Getenv("TMROOT")
if rootDir == "" {
rootDir = os.Getenv("HOME") + "/.tendermint"
}
}
func ConfigFile() string { return rootDir + "/config.json" }
func GenesisFile() string { return rootDir + "/genesis.json" }
func AddrBookFile() string { return rootDir + "/addrbook.json" }
func PrivValidatorFile() string { return rootDir + "/priv_validator.json" }
func DataDir() string { return rootDir + "/data" }
var Config ConfigType
func setFlags(printHelp *bool) { func setFlags(printHelp *bool) {
flag.BoolVar(printHelp, "help", false, "Print this help message.") flag.BoolVar(printHelp, "help", false, "Print this help message.")
@ -22,11 +36,7 @@ func setFlags(printHelp *bool) {
} }
func ParseFlags() { func ParseFlags() {
RootDir = os.Getenv("TMROOT") configFile := ConfigFile()
if RootDir == "" {
RootDir = os.Getenv("HOME") + "/.tendermint"
}
configFile := RootDir + "/config.json"
// try to read configuration. if missing, write default // try to read configuration. if missing, write default
configBytes, err := ioutil.ReadFile(configFile) configBytes, err := ioutil.ReadFile(configFile)
@ -38,7 +48,7 @@ func ParseFlags() {
} }
// try to parse configuration. on error, die // try to parse configuration. on error, die
Config = Config_{} Config = ConfigType{}
err = json.Unmarshal(configBytes, &Config) err = json.Unmarshal(configBytes, &Config)
if err != nil { if err != nil {
log.Panicf("Invalid configuration file %s: %v", configFile, err) log.Panicf("Invalid configuration file %s: %v", configFile, err)
@ -61,13 +71,13 @@ func ParseFlags() {
//-----------------------------------------------------------------------------j //-----------------------------------------------------------------------------j
// Default configuration // Default configuration
var defaultConfig = Config_{ var defaultConfig = ConfigType{
Network: "tendermint_testnet0", Network: "tendermint_testnet0",
LAddr: "0.0.0.0:0", LAddr: "0.0.0.0:0",
SeedNode: "", SeedNode: "",
Db: DbConfig{ Db: DbConfig{
Type: "level", Type: "level",
Dir: RootDir + "/data", Dir: DataDir(),
}, },
Alert: AlertConfig{}, Alert: AlertConfig{},
SMTP: SMTPConfig{}, SMTP: SMTPConfig{},
@ -79,7 +89,7 @@ var defaultConfig = Config_{
//-----------------------------------------------------------------------------j //-----------------------------------------------------------------------------j
// Configuration types // Configuration types
type Config_ struct { type ConfigType struct {
Network string Network string
LAddr string LAddr string
SeedNode string SeedNode string
@ -118,7 +128,7 @@ type RPCConfig struct {
//-----------------------------------------------------------------------------j //-----------------------------------------------------------------------------j
func (cfg *Config_) validate() error { func (cfg *ConfigType) validate() error {
if cfg.Network == "" { if cfg.Network == "" {
cfg.Network = defaultConfig.Network cfg.Network = defaultConfig.Network
} }
@ -134,7 +144,7 @@ func (cfg *Config_) validate() error {
return nil return nil
} }
func (cfg *Config_) bytes() []byte { func (cfg *ConfigType) bytes() []byte {
configBytes, err := json.MarshalIndent(cfg, "", "\t") configBytes, err := json.MarshalIndent(cfg, "", "\t")
if err != nil { if err != nil {
panic(err) panic(err)
@ -142,7 +152,7 @@ func (cfg *Config_) bytes() []byte {
return configBytes return configBytes
} }
func (cfg *Config_) write(configFile string) { func (cfg *ConfigType) write(configFile string) {
if strings.Index(configFile, "/") != -1 { if strings.Index(configFile, "/") != -1 {
err := os.MkdirAll(filepath.Dir(configFile), 0700) err := os.MkdirAll(filepath.Dir(configFile), 0700)
if err != nil { if err != nil {

View File

@ -2,111 +2,81 @@ package consensus
import ( import (
"fmt" "fmt"
"io"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/state"
) )
// Each signature of a POL (proof-of-lock, see whitepaper) is
// either a prevote or a commit.
// Commits require an additional round which is strictly less than
// the POL round. Prevote rounds are equal to the POL round.
type POLVoteSignature struct {
Round uint
Signature SignatureEd25519
}
// Proof of lock. // Proof of lock.
// +2/3 of validators' prevotes for a given blockhash (or nil) // +2/3 of validators' prevotes for a given blockhash (or nil)
type POL struct { type POL struct {
Height uint32 Height uint
Round uint16 Round uint
BlockHash []byte // Could be nil, which makes this a proof of unlock. BlockHash []byte // Could be nil, which makes this a proof of unlock.
BlockParts PartSetHeader // When BlockHash is nil, this is zero. BlockParts PartSetHeader // When BlockHash is nil, this is zero.
Votes []Signature // Vote signatures for height/round/hash Votes []POLVoteSignature // Prevote and commit signatures in ValidatorSet order.
Commits []RoundSignature // Commit signatures for height/hash
}
func ReadPOL(r io.Reader, n *int64, err *error) *POL {
return &POL{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
BlockHash: ReadByteSlice(r, n, err),
BlockParts: ReadPartSetHeader(r, n, err),
Votes: ReadSignatures(r, n, err),
Commits: ReadRoundSignatures(r, n, err),
}
}
func (pol *POL) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt32(w, pol.Height, &n, &err)
WriteUInt16(w, pol.Round, &n, &err)
WriteByteSlice(w, pol.BlockHash, &n, &err)
WriteBinary(w, pol.BlockParts, &n, &err)
WriteSignatures(w, pol.Votes, &n, &err)
WriteRoundSignatures(w, pol.Commits, &n, &err)
return
} }
// Returns whether +2/3 have voted/committed for BlockHash. // Returns whether +2/3 have voted/committed for BlockHash.
func (pol *POL) Verify(vset *state.ValidatorSet) error { func (pol *POL) Verify(valSet *state.ValidatorSet) error {
if uint(len(pol.Votes)) != valSet.Size() {
return Errorf("Invalid POL votes count: Expected %v, got %v",
valSet.Size(), len(pol.Votes))
}
talliedVotingPower := uint64(0) talliedVotingPower := uint64(0)
voteDoc := BinaryBytes(&Vote{ prevoteDoc := SignBytes(&Vote{
Height: pol.Height, Round: pol.Round, Type: VoteTypePrevote, Height: pol.Height, Round: pol.Round, Type: VoteTypePrevote,
BlockHash: pol.BlockHash, BlockHash: pol.BlockHash,
BlockParts: pol.BlockParts, BlockParts: pol.BlockParts,
}) })
seenValidators := map[uint64]struct{}{} seenValidators := map[string]struct{}{}
for _, sig := range pol.Votes { for idx, sig := range pol.Votes {
voteDoc := prevoteDoc
_, val := valSet.GetByIndex(uint(idx))
// Commit signature?
if sig.Round < pol.Round {
voteDoc = SignBytes(&Vote{
Height: pol.Height, Round: sig.Round, Type: VoteTypeCommit,
BlockHash: pol.BlockHash,
BlockParts: pol.BlockParts,
})
} else if sig.Round > pol.Round {
return Errorf("Invalid commit round %v for POL %v", sig.Round, pol)
}
// Validate // Validate
if _, seen := seenValidators[sig.SignerId]; seen { if _, seen := seenValidators[string(val.Address)]; seen {
return Errorf("Duplicate validator for vote %v for POL %v", sig, pol) return Errorf("Duplicate validator for vote %v for POL %v", sig, pol)
} }
_, val := vset.GetById(sig.SignerId) if !val.PubKey.VerifyBytes(voteDoc, sig.Signature.Bytes) {
if val == nil {
return Errorf("Invalid validator for vote %v for POL %v", sig, pol)
}
if !val.VerifyBytes(voteDoc, sig) {
return Errorf("Invalid signature for vote %v for POL %v", sig, pol) return Errorf("Invalid signature for vote %v for POL %v", sig, pol)
} }
// Tally // Tally
seenValidators[val.Id] = struct{}{} seenValidators[string(val.Address)] = struct{}{}
talliedVotingPower += val.VotingPower talliedVotingPower += val.VotingPower
} }
for _, rsig := range pol.Commits { if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
round := rsig.Round
sig := rsig.Signature
// Validate
if _, seen := seenValidators[sig.SignerId]; seen {
return Errorf("Duplicate validator for commit %v for POL %v", sig, pol)
}
_, val := vset.GetById(sig.SignerId)
if val == nil {
return Errorf("Invalid validator for commit %v for POL %v", sig, pol)
}
if round >= pol.Round {
return Errorf("Invalid commit round %v for POL %v", round, pol)
}
commitDoc := BinaryBytes(&Vote{
Height: pol.Height, Round: round, Type: VoteTypeCommit,
BlockHash: pol.BlockHash,
BlockParts: pol.BlockParts,
})
if !val.VerifyBytes(commitDoc, sig) {
return Errorf("Invalid signature for commit %v for POL %v", sig, pol)
}
// Tally
seenValidators[val.Id] = struct{}{}
talliedVotingPower += val.VotingPower
}
if talliedVotingPower > vset.TotalVotingPower()*2/3 {
return nil return nil
} else { } else {
return Errorf("Invalid POL, insufficient voting power %v, needed %v", return Errorf("Invalid POL, insufficient voting power %v, needed %v",
talliedVotingPower, (vset.TotalVotingPower()*2/3 + 1)) talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1))
} }
} }

View File

@ -1,30 +1,166 @@
package consensus package consensus
// TODO: This logic is crude. Should be more transactional.
import ( import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/blocks"
db_ "github.com/tendermint/tendermint/db" . "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/state" . "github.com/tendermint/tendermint/config"
"github.com/tendermint/go-ed25519"
) )
//----------------------------------------------------------------------------- const (
stepNone = 0 // Used to distinguish the initial state
stepPropose = 1
stepPrevote = 2
stepPrecommit = 3
stepCommit = 4
)
type PrivValidator struct { func voteToStep(vote *Vote) uint8 {
db db_.DB switch vote.Type {
state.PrivAccount case VoteTypePrevote:
} return stepPrevote
case VoteTypePrecommit:
func NewPrivValidator(db db_.DB, priv *state.PrivAccount) *PrivValidator { return stepPrecommit
return &PrivValidator{db, *priv} case VoteTypeCommit:
} return stepCommit
default:
// Double signing results in a panic. panic("Unknown vote type")
func (pv *PrivValidator) Sign(o Signable) { }
switch o.(type) { }
case *Proposal:
//TODO: prevent double signing && test. type PrivValidator struct {
pv.PrivAccount.Sign(o.(*Proposal)) Address []byte
case *Vote: PubKey PubKeyEd25519
//TODO: prevent double signing && test. PrivKey PrivKeyEd25519
pv.PrivAccount.Sign(o.(*Vote)) LastHeight uint
LastRound uint
LastStep uint8
}
// Generates a new validator with private key.
func GenPrivValidator() *PrivValidator {
privKeyBytes := CRandBytes(32)
pubKeyBytes := ed25519.MakePubKey(privKeyBytes)
pubKey := PubKeyEd25519{pubKeyBytes}
privKey := PrivKeyEd25519{pubKeyBytes, privKeyBytes}
return &PrivValidator{
Address: pubKey.Address(),
PubKey: pubKey,
PrivKey: privKey,
LastHeight: 0,
LastRound: 0,
LastStep: stepNone,
}
}
type PrivValidatorJSON struct {
Address string
PubKey string
PrivKey string
LastHeight uint
LastRound uint
LastStep uint8
}
func LoadPrivValidator() *PrivValidator {
privValJSONBytes, err := ioutil.ReadFile(PrivValidatorFile())
if err != nil {
panic(err)
}
privValJSON := PrivValidatorJSON{}
err = json.Unmarshal(privValJSONBytes, &privValJSON)
if err != nil {
panic(err)
}
address, err := base64.StdEncoding.DecodeString(privValJSON.Address)
if err != nil {
panic(err)
}
pubKeyBytes, err := base64.StdEncoding.DecodeString(privValJSON.PubKey)
if err != nil {
panic(err)
}
privKeyBytes, err := base64.StdEncoding.DecodeString(privValJSON.PrivKey)
if err != nil {
panic(err)
}
n := new(int64)
privVal := &PrivValidator{
Address: address,
PubKey: ReadBinary(PubKeyEd25519{}, bytes.NewReader(pubKeyBytes), n, &err).(PubKeyEd25519),
PrivKey: ReadBinary(PrivKeyEd25519{}, bytes.NewReader(privKeyBytes), n, &err).(PrivKeyEd25519),
LastHeight: privValJSON.LastHeight,
LastRound: privValJSON.LastRound,
LastStep: privValJSON.LastStep,
}
if err != nil {
panic(err)
}
return privVal
}
func (privVal *PrivValidator) Save() {
privValJSON := PrivValidatorJSON{
Address: base64.StdEncoding.EncodeToString(privVal.Address),
PubKey: base64.StdEncoding.EncodeToString(BinaryBytes(privVal.PubKey)),
PrivKey: base64.StdEncoding.EncodeToString(BinaryBytes(privVal.PrivKey)),
LastHeight: privVal.LastHeight,
LastRound: privVal.LastRound,
LastStep: privVal.LastStep,
}
privValJSONBytes, err := json.Marshal(privValJSON)
if err != nil {
panic(err)
}
err = ioutil.WriteFile(PrivValidatorFile(), privValJSONBytes, 0700)
if err != nil {
panic(err)
}
}
func (privVal *PrivValidator) SignVote(vote *Vote) SignatureEd25519 {
if privVal.LastHeight < vote.Height ||
privVal.LastHeight == vote.Height && privVal.LastRound < vote.Round ||
privVal.LastHeight == vote.Height && privVal.LastRound == vote.Round && privVal.LastStep < voteToStep(vote) {
// Persist height/round/step
privVal.LastHeight = vote.Height
privVal.LastRound = vote.Round
privVal.LastStep = voteToStep(vote)
privVal.Save()
// Sign
return privVal.PrivKey.Sign(SignBytes(vote)).(SignatureEd25519)
} else {
panic(fmt.Sprintf("Attempt of duplicate signing of vote: Height %v, Round %v, Type %v", vote.Height, vote.Round, vote.Type))
}
}
func (privVal *PrivValidator) SignProposal(proposal *Proposal) SignatureEd25519 {
if privVal.LastHeight < proposal.Height ||
privVal.LastHeight == proposal.Height && privVal.LastRound < proposal.Round ||
privVal.LastHeight == 0 && privVal.LastRound == 0 && privVal.LastStep == stepNone {
// Persist height/round/step
privVal.LastHeight = proposal.Height
privVal.LastRound = proposal.Round
privVal.LastStep = stepPropose
privVal.Save()
// Sign
return privVal.PrivKey.Sign(SignBytes(proposal)).(SignatureEd25519)
} else {
panic(fmt.Sprintf("Attempt of duplicate signing of proposal: Height %v, Round %v, Type %v", proposal.Height, proposal.Round))
} }
} }

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"io" "io"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks" . "github.com/tendermint/tendermint/blocks"
) )
@ -15,15 +16,14 @@ var (
) )
type Proposal struct { type Proposal struct {
Height uint32 Height uint
Round uint16 Round uint
BlockParts PartSetHeader BlockParts PartSetHeader
POLParts PartSetHeader POLParts PartSetHeader
Signature Signature Signature SignatureEd25519
} }
func NewProposal(height uint32, round uint16, blockParts, polParts PartSetHeader) *Proposal { func NewProposal(height uint, round uint, blockParts, polParts PartSetHeader) *Proposal {
return &Proposal{ return &Proposal{
Height: height, Height: height,
Round: round, Round: round,
@ -32,34 +32,14 @@ func NewProposal(height uint32, round uint16, blockParts, polParts PartSetHeader
} }
} }
func ReadProposal(r io.Reader, n *int64, err *error) *Proposal {
return &Proposal{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
BlockParts: ReadPartSetHeader(r, n, err),
POLParts: ReadPartSetHeader(r, n, err),
Signature: ReadSignature(r, n, err),
}
}
func (p *Proposal) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt32(w, p.Height, &n, &err)
WriteUInt16(w, p.Round, &n, &err)
WriteBinary(w, p.BlockParts, &n, &err)
WriteBinary(w, p.POLParts, &n, &err)
WriteBinary(w, p.Signature, &n, &err)
return
}
func (p *Proposal) GetSignature() Signature {
return p.Signature
}
func (p *Proposal) SetSignature(sig Signature) {
p.Signature = sig
}
func (p *Proposal) String() string { func (p *Proposal) String() string {
return fmt.Sprintf("Proposal{%v/%v %v %v %v}", p.Height, p.Round, return fmt.Sprintf("Proposal{%v/%v %v %v %v}", p.Height, p.Round,
p.BlockParts, p.POLParts, p.Signature) p.BlockParts, p.POLParts, p.Signature)
} }
func (p *Proposal) WriteSignBytes(w io.Writer, n *int64, err *error) {
WriteUVarInt(p.Height, w, n, err)
WriteUVarInt(p.Round, w, n, err)
WriteBinary(p.BlockParts, w, n, err)
WriteBinary(p.POLParts, w, n, err)
}

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"io"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -123,9 +122,9 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte
msg := msg_.(*NewRoundStepMessage) msg := msg_.(*NewRoundStepMessage)
ps.ApplyNewRoundStepMessage(msg, rs) ps.ApplyNewRoundStepMessage(msg, rs)
case *CommitMessage: case *CommitStepMessage:
msg := msg_.(*CommitMessage) msg := msg_.(*CommitStepMessage)
ps.ApplyCommitMessage(msg) ps.ApplyCommitStepMessage(msg)
case *HasVoteMessage: case *HasVoteMessage:
msg := msg_.(*HasVoteMessage) msg := msg_.(*HasVoteMessage)
@ -160,10 +159,17 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte
case VoteCh: case VoteCh:
switch msg_.(type) { switch msg_.(type) {
case *Vote: case *VoteMessage:
vote := msg_.(*Vote) voteMessage := msg_.(*VoteMessage)
added, index, err := conR.conS.AddVote(vote) vote := voteMessage.Vote
if rs.Height != vote.Height {
return // Wrong height. Not necessarily a bad peer.
}
validatorIndex := voteMessage.ValidatorIndex
address, _ := rs.Validators.GetByIndex(validatorIndex)
added, index, err := conR.conS.AddVote(address, vote)
if err != nil { if err != nil {
// Probably an invalid signature. Bad peer.
log.Warning("Error attempting to add vote: %v", err) log.Warning("Error attempting to add vote: %v", err)
} }
// Initialize Prevotes/Precommits/Commits if needed // Initialize Prevotes/Precommits/Commits if needed
@ -220,14 +226,14 @@ func (conR *ConsensusReactor) broadcastNewRoundStepRoutine() {
Height: rs.Height, Height: rs.Height,
Round: rs.Round, Round: rs.Round,
Step: rs.Step, Step: rs.Step,
SecondsSinceStartTime: uint32(timeElapsed.Seconds()), SecondsSinceStartTime: uint(timeElapsed.Seconds()),
} }
conR.sw.Broadcast(StateCh, msg) conR.sw.Broadcast(StateCh, msg)
} }
// If the step is commit, then also broadcast a CommitMessage. // If the step is commit, then also broadcast a CommitStepMessage.
if rs.Step == RoundStepCommit { if rs.Step == RoundStepCommit {
msg := &CommitMessage{ msg := &CommitStepMessage{
Height: rs.Height, Height: rs.Height,
BlockParts: rs.ProposalBlockParts.Header(), BlockParts: rs.ProposalBlockParts.Header(),
BlockBitArray: rs.ProposalBlockParts.BitArray(), BlockBitArray: rs.ProposalBlockParts.BitArray(),
@ -259,10 +265,10 @@ OUTER_LOOP:
Height: rs.Height, Height: rs.Height,
Round: rs.Round, Round: rs.Round,
Type: partTypeProposalBlock, Type: partTypeProposalBlock,
Part: rs.ProposalBlockParts.GetPart(uint16(index)), Part: rs.ProposalBlockParts.GetPart(index),
} }
peer.Send(DataCh, msg) peer.Send(DataCh, msg)
ps.SetHasProposalBlockPart(rs.Height, rs.Round, uint16(index)) ps.SetHasProposalBlockPart(rs.Height, rs.Round, index)
continue OUTER_LOOP continue OUTER_LOOP
} }
} }
@ -289,10 +295,10 @@ OUTER_LOOP:
Height: rs.Height, Height: rs.Height,
Round: rs.Round, Round: rs.Round,
Type: partTypeProposalPOL, Type: partTypeProposalPOL,
Part: rs.ProposalPOLParts.GetPart(uint16(index)), Part: rs.ProposalPOLParts.GetPart(index),
} }
peer.Send(DataCh, msg) peer.Send(DataCh, msg)
ps.SetHasProposalPOLPart(rs.Height, rs.Round, uint16(index)) ps.SetHasProposalPOLPart(rs.Height, rs.Round, index)
continue OUTER_LOOP continue OUTER_LOOP
} }
} }
@ -320,7 +326,7 @@ OUTER_LOOP:
if ok { if ok {
vote := voteSet.GetByIndex(index) vote := voteSet.GetByIndex(index)
// NOTE: vote may be a commit. // NOTE: vote may be a commit.
msg := p2p.TypedMessage{msgTypeVote, vote} msg := &VoteMessage{index, vote}
peer.Send(VoteCh, msg) peer.Send(VoteCh, msg)
ps.SetHasVote(vote, index) ps.SetHasVote(vote, index)
return true return true
@ -399,7 +405,7 @@ OUTER_LOOP:
BlockParts: header.LastBlockParts, BlockParts: header.LastBlockParts,
Signature: rsig.Signature, Signature: rsig.Signature,
} }
msg := p2p.TypedMessage{msgTypeVote, vote} msg := &VoteMessage{index, vote}
peer.Send(VoteCh, msg) peer.Send(VoteCh, msg)
ps.SetHasVote(vote, index) ps.SetHasVote(vote, index)
continue OUTER_LOOP continue OUTER_LOOP
@ -416,8 +422,8 @@ OUTER_LOOP:
// Read only when returned by PeerState.GetRoundState(). // Read only when returned by PeerState.GetRoundState().
type PeerRoundState struct { type PeerRoundState struct {
Height uint32 // Height peer is at Height uint // Height peer is at
Round uint16 // Round peer is at Round uint // Round peer is at
Step RoundStep // Step peer is at Step RoundStep // Step peer is at
StartTime time.Time // Estimated start of round 0 at this height StartTime time.Time // Estimated start of round 0 at this height
Proposal bool // True if peer has proposal for this round Proposal bool // True if peer has proposal for this round
@ -474,7 +480,7 @@ func (ps *PeerState) SetHasProposal(proposal *Proposal) {
ps.ProposalPOLBitArray = NewBitArray(uint(proposal.POLParts.Total)) ps.ProposalPOLBitArray = NewBitArray(uint(proposal.POLParts.Total))
} }
func (ps *PeerState) SetHasProposalBlockPart(height uint32, round uint16, index uint16) { func (ps *PeerState) SetHasProposalBlockPart(height uint, round uint, index uint) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
@ -485,7 +491,7 @@ func (ps *PeerState) SetHasProposalBlockPart(height uint32, round uint16, index
ps.ProposalBlockBitArray.SetIndex(uint(index), true) ps.ProposalBlockBitArray.SetIndex(uint(index), true)
} }
func (ps *PeerState) SetHasProposalPOLPart(height uint32, round uint16, index uint16) { func (ps *PeerState) SetHasProposalPOLPart(height uint, round uint, index uint) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
@ -496,7 +502,7 @@ func (ps *PeerState) SetHasProposalPOLPart(height uint32, round uint16, index ui
ps.ProposalPOLBitArray.SetIndex(uint(index), true) ps.ProposalPOLBitArray.SetIndex(uint(index), true)
} }
func (ps *PeerState) EnsureVoteBitArrays(height uint32, numValidators uint) { func (ps *PeerState) EnsureVoteBitArrays(height uint, numValidators uint) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
@ -521,7 +527,7 @@ func (ps *PeerState) SetHasVote(vote *Vote, index uint) {
ps.setHasVote(vote.Height, vote.Round, vote.Type, index) ps.setHasVote(vote.Height, vote.Round, vote.Type, index)
} }
func (ps *PeerState) setHasVote(height uint32, round uint16, type_ byte, index uint) { func (ps *PeerState) setHasVote(height uint, round uint, type_ byte, index uint) {
if ps.Height == height+1 && type_ == VoteTypeCommit { if ps.Height == height+1 && type_ == VoteTypeCommit {
// Special case for LastCommits. // Special case for LastCommits.
ps.LastCommits.SetIndex(index, true) ps.LastCommits.SetIndex(index, true)
@ -583,7 +589,7 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage, rs *Roun
} }
} }
func (ps *PeerState) ApplyCommitMessage(msg *CommitMessage) { func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
@ -614,15 +620,13 @@ func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) {
// Messages // Messages
const ( const (
msgTypeUnknown = byte(0x00) msgTypeUnknown = byte(0x00)
// Messages for communicating state changes
msgTypeNewRoundStep = byte(0x01) msgTypeNewRoundStep = byte(0x01)
msgTypeCommit = byte(0x02) msgTypeCommitStep = byte(0x02)
// Messages of data msgTypeProposal = byte(0x11)
msgTypeProposal = byte(0x11) msgTypePart = byte(0x12) // both block & POL
msgTypePart = byte(0x12) // both block & POL msgTypeVote = byte(0x13)
msgTypeVote = byte(0x13) msgTypeHasVote = byte(0x14)
msgTypeHasVote = byte(0x14)
) )
// TODO: check for unnecessary extra bytes at the end. // TODO: check for unnecessary extra bytes at the end.
@ -634,18 +638,18 @@ func decodeMessage(bz []byte) (msgType byte, msg interface{}) {
switch msgType { switch msgType {
// Messages for communicating state changes // Messages for communicating state changes
case msgTypeNewRoundStep: case msgTypeNewRoundStep:
msg = readNewRoundStepMessage(r, n, err) msg = ReadBinary(&NewRoundStepMessage{}, r, n, err)
case msgTypeCommit: case msgTypeCommitStep:
msg = readCommitMessage(r, n, err) msg = ReadBinary(&CommitStepMessage{}, r, n, err)
// Messages of data // Messages of data
case msgTypeProposal: case msgTypeProposal:
msg = ReadProposal(r, n, err) msg = ReadBinary(&Proposal{}, r, n, err)
case msgTypePart: case msgTypePart:
msg = readPartMessage(r, n, err) msg = ReadBinary(&PartMessage{}, r, n, err)
case msgTypeVote: case msgTypeVote:
msg = ReadVote(r, n, err) msg = ReadBinary(&VoteMessage{}, r, n, err)
case msgTypeHasVote: case msgTypeHasVote:
msg = readHasVoteMessage(r, n, err) msg = ReadBinary(&HasVoteMessage{}, r, n, err)
default: default:
msg = nil msg = nil
} }
@ -655,29 +659,13 @@ func decodeMessage(bz []byte) (msgType byte, msg interface{}) {
//------------------------------------- //-------------------------------------
type NewRoundStepMessage struct { type NewRoundStepMessage struct {
Height uint32 Height uint
Round uint16 Round uint
Step RoundStep Step RoundStep
SecondsSinceStartTime uint32 SecondsSinceStartTime uint
} }
func readNewRoundStepMessage(r io.Reader, n *int64, err *error) *NewRoundStepMessage { func (m *NewRoundStepMessage) TypeByte() byte { return msgTypeNewRoundStep }
return &NewRoundStepMessage{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
Step: RoundStep(ReadUInt8(r, n, err)),
SecondsSinceStartTime: ReadUInt32(r, n, err),
}
}
func (m *NewRoundStepMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, msgTypeNewRoundStep, &n, &err)
WriteUInt32(w, m.Height, &n, &err)
WriteUInt16(w, m.Round, &n, &err)
WriteUInt8(w, uint8(m.Step), &n, &err)
WriteUInt32(w, m.SecondsSinceStartTime, &n, &err)
return
}
func (m *NewRoundStepMessage) String() string { func (m *NewRoundStepMessage) String() string {
return fmt.Sprintf("[NewRoundStep %v/%v/%X]", m.Height, m.Round, m.Step) return fmt.Sprintf("[NewRoundStep %v/%v/%X]", m.Height, m.Round, m.Step)
@ -685,30 +673,16 @@ func (m *NewRoundStepMessage) String() string {
//------------------------------------- //-------------------------------------
type CommitMessage struct { type CommitStepMessage struct {
Height uint32 Height uint
BlockParts PartSetHeader BlockParts PartSetHeader
BlockBitArray BitArray BlockBitArray BitArray
} }
func readCommitMessage(r io.Reader, n *int64, err *error) *CommitMessage { func (m *CommitStepMessage) TypeByte() byte { return msgTypeCommitStep }
return &CommitMessage{
Height: ReadUInt32(r, n, err),
BlockParts: ReadPartSetHeader(r, n, err),
BlockBitArray: ReadBitArray(r, n, err),
}
}
func (m *CommitMessage) WriteTo(w io.Writer) (n int64, err error) { func (m *CommitStepMessage) String() string {
WriteByte(w, msgTypeCommit, &n, &err) return fmt.Sprintf("[CommitStep %v %v %v]", m.Height, m.BlockParts, m.BlockBitArray)
WriteUInt32(w, m.Height, &n, &err)
WriteBinary(w, m.BlockParts, &n, &err)
WriteBinary(w, m.BlockBitArray, &n, &err)
return
}
func (m *CommitMessage) String() string {
return fmt.Sprintf("[Commit %v %v %v]", m.Height, m.BlockParts, m.BlockBitArray)
} }
//------------------------------------- //-------------------------------------
@ -719,29 +693,13 @@ const (
) )
type PartMessage struct { type PartMessage struct {
Height uint32 Height uint
Round uint16 Round uint
Type byte Type byte
Part *Part Part *Part
} }
func readPartMessage(r io.Reader, n *int64, err *error) *PartMessage { func (m *PartMessage) TypeByte() byte { return msgTypePart }
return &PartMessage{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
Type: ReadByte(r, n, err),
Part: ReadPart(r, n, err),
}
}
func (m *PartMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, msgTypePart, &n, &err)
WriteUInt32(w, m.Height, &n, &err)
WriteUInt16(w, m.Round, &n, &err)
WriteByte(w, m.Type, &n, &err)
WriteBinary(w, m.Part, &n, &err)
return
}
func (m *PartMessage) String() string { func (m *PartMessage) String() string {
return fmt.Sprintf("[Part %v/%v T:%X %v]", m.Height, m.Round, m.Type, m.Part) return fmt.Sprintf("[Part %v/%v T:%X %v]", m.Height, m.Round, m.Type, m.Part)
@ -749,30 +707,27 @@ func (m *PartMessage) String() string {
//------------------------------------- //-------------------------------------
type VoteMessage struct {
ValidatorIndex uint
Vote *Vote
}
func (m *VoteMessage) TypeByte() byte { return msgTypeVote }
func (m *VoteMessage) String() string {
return fmt.Sprintf("[Vote ValidatorIndex:%v Vote:%v]", m.ValidatorIndex, m.Vote)
}
//-------------------------------------
type HasVoteMessage struct { type HasVoteMessage struct {
Height uint32 Height uint
Round uint16 Round uint
Type byte Type byte
Index uint Index uint
} }
func readHasVoteMessage(r io.Reader, n *int64, err *error) *HasVoteMessage { func (m *HasVoteMessage) TypeByte() byte { return msgTypeHasVote }
return &HasVoteMessage{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
Type: ReadByte(r, n, err),
Index: ReadUVarInt(r, n, err),
}
}
func (m *HasVoteMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, msgTypeHasVote, &n, &err)
WriteUInt32(w, m.Height, &n, &err)
WriteUInt16(w, m.Round, &n, &err)
WriteByte(w, m.Type, &n, &err)
WriteUVarInt(w, m.Index, &n, &err)
return
}
func (m *HasVoteMessage) String() string { func (m *HasVoteMessage) String() string {
return fmt.Sprintf("[HasVote %v/%v T:%X]", m.Height, m.Round, m.Type) return fmt.Sprintf("[HasVote %v/%v T:%X]", m.Height, m.Round, m.Type)

View File

@ -52,6 +52,7 @@ Consensus State Machine Overview:
package consensus package consensus
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"math" "math"
@ -59,6 +60,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
. "github.com/tendermint/tendermint/account"
. "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"
@ -82,7 +84,7 @@ const (
RoundActionPrevote = RoundActionType(0xA1) // Prevote and goto RoundStepPrevote RoundActionPrevote = RoundActionType(0xA1) // Prevote and goto RoundStepPrevote
RoundActionPrecommit = RoundActionType(0xA2) // Precommit and goto RoundStepPrecommit RoundActionPrecommit = RoundActionType(0xA2) // Precommit and goto RoundStepPrecommit
RoundActionTryCommit = RoundActionType(0xC0) // Goto RoundStepCommit, or RoundStepPropose for next round. RoundActionTryCommit = RoundActionType(0xC0) // Goto RoundStepCommit, or RoundStepPropose for next round.
RoundActionCommit = RoundActionType(0xC1) // Goto RoundStepCommit RoundActionCommit = RoundActionType(0xC1) // Goto RoundStepCommit upon +2/3 commits
RoundActionTryFinalize = RoundActionType(0xC2) // Maybe goto RoundStepPropose for next round. RoundActionTryFinalize = RoundActionType(0xC2) // Maybe goto RoundStepPropose for next round.
roundDuration0 = 60 * time.Second // The first round is 60 seconds long. roundDuration0 = 60 * time.Second // The first round is 60 seconds long.
@ -97,8 +99,8 @@ var (
) )
type RoundAction struct { type RoundAction struct {
Height uint32 // The block height for which consensus is reaching for. Height uint // The block height for which consensus is reaching for.
Round uint16 // The round number at given height. Round uint // The round number at given height.
Action RoundActionType // Action to perform. Action RoundActionType // Action to perform.
} }
@ -106,8 +108,8 @@ type RoundAction struct {
// Immutable when returned from ConsensusState.GetRoundState() // Immutable when returned from ConsensusState.GetRoundState()
type RoundState struct { type RoundState struct {
Height uint32 // Height we are working on Height uint // Height we are working on
Round uint16 Round uint
Step RoundStep Step RoundStep
StartTime time.Time StartTime time.Time
CommitTime time.Time // Time when +2/3 commits were found CommitTime time.Time // Time when +2/3 commits were found
@ -347,7 +349,6 @@ ACTION_LOOP:
if rs.Precommits.HasTwoThirdsMajority() { if rs.Precommits.HasTwoThirdsMajority() {
// Enter RoundStepCommit and commit. // Enter RoundStepCommit and commit.
cs.RunActionCommit(rs.Height) cs.RunActionCommit(rs.Height)
cs.queueAction(RoundAction{rs.Height, rs.Round, RoundActionTryFinalize})
continue ACTION_LOOP continue ACTION_LOOP
} else { } else {
// Could not commit, move onto next round. // Could not commit, move onto next round.
@ -363,7 +364,6 @@ ACTION_LOOP:
} }
// Enter RoundStepCommit and commit. // Enter RoundStepCommit and commit.
cs.RunActionCommit(rs.Height) cs.RunActionCommit(rs.Height)
cs.queueAction(RoundAction{rs.Height, rs.Round, RoundActionTryFinalize})
continue ACTION_LOOP continue ACTION_LOOP
case RoundActionTryFinalize: case RoundActionTryFinalize:
@ -435,7 +435,7 @@ func (cs *ConsensusState) updateToState(state *state.State) {
} }
// After the call cs.Step becomes RoundStepNewRound. // After the call cs.Step becomes RoundStepNewRound.
func (cs *ConsensusState) setupNewRound(round uint16) { func (cs *ConsensusState) setupNewRound(round uint) {
// Sanity check // Sanity check
if round == 0 { if round == 0 {
panic("setupNewRound() should never be called for round 0") panic("setupNewRound() should never be called for round 0")
@ -470,7 +470,7 @@ func (cs *ConsensusState) SetPrivValidator(priv *PrivValidator) {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Set up the round to desired round and set step to RoundStepNewRound // Set up the round to desired round and set step to RoundStepNewRound
func (cs *ConsensusState) SetupNewRound(height uint32, desiredRound uint16) bool { func (cs *ConsensusState) SetupNewRound(height uint, desiredRound uint) bool {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
if cs.Height != height { if cs.Height != height {
@ -485,7 +485,7 @@ func (cs *ConsensusState) SetupNewRound(height uint32, desiredRound uint16) bool
return true return true
} }
func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) { func (cs *ConsensusState) RunActionPropose(height uint, round uint) {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
if cs.Height != height || cs.Round != round { if cs.Height != height || cs.Round != round {
@ -497,7 +497,7 @@ func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
}() }()
// Nothing to do if it's not our turn. // Nothing to do if it's not our turn.
if cs.PrivValidator == nil || cs.Validators.Proposer().Id != cs.PrivValidator.Id { if cs.PrivValidator == nil || !bytes.Equal(cs.Validators.Proposer().Address, cs.PrivValidator.Address) {
return return
} }
@ -560,7 +560,7 @@ func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
// Make proposal // Make proposal
proposal := NewProposal(cs.Height, cs.Round, blockParts.Header(), polParts.Header()) proposal := NewProposal(cs.Height, cs.Round, blockParts.Header(), polParts.Header())
cs.PrivValidator.Sign(proposal) proposal.Signature = cs.PrivValidator.SignProposal(proposal)
// Set fields // Set fields
cs.Proposal = proposal cs.Proposal = proposal
@ -572,7 +572,7 @@ func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
// Prevote for LockedBlock if we're locked, or ProposealBlock if valid. // Prevote for LockedBlock if we're locked, or ProposealBlock if valid.
// Otherwise vote nil. // Otherwise vote nil.
func (cs *ConsensusState) RunActionPrevote(height uint32, round uint16) { func (cs *ConsensusState) RunActionPrevote(height uint, round uint) {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
if cs.Height != height || cs.Round != round { if cs.Height != height || cs.Round != round {
@ -612,7 +612,7 @@ func (cs *ConsensusState) RunActionPrevote(height uint32, round uint16) {
// Lock & Precommit the ProposalBlock if we have enough prevotes for it, // Lock & Precommit the ProposalBlock if we have enough prevotes for it,
// or unlock an existing lock if +2/3 of prevotes were nil. // or unlock an existing lock if +2/3 of prevotes were nil.
func (cs *ConsensusState) RunActionPrecommit(height uint32, round uint16) { func (cs *ConsensusState) RunActionPrecommit(height uint, round uint) {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
if cs.Height != height || cs.Round != round { if cs.Height != height || cs.Round != round {
@ -668,7 +668,11 @@ func (cs *ConsensusState) RunActionPrecommit(height uint32, round uint16) {
} }
// Enter commit step. See the diagram for details. // Enter commit step. See the diagram for details.
func (cs *ConsensusState) RunActionCommit(height uint32) { // There are two ways to enter this step:
// * After the Precommit step with +2/3 precommits, or,
// * Upon +2/3 commits regardless of current step
// Either way this action is run at most once per round.
func (cs *ConsensusState) RunActionCommit(height uint) {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
if cs.Height != height { if cs.Height != height {
@ -679,6 +683,7 @@ func (cs *ConsensusState) RunActionCommit(height uint32) {
cs.newStepCh <- cs.getRoundState() cs.newStepCh <- cs.getRoundState()
}() }()
// Sanity check.
// There are two ways to enter: // There are two ways to enter:
// 1. +2/3 precommits at the end of RoundStepPrecommit // 1. +2/3 precommits at the end of RoundStepPrecommit
// 2. +2/3 commits at any time // 2. +2/3 commits at any time
@ -712,15 +717,20 @@ func (cs *ConsensusState) RunActionCommit(height uint32) {
} }
} else { } else {
// We have the block, so save/stage/sign-commit-vote. // We have the block, so save/stage/sign-commit-vote.
cs.saveCommitVoteBlock(cs.ProposalBlock, cs.ProposalBlockParts)
}
cs.processBlockForCommit(cs.ProposalBlock, cs.ProposalBlockParts) // If we have the block AND +2/3 commits, queue RoundActionTryFinalize.
// Round will immediately become finalized.
if cs.ProposalBlock.HashesTo(hash) && cs.Commits.HasTwoThirdsMajority() {
cs.queueAction(RoundAction{cs.Height, cs.Round, RoundActionTryFinalize})
} }
} }
// Returns true if Finalize happened, which increments height && sets // Returns true if Finalize happened, which increments height && sets
// the step to RoundStepNewHeight (or RoundStepNewRound, but probably not). // the step to RoundStepNewHeight (or RoundStepNewRound, but probably not).
func (cs *ConsensusState) TryFinalizeCommit(height uint32) bool { func (cs *ConsensusState) TryFinalizeCommit(height uint) bool {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
@ -754,6 +764,7 @@ func (cs *ConsensusState) TryFinalizeCommit(height uint32) bool {
return true return true
} else { } else {
// Prevent zombies. // Prevent zombies.
// TODO: Does this ever happen?
Panicf("+2/3 committed an invalid block: %v", err) Panicf("+2/3 committed an invalid block: %v", err)
} }
} }
@ -782,7 +793,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
} }
// Verify signature // Verify signature
if !cs.Validators.Proposer().Verify(proposal) { if !cs.Validators.Proposer().PubKey.VerifyBytes(SignBytes(proposal), proposal.Signature) {
return ErrInvalidProposalSignature return ErrInvalidProposalSignature
} }
@ -794,7 +805,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
// NOTE: block is not necessarily valid. // NOTE: block is not necessarily valid.
// NOTE: This function may increment the height. // NOTE: This function may increment the height.
func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part *Part) (added bool, err error) { func (cs *ConsensusState) AddProposalBlockPart(height uint, round uint, part *Part) (added bool, err error) {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
@ -815,8 +826,11 @@ func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part
if added && cs.ProposalBlockParts.IsComplete() { if added && cs.ProposalBlockParts.IsComplete() {
var n int64 var n int64
var err error var err error
cs.ProposalBlock = ReadBlock(cs.ProposalBlockParts.GetReader(), &n, &err) cs.ProposalBlock = ReadBinary(&Block{}, cs.ProposalBlockParts.GetReader(), &n, &err).(*Block)
cs.queueAction(RoundAction{cs.Height, cs.Round, RoundActionTryFinalize}) // If we're already in the commit step, try to finalize round.
if cs.Step == RoundStepCommit {
cs.queueAction(RoundAction{cs.Height, cs.Round, RoundActionTryFinalize})
}
// XXX If POL is valid, consider unlocking. // XXX If POL is valid, consider unlocking.
return true, err return true, err
} }
@ -824,7 +838,7 @@ func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part
} }
// NOTE: POL is not necessarily valid. // NOTE: POL is not necessarily valid.
func (cs *ConsensusState) AddProposalPOLPart(height uint32, round uint16, part *Part) (added bool, err error) { func (cs *ConsensusState) AddProposalPOLPart(height uint, round uint, part *Part) (added bool, err error) {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
@ -844,21 +858,21 @@ func (cs *ConsensusState) AddProposalPOLPart(height uint32, round uint16, part *
if added && cs.ProposalPOLParts.IsComplete() { if added && cs.ProposalPOLParts.IsComplete() {
var n int64 var n int64
var err error var err error
cs.ProposalPOL = ReadPOL(cs.ProposalPOLParts.GetReader(), &n, &err) cs.ProposalPOL = ReadBinary(&POL{}, cs.ProposalPOLParts.GetReader(), &n, &err).(*POL)
return true, err return true, err
} }
return true, nil return true, nil
} }
func (cs *ConsensusState) AddVote(vote *Vote) (added bool, index uint, err error) { func (cs *ConsensusState) AddVote(address []byte, vote *Vote) (added bool, index uint, err error) {
cs.mtx.Lock() cs.mtx.Lock()
defer cs.mtx.Unlock() defer cs.mtx.Unlock()
return cs.addVote(vote) return cs.addVote(address, vote)
} }
// TODO: Maybe move this out of here? // TODO: Maybe move this out of here?
func (cs *ConsensusState) LoadHeaderValidation(height uint32) (*Header, *Validation) { func (cs *ConsensusState) LoadHeaderValidation(height uint) (*Header, *Validation) {
meta := cs.blockStore.LoadBlockMeta(height) meta := cs.blockStore.LoadBlockMeta(height)
if meta == nil { if meta == nil {
return nil, nil return nil, nil
@ -869,21 +883,21 @@ func (cs *ConsensusState) LoadHeaderValidation(height uint32) (*Header, *Validat
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
func (cs *ConsensusState) addVote(vote *Vote) (added bool, index uint, err error) { func (cs *ConsensusState) addVote(address []byte, vote *Vote) (added bool, index uint, err error) {
switch vote.Type { switch vote.Type {
case VoteTypePrevote: case VoteTypePrevote:
// Prevotes checks for height+round match. // Prevotes checks for height+round match.
return cs.Prevotes.Add(vote) return cs.Prevotes.Add(address, vote)
case VoteTypePrecommit: case VoteTypePrecommit:
// Precommits checks for height+round match. // Precommits checks for height+round match.
return cs.Precommits.Add(vote) return cs.Precommits.Add(address, vote)
case VoteTypeCommit: case VoteTypeCommit:
if vote.Height == cs.Height { if vote.Height == cs.Height {
// No need to check if vote.Round < cs.Round ... // No need to check if vote.Round < cs.Round ...
// Prevotes && Precommits already checks that. // Prevotes && Precommits already checks that.
cs.Prevotes.Add(vote) cs.Prevotes.Add(address, vote)
cs.Precommits.Add(vote) cs.Precommits.Add(address, vote)
added, index, err = cs.Commits.Add(vote) added, index, err = cs.Commits.Add(address, vote)
if added && cs.Commits.HasTwoThirdsMajority() && cs.CommitTime.IsZero() { if added && cs.Commits.HasTwoThirdsMajority() && cs.CommitTime.IsZero() {
cs.CommitTime = time.Now() cs.CommitTime = time.Now()
log.Debug("Set CommitTime to %v", cs.CommitTime) log.Debug("Set CommitTime to %v", cs.CommitTime)
@ -896,7 +910,7 @@ func (cs *ConsensusState) addVote(vote *Vote) (added bool, index uint, err error
return added, index, err return added, index, err
} }
if vote.Height+1 == cs.Height { if vote.Height+1 == cs.Height {
return cs.LastCommits.Add(vote) return cs.LastCommits.Add(address, vote)
} }
return false, 0, nil return false, 0, nil
default: default:
@ -930,7 +944,7 @@ func (cs *ConsensusState) stageBlock(block *Block, blockParts *PartSet) error {
} }
func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header PartSetHeader) *Vote { func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header PartSetHeader) *Vote {
if cs.PrivValidator == nil || !cs.Validators.HasId(cs.PrivValidator.Id) { if cs.PrivValidator == nil || !cs.Validators.HasAddress(cs.PrivValidator.Address) {
return nil return nil
} }
vote := &Vote{ vote := &Vote{
@ -940,12 +954,12 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header PartSetHea
BlockHash: hash, BlockHash: hash,
BlockParts: header, BlockParts: header,
} }
cs.PrivValidator.Sign(vote) vote.Signature = cs.PrivValidator.SignVote(vote)
cs.addVote(vote) cs.addVote(cs.PrivValidator.Address, vote)
return vote return vote
} }
func (cs *ConsensusState) processBlockForCommit(block *Block, blockParts *PartSet) { func (cs *ConsensusState) saveCommitVoteBlock(block *Block, blockParts *PartSet) {
// The proposal must be valid. // The proposal must be valid.
if err := cs.stageBlock(block, blockParts); err != nil { if err := cs.stageBlock(block, blockParts); err != nil {
@ -969,19 +983,19 @@ func (cs *ConsensusState) processBlockForCommit(block *Block, blockParts *PartSe
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// total duration of given round // total duration of given round
func calcRoundDuration(round uint16) time.Duration { func calcRoundDuration(round uint) time.Duration {
return roundDuration0 + roundDurationDelta*time.Duration(round) return roundDuration0 + roundDurationDelta*time.Duration(round)
} }
// startTime is when round zero started. // startTime is when round zero started.
func calcRoundStartTime(round uint16, startTime time.Time) time.Time { func calcRoundStartTime(round uint, startTime time.Time) time.Time {
return startTime.Add(roundDuration0*time.Duration(round) + return startTime.Add(roundDuration0*time.Duration(round) +
roundDurationDelta*(time.Duration((int64(round)*int64(round)-int64(round))/2))) roundDurationDelta*(time.Duration((int64(round)*int64(round)-int64(round))/2)))
} }
// calculates the current round given startTime of round zero. // calculates the current round given startTime of round zero.
// NOTE: round is zero if startTime is in the future. // NOTE: round is zero if startTime is in the future.
func calcRound(startTime time.Time) uint16 { func calcRound(startTime time.Time) uint {
now := time.Now() now := time.Now()
if now.Before(startTime) { if now.Before(startTime) {
return 0 return 0
@ -997,18 +1011,18 @@ func calcRound(startTime time.Time) uint16 {
if math.IsNaN(R) { if math.IsNaN(R) {
panic("Could not calc round, should not happen") panic("Could not calc round, should not happen")
} }
if R > math.MaxInt16 { if R > math.MaxInt32 {
Panicf("Could not calc round, round overflow: %v", R) Panicf("Could not calc round, round overflow: %v", R)
} }
if R < 0 { if R < 0 {
return 0 return 0
} }
return uint16(R) return uint(R)
} }
// convenience // convenience
// NOTE: elapsedRatio can be negative if startTime is in the future. // NOTE: elapsedRatio can be negative if startTime is in the future.
func calcRoundInfo(startTime time.Time) (round uint16, roundStartTime time.Time, roundDuration time.Duration, func calcRoundInfo(startTime time.Time) (round uint, roundStartTime time.Time, roundDuration time.Duration,
roundElapsed time.Duration, elapsedRatio float64) { roundElapsed time.Duration, elapsedRatio float64) {
round = calcRound(startTime) round = calcRound(startTime)
roundStartTime = calcRoundStartTime(round, startTime) roundStartTime = calcRoundStartTime(round, startTime)

View File

@ -6,23 +6,27 @@ import (
// Common test methods // Common test methods
func makeValidator(id uint64, votingPower uint64) (*state.Validator, *state.PrivAccount) { func makeValidator(votingPower uint64) (*state.Validator, *PrivValidator) {
privAccount := state.GenPrivAccount() privValidator := GenPrivValidator()
privAccount.Id = id
return &state.Validator{ return &state.Validator{
Account: privAccount.Account, Address: privValidator.Address,
VotingPower: votingPower, PubKey: privValidator.PubKey,
}, privAccount BondHeight: 0,
UnbondHeight: 0,
LastCommitHeight: 0,
VotingPower: votingPower,
Accum: 0,
}, privValidator
} }
func makeVoteSet(height uint32, round uint16, type_ byte, numValidators int, votingPower uint64) (*VoteSet, *state.ValidatorSet, []*state.PrivAccount) { func makeVoteSet(height uint, round uint, type_ byte, numValidators int, votingPower uint64) (*VoteSet, *state.ValidatorSet, []*PrivValidator) {
vals := make([]*state.Validator, numValidators) vals := make([]*state.Validator, numValidators)
privAccounts := make([]*state.PrivAccount, numValidators) privValidators := make([]*PrivValidator, numValidators)
for i := 0; i < numValidators; i++ { for i := 0; i < numValidators; i++ {
val, privAccount := makeValidator(uint64(i), votingPower) val, privValidator := makeValidator(votingPower)
vals[i] = val vals[i] = val
privAccounts[i] = privAccount privValidators[i] = privValidator
} }
valSet := state.NewValidatorSet(vals) valSet := state.NewValidatorSet(vals)
return NewVoteSet(height, round, type_, valSet), valSet, privAccounts return NewVoteSet(height, round, type_, valSet), valSet, privValidators
} }

View File

@ -6,6 +6,7 @@ import (
"strings" "strings"
"sync" "sync"
. "github.com/tendermint/tendermint/account"
. "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"
@ -16,17 +17,16 @@ import (
// for a predefined vote type. // for a predefined vote type.
// Note that there three kinds of votes: prevotes, precommits, and commits. // Note that there three kinds of votes: prevotes, precommits, and commits.
// A commit of prior rounds can be added added in lieu of votes/precommits. // A commit of prior rounds can be added added in lieu of votes/precommits.
// TODO: test majority calculations etc. // NOTE: Assumes that the sum total of voting power does not exceed MaxUInt64.
// NOTE: assumes that the sum total of voting power does not exceed MaxUInt64.
type VoteSet struct { type VoteSet struct {
height uint32 height uint
round uint16 round uint
type_ byte type_ byte
mtx sync.Mutex mtx sync.Mutex
vset *state.ValidatorSet valSet *state.ValidatorSet
votes map[uint64]*Vote votes []*Vote // validator index -> vote
votesBitArray BitArray votesBitArray BitArray // validator index -> has vote?
votesByBlock map[string]uint64 // string(blockHash)+string(blockParts) -> vote sum. votesByBlock map[string]uint64 // string(blockHash)+string(blockParts) -> vote sum.
totalVotes uint64 totalVotes uint64
maj23Hash []byte maj23Hash []byte
@ -35,7 +35,7 @@ type VoteSet struct {
} }
// Constructs a new VoteSet struct used to accumulate votes for each round. // Constructs a new VoteSet struct used to accumulate votes for each round.
func NewVoteSet(height uint32, round uint16, type_ byte, vset *state.ValidatorSet) *VoteSet { func NewVoteSet(height uint, round uint, type_ byte, valSet *state.ValidatorSet) *VoteSet {
if height == 0 { if height == 0 {
panic("Cannot make VoteSet for height == 0, doesn't make sense.") panic("Cannot make VoteSet for height == 0, doesn't make sense.")
} }
@ -46,55 +46,55 @@ func NewVoteSet(height uint32, round uint16, type_ byte, vset *state.ValidatorSe
height: height, height: height,
round: round, round: round,
type_: type_, type_: type_,
vset: vset, valSet: valSet,
votes: make(map[uint64]*Vote, vset.Size()), votes: make([]*Vote, valSet.Size()),
votesBitArray: NewBitArray(vset.Size()), votesBitArray: NewBitArray(valSet.Size()),
votesByBlock: make(map[string]uint64), votesByBlock: make(map[string]uint64),
totalVotes: 0, totalVotes: 0,
} }
} }
func (vs *VoteSet) Size() uint { func (voteSet *VoteSet) Size() uint {
if vs == nil { if voteSet == nil {
return 0 return 0
} else { } else {
return vs.vset.Size() return voteSet.valSet.Size()
} }
} }
// True if added, false if not. // True if added, false if not.
// Returns ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature] // Returns ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
// NOTE: vote should not be mutated after adding. // NOTE: vote should not be mutated after adding.
func (vs *VoteSet) Add(vote *Vote) (bool, uint, error) { func (voteSet *VoteSet) Add(address []byte, vote *Vote) (bool, uint, error) {
vs.mtx.Lock() voteSet.mtx.Lock()
defer vs.mtx.Unlock() defer voteSet.mtx.Unlock()
// Make sure the step matches. (or that vote is commit && round < vs.round) // Make sure the step matches. (or that vote is commit && round < voteSet.round)
if vote.Height != vs.height || if vote.Height != voteSet.height ||
(vote.Type != VoteTypeCommit && vote.Round != vs.round) || (vote.Type != VoteTypeCommit && vote.Round != voteSet.round) ||
(vote.Type != VoteTypeCommit && vote.Type != vs.type_) || (vote.Type != VoteTypeCommit && vote.Type != voteSet.type_) ||
(vote.Type == VoteTypeCommit && vs.type_ != VoteTypeCommit && vote.Round >= vs.round) { (vote.Type == VoteTypeCommit && voteSet.type_ != VoteTypeCommit && vote.Round >= voteSet.round) {
return false, 0, ErrVoteUnexpectedStep return false, 0, ErrVoteUnexpectedStep
} }
// Ensure that signer is a validator. // Ensure that signer is a validator.
_, val := vs.vset.GetById(vote.SignerId) valIndex, val := voteSet.valSet.GetByAddress(address)
if val == nil { if val == nil {
return false, 0, ErrVoteInvalidAccount return false, 0, ErrVoteInvalidAccount
} }
// Check signature. // Check signature.
if !val.Verify(vote) { if !val.PubKey.VerifyBytes(SignBytes(vote), vote.Signature) {
// Bad signature. // Bad signature.
return false, 0, ErrVoteInvalidSignature return false, 0, ErrVoteInvalidSignature
} }
return vs.addVote(vote) return voteSet.addVote(valIndex, vote)
} }
func (vs *VoteSet) addVote(vote *Vote) (bool, uint, error) { func (voteSet *VoteSet) addVote(valIndex uint, vote *Vote) (bool, uint, error) {
// If vote already exists, return false. // If vote already exists, return false.
if existingVote, ok := vs.votes[vote.SignerId]; ok { if existingVote := voteSet.votes[valIndex]; existingVote != nil {
if bytes.Equal(existingVote.BlockHash, vote.BlockHash) { if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
return false, 0, nil return false, 0, nil
} else { } else {
@ -103,170 +103,154 @@ func (vs *VoteSet) addVote(vote *Vote) (bool, uint, error) {
} }
// Add vote. // Add vote.
vs.votes[vote.SignerId] = vote _, val := voteSet.valSet.GetByIndex(valIndex)
voterIndex, val := vs.vset.GetById(vote.SignerId)
if val == nil { if val == nil {
return false, 0, ErrVoteInvalidAccount panic(fmt.Sprintf("Missing validator for index %v", valIndex))
} }
vs.votesBitArray.SetIndex(uint(voterIndex), true) voteSet.votes[valIndex] = vote
voteSet.votesBitArray.SetIndex(valIndex, true)
blockKey := string(vote.BlockHash) + string(BinaryBytes(vote.BlockParts)) blockKey := string(vote.BlockHash) + string(BinaryBytes(vote.BlockParts))
totalBlockHashVotes := vs.votesByBlock[blockKey] + val.VotingPower totalBlockHashVotes := voteSet.votesByBlock[blockKey] + val.VotingPower
vs.votesByBlock[blockKey] = totalBlockHashVotes voteSet.votesByBlock[blockKey] = totalBlockHashVotes
vs.totalVotes += val.VotingPower voteSet.totalVotes += val.VotingPower
// If we just nudged it up to two thirds majority, add it. // If we just nudged it up to two thirds majority, add it.
if totalBlockHashVotes > vs.vset.TotalVotingPower()*2/3 && if totalBlockHashVotes > voteSet.valSet.TotalVotingPower()*2/3 &&
(totalBlockHashVotes-val.VotingPower) <= vs.vset.TotalVotingPower()*2/3 { (totalBlockHashVotes-val.VotingPower) <= voteSet.valSet.TotalVotingPower()*2/3 {
vs.maj23Hash = vote.BlockHash voteSet.maj23Hash = vote.BlockHash
vs.maj23Parts = vote.BlockParts voteSet.maj23Parts = vote.BlockParts
vs.maj23Exists = true voteSet.maj23Exists = true
} }
return true, voterIndex, nil return true, valIndex, nil
} }
// Assumes that commits VoteSet is valid. // Assumes that commits VoteSet is valid.
func (vs *VoteSet) AddFromCommits(commits *VoteSet) { func (voteSet *VoteSet) AddFromCommits(commits *VoteSet) {
commitVotes := commits.AllVotes() for valIndex, commit := range commits.votes {
for _, commit := range commitVotes { if commit.Round < voteSet.round {
if commit.Round < vs.round { voteSet.addVote(uint(valIndex), commit)
vs.addVote(commit)
} }
} }
} }
func (vs *VoteSet) BitArray() BitArray { func (voteSet *VoteSet) BitArray() BitArray {
if vs == nil { if voteSet == nil {
return BitArray{} return BitArray{}
} }
vs.mtx.Lock() voteSet.mtx.Lock()
defer vs.mtx.Unlock() defer voteSet.mtx.Unlock()
return vs.votesBitArray.Copy() return voteSet.votesBitArray.Copy()
} }
func (vs *VoteSet) GetByIndex(index uint) *Vote { func (voteSet *VoteSet) GetByIndex(valIndex uint) *Vote {
vs.mtx.Lock() voteSet.mtx.Lock()
defer vs.mtx.Unlock() defer voteSet.mtx.Unlock()
return voteSet.votes[valIndex]
}
id, val := vs.vset.GetByIndex(index) func (voteSet *VoteSet) GetByAddress(address []byte) *Vote {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
valIndex, val := voteSet.valSet.GetByAddress(address)
if val == nil { if val == nil {
panic("GetByIndex(index) returned nil") panic("GetByAddress(address) returned nil")
} }
return voteSet.votes[valIndex]
return vs.votes[id]
} }
func (vs *VoteSet) GetById(id uint64) *Vote { func (voteSet *VoteSet) HasTwoThirdsMajority() bool {
vs.mtx.Lock() if voteSet == nil {
defer vs.mtx.Unlock()
return vs.votes[id]
}
func (vs *VoteSet) AllVotes() []*Vote {
vs.mtx.Lock()
defer vs.mtx.Unlock()
votes := []*Vote{}
for _, vote := range vs.votes {
votes = append(votes, vote)
}
return votes
}
func (vs *VoteSet) HasTwoThirdsMajority() bool {
if vs == nil {
return false return false
} }
vs.mtx.Lock() voteSet.mtx.Lock()
defer vs.mtx.Unlock() defer voteSet.mtx.Unlock()
return vs.maj23Exists return voteSet.maj23Exists
} }
// Returns either a blockhash (or nil) that received +2/3 majority. // Returns either a blockhash (or nil) that received +2/3 majority.
// If there exists no such majority, returns (nil, false). // If there exists no such majority, returns (nil, false).
func (vs *VoteSet) TwoThirdsMajority() (hash []byte, parts PartSetHeader, ok bool) { func (voteSet *VoteSet) TwoThirdsMajority() (hash []byte, parts PartSetHeader, ok bool) {
vs.mtx.Lock() voteSet.mtx.Lock()
defer vs.mtx.Unlock() defer voteSet.mtx.Unlock()
if vs.maj23Exists { if voteSet.maj23Exists {
return vs.maj23Hash, vs.maj23Parts, true return voteSet.maj23Hash, voteSet.maj23Parts, true
} else { } else {
return nil, PartSetHeader{}, false return nil, PartSetHeader{}, false
} }
} }
func (vs *VoteSet) MakePOL() *POL { func (voteSet *VoteSet) MakePOL() *POL {
if vs.type_ != VoteTypePrevote { if voteSet.type_ != VoteTypePrevote {
panic("Cannot MakePOL() unless VoteSet.Type is VoteTypePrevote") panic("Cannot MakePOL() unless VoteSet.Type is VoteTypePrevote")
} }
vs.mtx.Lock() voteSet.mtx.Lock()
defer vs.mtx.Unlock() defer voteSet.mtx.Unlock()
if !vs.maj23Exists { if !voteSet.maj23Exists {
return nil return nil
} }
pol := &POL{ pol := &POL{
Height: vs.height, Height: voteSet.height,
Round: vs.round, Round: voteSet.round,
BlockHash: vs.maj23Hash, BlockHash: voteSet.maj23Hash,
BlockParts: vs.maj23Parts, BlockParts: voteSet.maj23Parts,
Votes: make([]POLVoteSignature, voteSet.valSet.Size()),
} }
for _, vote := range vs.votes { for valIndex, vote := range voteSet.votes {
if !bytes.Equal(vote.BlockHash, vs.maj23Hash) { if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) {
continue continue
} }
if !vote.BlockParts.Equals(vs.maj23Parts) { if !vote.BlockParts.Equals(voteSet.maj23Parts) {
continue continue
} }
if vote.Type == VoteTypePrevote { pol.Votes[valIndex] = POLVoteSignature{
pol.Votes = append(pol.Votes, vote.Signature) Round: vote.Round,
} else if vote.Type == VoteTypeCommit { Signature: vote.Signature,
pol.Commits = append(pol.Commits, RoundSignature{vote.Round, vote.Signature})
} else {
Panicf("Unexpected vote type %X", vote.Type)
} }
} }
return pol return pol
} }
func (vs *VoteSet) MakeValidation() *Validation { func (voteSet *VoteSet) MakeValidation() *Validation {
if vs.type_ != VoteTypeCommit { if voteSet.type_ != VoteTypeCommit {
panic("Cannot MakeValidation() unless VoteSet.Type is VoteTypeCommit") panic("Cannot MakeValidation() unless VoteSet.Type is VoteTypeCommit")
} }
vs.mtx.Lock() voteSet.mtx.Lock()
defer vs.mtx.Unlock() defer voteSet.mtx.Unlock()
if len(vs.maj23Hash) == 0 { if len(voteSet.maj23Hash) == 0 {
panic("Cannot MakeValidation() unless a blockhash has +2/3") panic("Cannot MakeValidation() unless a blockhash has +2/3")
} }
rsigs := make([]RoundSignature, vs.vset.Size()) commits := make([]Commit, voteSet.valSet.Size())
vs.vset.Iterate(func(index uint, val *state.Validator) bool { voteSet.valSet.Iterate(func(valIndex uint, val *state.Validator) bool {
vote := vs.votes[val.Id] vote := voteSet.votes[valIndex]
if vote == nil { if vote == nil {
return false return false
} }
if !bytes.Equal(vote.BlockHash, vs.maj23Hash) { if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) {
return false return false
} }
if !vote.BlockParts.Equals(vs.maj23Parts) { if !vote.BlockParts.Equals(voteSet.maj23Parts) {
return false return false
} }
rsigs[index] = RoundSignature{vote.Round, vote.Signature} commits[valIndex] = Commit{vote.Round, vote.Signature}
return false return false
}) })
return &Validation{ return &Validation{
Commits: rsigs, Commits: commits,
} }
} }
func (vs *VoteSet) String() string { func (voteSet *VoteSet) String() string {
return vs.StringWithIndent("") return voteSet.StringWithIndent("")
} }
func (vs *VoteSet) StringWithIndent(indent string) string { func (voteSet *VoteSet) StringWithIndent(indent string) string {
vs.mtx.Lock() voteSet.mtx.Lock()
defer vs.mtx.Unlock() defer voteSet.mtx.Unlock()
voteStrings := make([]string, len(vs.votes)) voteStrings := make([]string, len(voteSet.votes))
counter := 0 counter := 0
for _, vote := range vs.votes { for _, vote := range voteSet.votes {
voteStrings[counter] = vote.String() voteStrings[counter] = vote.String()
counter++ counter++
} }
@ -275,18 +259,18 @@ func (vs *VoteSet) StringWithIndent(indent string) string {
%s %v %s %v
%s %v %s %v
%s}`, %s}`,
indent, vs.height, vs.round, vs.type_, indent, voteSet.height, voteSet.round, voteSet.type_,
indent, strings.Join(voteStrings, "\n"+indent+" "), indent, strings.Join(voteStrings, "\n"+indent+" "),
indent, vs.votesBitArray, indent, voteSet.votesBitArray,
indent) indent)
} }
func (vs *VoteSet) Description() string { func (voteSet *VoteSet) Description() string {
if vs == nil { if voteSet == nil {
return "nil-VoteSet" return "nil-VoteSet"
} }
vs.mtx.Lock() voteSet.mtx.Lock()
defer vs.mtx.Unlock() defer voteSet.mtx.Unlock()
return fmt.Sprintf(`VoteSet{H:%v R:%v T:%v %v}`, return fmt.Sprintf(`VoteSet{H:%v R:%v T:%v %v}`,
vs.height, vs.round, vs.type_, vs.votesBitArray) voteSet.height, voteSet.round, voteSet.type_, voteSet.votesBitArray)
} }

View File

@ -3,4 +3,7 @@ package db
type DB interface { type DB interface {
Get([]byte) []byte Get([]byte) []byte
Set([]byte, []byte) Set([]byte, []byte)
SetSync([]byte, []byte)
Delete([]byte)
DeleteSync([]byte)
} }

View File

@ -3,6 +3,7 @@ package db
import ( import (
"fmt" "fmt"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt"
"path" "path"
) )
@ -20,13 +21,6 @@ func NewLevelDB(name string) (*LevelDB, error) {
return database, nil return database, nil
} }
func (db *LevelDB) Set(key []byte, value []byte) {
err := db.db.Put(key, value, nil)
if err != nil {
panic(err)
}
}
func (db *LevelDB) Get(key []byte) []byte { func (db *LevelDB) Get(key []byte) []byte {
res, err := db.db.Get(key, nil) res, err := db.db.Get(key, nil)
if err != nil { if err != nil {
@ -35,6 +29,20 @@ func (db *LevelDB) Get(key []byte) []byte {
return res return res
} }
func (db *LevelDB) Set(key []byte, value []byte) {
err := db.db.Put(key, value, nil)
if err != nil {
panic(err)
}
}
func (db *LevelDB) SetSync(key []byte, value []byte) {
err := db.db.Put(key, value, &opt.WriteOptions{Sync: true})
if err != nil {
panic(err)
}
}
func (db *LevelDB) Delete(key []byte) { func (db *LevelDB) Delete(key []byte) {
err := db.db.Delete(key, nil) err := db.db.Delete(key, nil)
if err != nil { if err != nil {
@ -42,6 +50,13 @@ func (db *LevelDB) Delete(key []byte) {
} }
} }
func (db *LevelDB) DeleteSync(key []byte) {
err := db.db.Delete(key, &opt.WriteOptions{Sync: true})
if err != nil {
panic(err)
}
}
func (db *LevelDB) Db() *leveldb.DB { func (db *LevelDB) Db() *leveldb.DB {
return db.db return db.db
} }

View File

@ -13,18 +13,26 @@ func NewMemDB() *MemDB {
return database return database
} }
func (db *MemDB) Set(key []byte, value []byte) {
db.db[string(key)] = value
}
func (db *MemDB) Get(key []byte) []byte { func (db *MemDB) Get(key []byte) []byte {
return db.db[string(key)] return db.db[string(key)]
} }
func (db *MemDB) Set(key []byte, value []byte) {
db.db[string(key)] = value
}
func (db *MemDB) SetSync(key []byte, value []byte) {
db.db[string(key)] = value
}
func (db *MemDB) Delete(key []byte) { func (db *MemDB) Delete(key []byte) {
delete(db.db, string(key)) delete(db.db, string(key))
} }
func (db *MemDB) DeleteSync(key []byte) {
delete(db.db, string(key))
}
func (db *MemDB) Print() { func (db *MemDB) Print() {
for key, value := range db.db { for key, value := range db.db {
fmt.Printf("[%X]:\t[%X]\n", []byte(key), value) fmt.Printf("[%X]:\t[%X]\n", []byte(key), value)

View File

@ -59,14 +59,14 @@ func (mem *Mempool) ResetForBlockAndState(block *Block, state *state.State) {
// First, create a lookup map of txns in new block. // First, create a lookup map of txns in new block.
blockTxsMap := make(map[string]struct{}) blockTxsMap := make(map[string]struct{})
for _, tx := range block.Data.Txs { for _, tx := range block.Data.Txs {
txHash := BinaryHash(tx) txHash := BinarySha256(tx)
blockTxsMap[string(txHash)] = struct{}{} blockTxsMap[string(txHash)] = struct{}{}
} }
// Next, filter all txs from mem.txs that are in blockTxsMap // Next, filter all txs from mem.txs that are in blockTxsMap
txs := []Tx{} txs := []Tx{}
for _, tx := range mem.txs { for _, tx := range mem.txs {
txHash := BinaryHash(tx) txHash := BinarySha256(tx)
if _, ok := blockTxsMap[string(txHash)]; ok { if _, ok := blockTxsMap[string(txHash)]; ok {
continue continue
} else { } else {

View File

@ -3,7 +3,6 @@ package mempool
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"sync/atomic" "sync/atomic"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
@ -119,12 +118,10 @@ const (
// TODO: check for unnecessary extra bytes at the end. // TODO: check for unnecessary extra bytes at the end.
func decodeMessage(bz []byte) (msgType byte, msg interface{}) { func decodeMessage(bz []byte) (msgType byte, msg interface{}) {
n, err := new(int64), new(error) n, err := new(int64), new(error)
// log.Debug("decoding msg bytes: %X", bz)
msgType = bz[0] msgType = bz[0]
switch msgType { switch msgType {
case msgTypeTx: case msgTypeTx:
msg = readTxMessage(bytes.NewReader(bz[1:]), n, err) msg = ReadBinary(&TxMessage{}, bytes.NewReader(bz[1:]), n, err)
// case ...:
default: default:
msg = nil msg = nil
} }
@ -137,17 +134,7 @@ type TxMessage struct {
Tx Tx Tx Tx
} }
func readTxMessage(r io.Reader, n *int64, err *error) *TxMessage { func (m *TxMessage) TypeByte() byte { return msgTypeTx }
return &TxMessage{
Tx: ReadTx(r, n, err),
}
}
func (m *TxMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, msgTypeTx, &n, &err)
WriteBinary(w, m.Tx, &n, &err)
return
}
func (m *TxMessage) String() string { func (m *TxMessage) String() string {
return fmt.Sprintf("[TxMessage %v]", m.Tx) return fmt.Sprintf("[TxMessage %v]", m.Tx)

View File

@ -253,8 +253,8 @@ func (node *IAVLNode) remove(t *IAVLTree, key interface{}) (
// NOTE: sets hashes recursively // NOTE: sets hashes recursively
func (node *IAVLNode) writeToCountHashes(t *IAVLTree, w io.Writer) (n int64, hashCount uint64, err error) { func (node *IAVLNode) writeToCountHashes(t *IAVLTree, w io.Writer) (n int64, hashCount uint64, err error) {
// height & size & key // height & size & key
WriteUInt8(w, node.height, &n, &err) WriteUInt8(node.height, w, &n, &err)
WriteUInt64(w, node.size, &n, &err) WriteUInt64(node.size, w, &n, &err)
t.keyCodec.Encode(node.key, w, &n, &err) t.keyCodec.Encode(node.key, w, &n, &err)
if err != nil { if err != nil {
return return
@ -273,7 +273,7 @@ func (node *IAVLNode) writeToCountHashes(t *IAVLTree, w io.Writer) (n int64, has
if node.leftHash == nil { if node.leftHash == nil {
panic("node.leftHash was nil in save") panic("node.leftHash was nil in save")
} }
WriteByteSlice(w, node.leftHash, &n, &err) WriteByteSlice(node.leftHash, w, &n, &err)
// right // right
if node.rightNode != nil { if node.rightNode != nil {
rightHash, rightCount := node.rightNode.hashWithCount(t) rightHash, rightCount := node.rightNode.hashWithCount(t)
@ -283,7 +283,7 @@ func (node *IAVLNode) writeToCountHashes(t *IAVLTree, w io.Writer) (n int64, has
if node.rightHash == nil { if node.rightHash == nil {
panic("node.rightHash was nil in save") panic("node.rightHash was nil in save")
} }
WriteByteSlice(w, node.rightHash, &n, &err) WriteByteSlice(node.rightHash, w, &n, &err)
} }
return return
} }

View File

@ -35,8 +35,8 @@ func HashFromTwoHashes(left []byte, right []byte) []byte {
var n int64 var n int64
var err error var err error
var hasher = sha256.New() var hasher = sha256.New()
WriteByteSlice(hasher, left, &n, &err) WriteByteSlice(left, hasher, &n, &err)
WriteByteSlice(hasher, right, &n, &err) WriteByteSlice(right, hasher, &n, &err)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -58,16 +58,15 @@ func HashFromHashes(hashes [][]byte) []byte {
} }
// Convenience for HashFromHashes. // Convenience for HashFromHashes.
func HashFromBinaries(items []Binary) []byte { func HashFromBinaries(items []interface{}) []byte {
hashes := [][]byte{} hashes := [][]byte{}
for _, item := range items { for _, item := range items {
hasher := sha256.New() hasher, n, err := sha256.New(), new(int64), new(error)
_, err := item.WriteTo(hasher) WriteBinary(item, hasher, n, err)
if err != nil { if *err != nil {
panic(err) panic(err)
} }
hash := hasher.Sum(nil) hashes = append(hashes, hasher.Sum(nil))
hashes = append(hashes, hash)
} }
return HashFromHashes(hashes) return HashFromHashes(hashes)
} }

View File

@ -17,7 +17,7 @@ import (
) )
const ( const (
numBatchPackets = 10 numBatchMsgPackets = 10
minReadBufferSize = 1024 minReadBufferSize = 1024
minWriteBufferSize = 1024 minWriteBufferSize = 1024
flushThrottleMS = 50 flushThrottleMS = 50
@ -35,7 +35,7 @@ type errorCbFunc func(interface{})
/* /*
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)". <essages are sent with ".Send(channelId, msg)".
Inbound message bytes are handled with an onReceive callback function. Inbound message bytes are handled with an onReceive callback function.
*/ */
type MConnection struct { type MConnection struct {
@ -158,7 +158,7 @@ func (c *MConnection) stopForError(r interface{}) {
} }
// Queues a message to be sent to channel. // Queues a message to be sent to channel.
func (c *MConnection) Send(chId byte, msg Binary) bool { func (c *MConnection) Send(chId byte, msg interface{}) bool {
if atomic.LoadUint32(&c.stopped) == 1 { if atomic.LoadUint32(&c.stopped) == 1 {
return false return false
} }
@ -185,7 +185,7 @@ func (c *MConnection) Send(chId byte, msg Binary) bool {
// Queues a message to be sent to channel. // Queues a message to be sent to channel.
// Nonblocking, returns true if successful. // Nonblocking, returns true if successful.
func (c *MConnection) TrySend(chId byte, msg Binary) bool { func (c *MConnection) TrySend(chId byte, msg interface{}) bool {
if atomic.LoadUint32(&c.stopped) == 1 { if atomic.LoadUint32(&c.stopped) == 1 {
return false return false
} }
@ -242,18 +242,18 @@ FOR_LOOP:
channel.updateStats() channel.updateStats()
} }
case <-c.pingTimer.Ch: case <-c.pingTimer.Ch:
WriteByte(c.bufWriter, packetTypePing, &n, &err) WriteByte(packetTypePing, c.bufWriter, &n, &err)
c.sendMonitor.Update(int(n)) c.sendMonitor.Update(int(n))
c.flush() c.flush()
case <-c.pong: case <-c.pong:
WriteByte(c.bufWriter, packetTypePong, &n, &err) WriteByte(packetTypePing, c.bufWriter, &n, &err)
c.sendMonitor.Update(int(n)) c.sendMonitor.Update(int(n))
c.flush() c.flush()
case <-c.quit: case <-c.quit:
break FOR_LOOP break FOR_LOOP
case <-c.send: case <-c.send:
// Send some packets // Send some msgPackets
eof := c.sendSomePackets() eof := c.sendSomeMsgPackets()
if !eof { if !eof {
// Keep sendRoutine awake. // Keep sendRoutine awake.
select { select {
@ -278,15 +278,15 @@ FOR_LOOP:
// Returns true if messages from channels were exhausted. // Returns true if messages from channels were exhausted.
// Blocks in accordance to .sendMonitor throttling. // Blocks in accordance to .sendMonitor throttling.
func (c *MConnection) sendSomePackets() bool { func (c *MConnection) sendSomeMsgPackets() bool {
// Block until .sendMonitor says we can write. // Block until .sendMonitor says we can write.
// Once we're ready we send more than we asked for, // Once we're ready we send more than we asked for,
// but amortized it should even out. // but amortized it should even out.
c.sendMonitor.Limit(maxPacketSize, atomic.LoadInt64(&c.sendRate), true) c.sendMonitor.Limit(maxMsgPacketSize, atomic.LoadInt64(&c.sendRate), true)
// Now send some packets. // Now send some msgPackets.
for i := 0; i < numBatchPackets; i++ { for i := 0; i < numBatchMsgPackets; i++ {
if c.sendPacket() { if c.sendMsgPacket() {
return true return true
} }
} }
@ -294,8 +294,8 @@ func (c *MConnection) sendSomePackets() bool {
} }
// Returns true if messages from channels were exhausted. // Returns true if messages from channels were exhausted.
func (c *MConnection) sendPacket() bool { func (c *MConnection) sendMsgPacket() bool {
// Choose a channel to create a packet from. // Choose a channel to create a msgPacket from.
// The chosen channel will be the one whose recentlySent/priority is the least. // The chosen channel will be the one whose recentlySent/priority is the least.
var leastRatio float32 = math.MaxFloat32 var leastRatio float32 = math.MaxFloat32
var leastChannel *Channel var leastChannel *Channel
@ -316,13 +316,13 @@ func (c *MConnection) sendPacket() bool {
if leastChannel == nil { if leastChannel == nil {
return true return true
} else { } else {
// log.Debug("Found a packet to send") // log.Debug("Found a msgPacket to send")
} }
// Make & send a packet from this channel // Make & send a msgPacket from this channel
n, err := leastChannel.writePacketTo(c.bufWriter) n, err := leastChannel.writeMsgPacketTo(c.bufWriter)
if err != nil { if err != nil {
log.Warning("Failed to write packet. Error: %v", err) log.Warning("Failed to write msgPacket. Error: %v", err)
c.stopForError(err) c.stopForError(err)
return true return true
} }
@ -331,7 +331,7 @@ func (c *MConnection) sendPacket() bool {
return false return false
} }
// recvRoutine reads packets and reconstructs the message using the channels' "recving" buffer. // recvRoutine reads msgPackets and reconstructs the message using the channels' "recving" buffer.
// After a whole message has been assembled, it's pushed to onReceive(). // After a whole message has been assembled, it's pushed to onReceive().
// Blocks depending on how the connection is throttled. // Blocks depending on how the connection is throttled.
func (c *MConnection) recvRoutine() { func (c *MConnection) recvRoutine() {
@ -340,7 +340,7 @@ func (c *MConnection) recvRoutine() {
FOR_LOOP: FOR_LOOP:
for { for {
// Block until .recvMonitor says we can read. // Block until .recvMonitor says we can read.
c.recvMonitor.Limit(maxPacketSize, atomic.LoadInt64(&c.recvRate), true) c.recvMonitor.Limit(maxMsgPacketSize, atomic.LoadInt64(&c.recvRate), true)
// Read packet type // Read packet type
var n int64 var n int64
@ -359,7 +359,7 @@ FOR_LOOP:
if log.IsEnabledFor(logging.DEBUG) { if log.IsEnabledFor(logging.DEBUG) {
numBytes := c.bufReader.Buffered() numBytes := c.bufReader.Buffered()
bytes, err := c.bufReader.Peek(MinInt(numBytes, 100)) bytes, err := c.bufReader.Peek(MinInt(numBytes, 100))
if err != nil { if err == nil {
log.Debug("recvRoutine packet type %X, peeked: %X", pktType, bytes) log.Debug("recvRoutine packet type %X, peeked: %X", pktType, bytes)
} }
} }
@ -371,10 +371,11 @@ FOR_LOOP:
c.pong <- struct{}{} c.pong <- struct{}{}
case packetTypePong: case packetTypePong:
// do nothing // do nothing
case packetTypeMessage: case packetTypeMsg:
pkt, n, err := readPacketSafe(c.bufReader) pkt, n, err := msgPacket{}, new(int64), new(error)
c.recvMonitor.Update(int(n)) ReadBinary(&pkt, c.bufReader, n, err)
if err != nil { c.recvMonitor.Update(int(*n))
if *err != nil {
if atomic.LoadUint32(&c.stopped) != 1 { if atomic.LoadUint32(&c.stopped) != 1 {
log.Info("%v failed @ recvRoutine", c) log.Info("%v failed @ recvRoutine", c)
c.Stop() c.Stop()
@ -385,7 +386,7 @@ FOR_LOOP:
if !ok || channel == nil { if !ok || channel == nil {
Panicf("Unknown channel %X", pkt.ChannelId) Panicf("Unknown channel %X", pkt.ChannelId)
} }
msgBytes := channel.recvPacket(pkt) msgBytes := channel.recvMsgPacket(pkt)
if msgBytes != nil { if msgBytes != nil {
c.onReceive(pkt.ChannelId, msgBytes) c.onReceive(pkt.ChannelId, msgBytes)
} }
@ -394,7 +395,7 @@ FOR_LOOP:
} }
// TODO: shouldn't this go in the sendRoutine? // TODO: shouldn't this go in the sendRoutine?
// Better to send a packet when *we* haven't sent anything for a while. // Better to send a ping packet when *we* haven't sent anything for a while.
c.pingTimer.Reset() c.pingTimer.Reset()
} }
@ -471,8 +472,8 @@ func (ch *Channel) canSend() bool {
return ch.loadSendQueueSize() < defaultSendQueueCapacity return ch.loadSendQueueSize() < defaultSendQueueCapacity
} }
// Returns true if any packets are pending to be sent. // Returns true if any msgPackets are pending to be sent.
// Call before calling nextPacket() // Call before calling nextMsgPacket()
// Goroutine-safe // Goroutine-safe
func (ch *Channel) isSendPending() bool { func (ch *Channel) isSendPending() bool {
if len(ch.sending) == 0 { if len(ch.sending) == 0 {
@ -484,38 +485,37 @@ func (ch *Channel) isSendPending() bool {
return true return true
} }
// Creates a new packet to send. // Creates a new msgPacket to send.
// Not goroutine-safe // Not goroutine-safe
func (ch *Channel) nextPacket() packet { func (ch *Channel) nextMsgPacket() msgPacket {
packet := packet{} packet := msgPacket{}
packet.ChannelId = byte(ch.id) packet.ChannelId = byte(ch.id)
packet.Bytes = ch.sending[:MinInt(maxPacketSize, len(ch.sending))] packet.Bytes = ch.sending[:MinInt(maxMsgPacketSize, len(ch.sending))]
if len(ch.sending) <= maxPacketSize { if len(ch.sending) <= maxMsgPacketSize {
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(maxMsgPacketSize, len(ch.sending)):]
} }
return packet return packet
} }
// Writes next packet to w. // Writes next msgPacket to w.
// Not goroutine-safe // Not goroutine-safe
func (ch *Channel) writePacketTo(w io.Writer) (n int64, err error) { func (ch *Channel) writeMsgPacketTo(w io.Writer) (n int64, err error) {
packet := ch.nextPacket() packet := ch.nextMsgPacket()
WriteByte(w, packetTypeMessage, &n, &err) WriteBinary(packet, w, &n, &err)
WriteBinary(w, packet, &n, &err)
if err != nil { if err != nil {
ch.recentlySent += n ch.recentlySent += n
} }
return return
} }
// Handles incoming packets. Returns a msg bytes if msg is complete. // Handles incoming msgPackets. Returns a msg bytes if msg is complete.
// Not goroutine-safe // Not goroutine-safe
func (ch *Channel) recvPacket(pkt packet) []byte { func (ch *Channel) recvMsgPacket(pkt msgPacket) []byte {
ch.recving = append(ch.recving, pkt.Bytes...) ch.recving = append(ch.recving, pkt.Bytes...)
if pkt.EOF == byte(0x01) { if pkt.EOF == byte(0x01) {
msgBytes := ch.recving msgBytes := ch.recving
@ -536,36 +536,23 @@ func (ch *Channel) updateStats() {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const ( const (
maxPacketSize = 1024 maxMsgPacketSize = 1024
packetTypePing = byte(0x00) packetTypePing = byte(0x00)
packetTypePong = byte(0x01) packetTypePong = byte(0x01)
packetTypeMessage = byte(0x10) packetTypeMsg = byte(0x10)
) )
// Messages in channels are chopped into smaller packets for multiplexing. // Messages in channels are chopped into smaller msgPackets for multiplexing.
type packet struct { type msgPacket struct {
ChannelId byte ChannelId byte
EOF byte // 1 means message ends here. EOF byte // 1 means message ends here.
Bytes []byte Bytes []byte
} }
func (p packet) WriteTo(w io.Writer) (n int64, err error) { func (p msgPacket) TypeByte() byte { return packetTypeMsg }
WriteByte(w, p.ChannelId, &n, &err)
WriteByte(w, p.EOF, &n, &err)
WriteByteSlice(w, p.Bytes, &n, &err)
return
}
func (p packet) String() string { func (p msgPacket) String() string {
return fmt.Sprintf("Packet{%X:%X}", p.ChannelId, p.Bytes) return fmt.Sprintf("MsgPacket{%X:%X}", p.ChannelId, p.Bytes)
}
func readPacketSafe(r io.Reader) (pkt packet, n int64, err error) {
chId := ReadByte(r, &n, &err)
eof := ReadByte(r, &n, &err)
bytes := ReadByteSlice(r, &n, &err)
pkt = packet{chId, eof, bytes}
return
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -574,19 +561,9 @@ func readPacketSafe(r io.Reader) (pkt packet, n int64, err error) {
// 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 interface{}
}
func (tm TypedMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, tm.Type, &n, &err)
WriteBinary(w, tm.Msg, &n, &err)
return
} }
func (tm TypedMessage) String() string { func (tm TypedMessage) String() string {
return fmt.Sprintf("TMsg{%X:%v}", tm.Type, tm.Msg) return fmt.Sprintf("TMsg{%X:%v}", tm.Type, tm.Msg)
} }
func (tm TypedMessage) Bytes() []byte {
return BinaryBytes(tm)
}

View File

@ -8,7 +8,7 @@ var log = logging.MustGetLogger("p2p")
func init() { func init() {
logging.SetFormatter(logging.MustStringFormatter("[%{level:.1s}] %{message}")) logging.SetFormatter(logging.MustStringFormatter("[%{level:.1s}] %{message}"))
logging.SetLevel(logging.WARNING, "p2p") logging.SetLevel(logging.DEBUG, "p2p")
} }
func SetP2PLogger(l *logging.Logger) { func SetP2PLogger(l *logging.Logger) {

View File

@ -5,13 +5,10 @@
package p2p package p2p
import ( import (
"io" "fmt"
"net" "net"
"strconv" "strconv"
"time" "time"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
) )
/* NetAddress */ /* NetAddress */
@ -26,7 +23,7 @@ type NetAddress struct {
func NewNetAddress(addr net.Addr) *NetAddress { func NewNetAddress(addr net.Addr) *NetAddress {
tcpAddr, ok := addr.(*net.TCPAddr) tcpAddr, ok := addr.(*net.TCPAddr)
if !ok { if !ok {
Panicf("Only TCPAddrs are supported. Got: %v", addr) panic(fmt.Sprintf("Only TCPAddrs are supported. Got: %v", addr))
} }
ip := tcpAddr.IP ip := tcpAddr.IP
port := uint16(tcpAddr.Port) port := uint16(tcpAddr.Port)
@ -47,12 +44,6 @@ func NewNetAddressString(addr string) *NetAddress {
return na return na
} }
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)
}
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress { func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
na := &NetAddress{ na := &NetAddress{
IP: ip, IP: ip,
@ -65,12 +56,6 @@ func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
return na return na
} }
func (na *NetAddress) WriteTo(w io.Writer) (n int64, err error) {
WriteByteSlice(w, na.IP.To16(), &n, &err)
WriteUInt16(w, na.Port, &n, &err)
return
}
func (na *NetAddress) Equals(other interface{}) bool { func (na *NetAddress) Equals(other interface{}) bool {
if o, ok := other.(*NetAddress); ok { if o, ok := other.(*NetAddress); ok {
return na.String() == o.String() return na.String() == o.String()

View File

@ -72,14 +72,14 @@ func (p *Peer) IsOutbound() bool {
return p.outbound return p.outbound
} }
func (p *Peer) Send(chId byte, msg Binary) bool { func (p *Peer) Send(chId byte, msg interface{}) bool {
if atomic.LoadUint32(&p.stopped) == 1 { if atomic.LoadUint32(&p.stopped) == 1 {
return false return false
} }
return p.mconn.Send(chId, msg) return p.mconn.Send(chId, msg)
} }
func (p *Peer) TrySend(chId byte, msg Binary) bool { func (p *Peer) TrySend(chId byte, msg interface{}) bool {
if atomic.LoadUint32(&p.stopped) == 1 { if atomic.LoadUint32(&p.stopped) == 1 {
return false return false
} }
@ -94,7 +94,7 @@ 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) {
WriteString(w, p.Key, &n, &err) WriteString(p.Key, w, &n, &err)
return return
} }

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"io"
"sync/atomic" "sync/atomic"
"time" "time"
@ -97,17 +96,17 @@ func (pexR *PEXReactor) Receive(chId byte, src *Peer, msgBytes []byte) {
// src requested some peers. // src requested some peers.
// TODO: prevent abuse. // TODO: prevent abuse.
addrs := pexR.book.GetSelection() addrs := pexR.book.GetSelection()
msg := &pexRddrsMessage{Addrs: addrs} msg := &pexAddrsMessage{Addrs: addrs}
queued := src.TrySend(PexCh, msg) queued := src.TrySend(PexCh, msg)
if !queued { if !queued {
// ignore // ignore
} }
case *pexRddrsMessage: case *pexAddrsMessage:
// We received some peer addresses from src. // We received some peer addresses from src.
// TODO: prevent abuse. // TODO: prevent abuse.
// (We don't want to get spammed with bad peers) // (We don't want to get spammed with bad peers)
srcAddr := src.RemoteAddress() srcAddr := src.RemoteAddress()
for _, addr := range msg.(*pexRddrsMessage).Addrs { for _, addr := range msg.(*pexAddrsMessage).Addrs {
pexR.book.AddAddress(addr, srcAddr) pexR.book.AddAddress(addr, srcAddr)
} }
default: default:
@ -122,7 +121,7 @@ func (pexR *PEXReactor) RequestPEX(peer *Peer) {
} }
func (pexR *PEXReactor) SendAddrs(peer *Peer, addrs []*NetAddress) { func (pexR *PEXReactor) SendAddrs(peer *Peer, addrs []*NetAddress) {
peer.Send(PexCh, &pexRddrsMessage{Addrs: addrs}) peer.Send(PexCh, &pexAddrsMessage{Addrs: addrs})
} }
// Ensures that sufficient peers are connected. (continuous) // Ensures that sufficient peers are connected. (continuous)
@ -193,8 +192,7 @@ func (pexR *PEXReactor) ensurePeers() {
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Messages
/* Messages */
const ( const (
msgTypeUnknown = byte(0x00) msgTypeUnknown = byte(0x00)
@ -211,7 +209,7 @@ func decodeMessage(bz []byte) (msg interface{}) {
case msgTypeRequest: case msgTypeRequest:
return &pexRequestMessage{} return &pexRequestMessage{}
case msgTypeAddrs: case msgTypeAddrs:
return readPexAddrsMessage(bytes.NewReader(bz[1:]), &n, &err) return ReadBinary(&pexAddrsMessage{}, bytes.NewReader(bz[1:]), &n, &err)
default: default:
return nil return nil
} }
@ -223,10 +221,7 @@ A pexRequestMessage requests additional peer addresses.
type pexRequestMessage struct { type pexRequestMessage struct {
} }
func (m *pexRequestMessage) WriteTo(w io.Writer) (n int64, err error) { func (m *pexRequestMessage) TypeByte() byte { return msgTypeRequest }
WriteByte(w, msgTypeRequest, &n, &err)
return
}
func (m *pexRequestMessage) String() string { func (m *pexRequestMessage) String() string {
return "[pexRequest]" return "[pexRequest]"
@ -235,31 +230,12 @@ func (m *pexRequestMessage) String() string {
/* /*
A message with announced peer addresses. A message with announced peer addresses.
*/ */
type pexRddrsMessage struct { type pexAddrsMessage struct {
Addrs []*NetAddress Addrs []*NetAddress
} }
func readPexAddrsMessage(r io.Reader, n *int64, err *error) *pexRddrsMessage { func (m *pexAddrsMessage) TypeByte() byte { return msgTypeAddrs }
numAddrs := int(ReadUInt32(r, n, err))
addrs := []*NetAddress{}
for i := 0; i < numAddrs; i++ {
addr := ReadNetAddress(r, n, err)
addrs = append(addrs, addr)
}
return &pexRddrsMessage{
Addrs: addrs,
}
}
func (m *pexRddrsMessage) WriteTo(w io.Writer) (n int64, err error) { func (m *pexAddrsMessage) String() string {
WriteByte(w, msgTypeAddrs, &n, &err) return fmt.Sprintf("[pexAddrs %v]", m.Addrs)
WriteUInt32(w, uint32(len(m.Addrs)), &n, &err)
for _, addr := range m.Addrs {
WriteBinary(w, addr, &n, &err)
}
return
}
func (m *pexRddrsMessage) String() string {
return fmt.Sprintf("[pexRddrs %v]", m.Addrs)
} }

View File

@ -2,11 +2,11 @@ package p2p
import ( import (
"errors" "errors"
"fmt"
"net" "net"
"sync/atomic" "sync/atomic"
"time" "time"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common" . "github.com/tendermint/tendermint/common"
) )
@ -67,7 +67,7 @@ func NewSwitch(reactors []Reactor) *Switch {
for _, chDesc := range reactorChannels { for _, chDesc := range reactorChannels {
chId := chDesc.Id chId := chDesc.Id
if reactorsByCh[chId] != nil { if reactorsByCh[chId] != nil {
Panicf("Channel %X has multiple reactors %v & %v", chId, reactorsByCh[chId], reactor) panic(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chId, reactorsByCh[chId], reactor))
} }
chDescs = append(chDescs, chDesc) chDescs = append(chDescs, chDesc)
reactorsByCh[chId] = reactor reactorsByCh[chId] = reactor
@ -165,7 +165,7 @@ func (sw *Switch) IsDialing(addr *NetAddress) bool {
} }
// XXX: This is wrong, we can't just ignore failures on TrySend. // XXX: This is wrong, we can't just ignore failures on TrySend.
func (sw *Switch) Broadcast(chId byte, msg Binary) (numSuccess, numFailure int) { func (sw *Switch) Broadcast(chId byte, msg interface{}) (numSuccess, numFailure int) {
if atomic.LoadUint32(&sw.stopped) == 1 { if atomic.LoadUint32(&sw.stopped) == 1 {
return return
} }

View File

@ -2,7 +2,6 @@ package p2p
import ( import (
"bytes" "bytes"
"io"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -10,15 +9,6 @@ import (
. "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
}
//-----------------------------------------------------------------------------
type PeerMessage struct { type PeerMessage struct {
PeerKey string PeerKey string
Bytes []byte Bytes []byte
@ -137,16 +127,16 @@ func TestSwitches(t *testing.T) {
t.Errorf("Expected exactly 1 peer in s2, got %v", s2.Peers().Size()) t.Errorf("Expected exactly 1 peer in s2, got %v", s2.Peers().Size())
} }
ch0Msg := String("channel zero") ch0Msg := "channel zero"
ch1Msg := String("channel one") ch1Msg := "channel one"
ch2Msg := String("channel two") ch2Msg := "channel two"
s1.Broadcast(byte(0x00), ch0Msg) s1.Broadcast(byte(0x00), ch0Msg)
s1.Broadcast(byte(0x01), ch1Msg) s1.Broadcast(byte(0x01), ch1Msg)
s1.Broadcast(byte(0x02), ch2Msg) s1.Broadcast(byte(0x02), ch2Msg)
// Wait for things to settle... // Wait for things to settle...
time.Sleep(100 * time.Millisecond) time.Sleep(5000 * time.Millisecond)
// Check message on ch0 // Check message on ch0
ch0Msgs := s2.Reactors()[0].(*TestReactor).msgsReceived[byte(0x00)] ch0Msgs := s2.Reactors()[0].(*TestReactor).msgsReceived[byte(0x00)]
@ -206,7 +196,7 @@ func BenchmarkSwitches(b *testing.B) {
// Send random message from one channel to another // Send random message from one channel to another
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
chId := byte(i % 4) chId := byte(i % 4)
nS, nF := s1.Broadcast(chId, String("test data")) nS, nF := s1.Broadcast(chId, "test data")
numSuccess += nS numSuccess += nS
numFailure += nF numFailure += nF
} }

View File

@ -1,7 +1,6 @@
package rpc package rpc
import ( import (
"fmt"
"net/http" "net/http"
"regexp" "regexp"
"strconv" "strconv"

View File

@ -1,177 +1,47 @@
package state package state
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"github.com/tendermint/go-ed25519" . "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common"
)
const (
AccountStatusNominal = byte(0x00)
AccountStatusBonded = byte(0x01)
AccountStatusUnbonding = byte(0x02)
AccountStatusDupedOut = byte(0x03)
) )
type Account struct { type Account struct {
Id uint64 // Numeric id of account, incrementing. Address []byte
PubKey []byte PubKey PubKey
}
func ReadAccount(r io.Reader, n *int64, err *error) Account {
return Account{
Id: ReadUInt64(r, n, err),
PubKey: ReadByteSlice(r, n, err),
}
}
func (account Account) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt64(w, account.Id, &n, &err)
WriteByteSlice(w, account.PubKey, &n, &err)
return
}
func (account Account) VerifyBytes(msg []byte, sig Signature) bool {
if sig.SignerId != account.Id {
panic("account.id doesn't match sig.signerid")
}
if len(sig.Bytes) == 0 {
panic("signature is empty")
}
v1 := &ed25519.Verify{
Message: msg,
PubKey: account.PubKey,
Signature: sig.Bytes,
}
ok := ed25519.VerifyBatch([]*ed25519.Verify{v1})
return ok
}
func (account Account) Verify(o Signable) bool {
sig := o.GetSignature()
o.SetSignature(Signature{}) // clear
msg := BinaryBytes(o)
o.SetSignature(sig) // restore
return account.VerifyBytes(msg, sig)
}
func (account Account) String() string {
return fmt.Sprintf("Account{%v:%X}", account.Id, account.PubKey[:6])
}
//-----------------------------------------------------------------------------
type AccountDetail struct {
Account
Sequence uint Sequence uint
Balance uint64 Balance uint64
Status byte
} }
func ReadAccountDetail(r io.Reader, n *int64, err *error) *AccountDetail { func NewAccount(address []byte, pubKey PubKey) *Account {
return &AccountDetail{ return &Account{
Account: ReadAccount(r, n, err), Address: address,
Sequence: ReadUVarInt(r, n, err), PubKey: pubKey,
Balance: ReadUInt64(r, n, err), Sequence: uint(0),
Status: ReadByte(r, n, err), Balance: uint64(0),
} }
} }
func (accDet *AccountDetail) WriteTo(w io.Writer) (n int64, err error) { func (account *Account) Copy() *Account {
WriteBinary(w, accDet.Account, &n, &err) accountCopy := *account
WriteUVarInt(w, accDet.Sequence, &n, &err) return &accountCopy
WriteUInt64(w, accDet.Balance, &n, &err)
WriteByte(w, accDet.Status, &n, &err)
return
} }
func (accDet *AccountDetail) String() string { func (account *Account) String() string {
return fmt.Sprintf("AccountDetail{%v:%X Sequence:%v Balance:%v Status:%X}", return fmt.Sprintf("Account{%X:%v}", account.Address, account.PubKey)
accDet.Id, accDet.PubKey, accDet.Sequence, accDet.Balance, accDet.Status)
} }
func (accDet *AccountDetail) Copy() *AccountDetail { func AccountEncoder(o interface{}, w io.Writer, n *int64, err *error) {
accDetCopy := *accDet WriteBinary(o.(*Account), w, n, err)
return &accDetCopy
} }
//------------------------------------- func AccountDecoder(r io.Reader, n *int64, err *error) interface{} {
return ReadBinary(&Account{}, r, n, err)
var AccountDetailCodec = accountDetailCodec{}
type accountDetailCodec struct{}
func (abc accountDetailCodec) Encode(accDet interface{}, w io.Writer, n *int64, err *error) {
WriteBinary(w, accDet.(*AccountDetail), n, err)
} }
func (abc accountDetailCodec) Decode(r io.Reader, n *int64, err *error) interface{} { var AccountCodec = Codec{
return ReadAccountDetail(r, n, err) Encode: AccountEncoder,
} Decode: AccountDecoder,
func (abc accountDetailCodec) Compare(o1 interface{}, o2 interface{}) int {
panic("AccountDetailCodec.Compare not implemented")
}
//-----------------------------------------------------------------------------
type PrivAccount struct {
Account
PrivKey []byte
}
// Generates a new account with private key.
// The Account.Id is empty since it isn't in the blockchain.
func GenPrivAccount() *PrivAccount {
privKey := CRandBytes(32)
pubKey := ed25519.MakePubKey(privKey)
return &PrivAccount{
Account: Account{
Id: uint64(0),
PubKey: pubKey,
},
PrivKey: privKey,
}
}
// The Account.Id is empty since it isn't in the blockchain.
func PrivAccountFromJSON(jsonBlob []byte) (privAccount *PrivAccount) {
err := json.Unmarshal(jsonBlob, &privAccount)
if err != nil {
Panicf("Couldn't read PrivAccount: %v", err)
}
return
}
// The Account.Id is empty since it isn't in the blockchain.
func PrivAccountFromFile(file string) *PrivAccount {
jsonBlob, err := ioutil.ReadFile(file)
if err != nil {
Panicf("Couldn't read PrivAccount from file: %v", err)
}
return PrivAccountFromJSON(jsonBlob)
}
func (pa *PrivAccount) SignBytes(msg []byte) Signature {
signature := ed25519.SignMessage(msg, pa.PrivKey, pa.PubKey)
sig := Signature{
SignerId: pa.Id,
Bytes: signature,
}
return sig
}
func (pa *PrivAccount) Sign(o Signable) {
if !o.GetSignature().IsZero() {
panic("Cannot sign: already signed")
}
msg := BinaryBytes(o)
sig := pa.SignBytes(msg)
o.SetSignature(sig)
} }

View File

@ -13,8 +13,9 @@ import (
) )
type GenesisDoc struct { type GenesisDoc struct {
GenesisTime time.Time GenesisTime time.Time
AccountDetails []*AccountDetail Accounts []*Account
Validators []*Validator
} }
func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) {
@ -31,47 +32,32 @@ func GenesisStateFromFile(db db_.DB, genDocFile string) *State {
Panicf("Couldn't read GenesisDoc file: %v", err) Panicf("Couldn't read GenesisDoc file: %v", err)
} }
genDoc := GenesisDocFromJSON(jsonBlob) genDoc := GenesisDocFromJSON(jsonBlob)
return GenesisStateFromDoc(db, genDoc) return GenesisState(db, genDoc)
} }
func GenesisStateFromDoc(db db_.DB, genDoc *GenesisDoc) *State { func GenesisState(db db_.DB, genDoc *GenesisDoc) *State {
return GenesisState(db, genDoc.GenesisTime, genDoc.AccountDetails) if len(genDoc.Validators) == 0 {
}
func GenesisState(db db_.DB, genesisTime time.Time, accDets []*AccountDetail) *State {
if genesisTime.IsZero() {
genesisTime = time.Now()
}
// TODO: Use "uint64Codec" instead of BasicCodec
accountDetails := merkle.NewIAVLTree(BasicCodec, AccountDetailCodec, defaultAccountDetailsCacheCapacity, db)
validators := []*Validator{}
for _, accDet := range accDets {
accountDetails.Set(accDet.Id, accDet)
if accDet.Status == AccountStatusBonded {
validators = append(validators, &Validator{
Account: accDet.Account,
BondHeight: 0,
VotingPower: accDet.Balance,
Accum: 0,
})
}
}
if len(validators) == 0 {
panic("Must have some validators") panic("Must have some validators")
} }
if genDoc.GenesisTime.IsZero() {
genDoc.GenesisTime = time.Now()
}
// Make accounts state tree
accounts := merkle.NewIAVLTree(BasicCodec, AccountCodec, defaultAccountsCacheCapacity, db)
for _, acc := range genDoc.Accounts {
accounts.Set(acc.Address, acc)
}
return &State{ return &State{
DB: db, DB: db,
LastBlockHeight: 0, LastBlockHeight: 0,
LastBlockHash: nil, LastBlockHash: nil,
LastBlockParts: PartSetHeader{}, LastBlockParts: PartSetHeader{},
LastBlockTime: genesisTime, LastBlockTime: genDoc.GenesisTime,
BondedValidators: NewValidatorSet(validators), BondedValidators: NewValidatorSet(genDoc.Validators),
UnbondingValidators: NewValidatorSet(nil), UnbondingValidators: NewValidatorSet(nil),
accountDetails: accountDetails, accounts: accounts,
} }
} }

View File

@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"time" "time"
. "github.com/tendermint/tendermint/account"
. "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"
@ -14,17 +15,11 @@ import (
) )
var ( var (
ErrStateInvalidAccountId = errors.New("Error State invalid account id") stateKey = []byte("stateKey")
ErrStateInvalidSignature = errors.New("Error State invalid signature") minBondAmount = uint64(1) // TODO adjust
ErrStateInvalidSequenceNumber = errors.New("Error State invalid sequence number") defaultAccountsCacheCapacity = 1000 // TODO adjust
ErrStateInvalidAccountState = errors.New("Error State invalid account state") unbondingPeriodBlocks = uint(60 * 24 * 365) // TODO probably better to make it time based.
ErrStateInsufficientFunds = errors.New("Error State insufficient funds") validatorTimeoutBlocks = uint(10) // TODO adjust
stateKey = []byte("stateKey")
minBondAmount = uint64(1) // TODO adjust
defaultAccountDetailsCacheCapacity = 1000 // TODO adjust
unbondingPeriodBlocks = uint32(60 * 24 * 365) // TODO probably better to make it time based.
validatorTimeoutBlocks = uint32(10) // TODO adjust
) )
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -43,13 +38,14 @@ func (txErr InvalidTxError) Error() string {
// NOTE: not goroutine-safe. // NOTE: not goroutine-safe.
type State struct { type State struct {
DB db_.DB DB db_.DB
LastBlockHeight uint32 LastBlockHeight uint
LastBlockHash []byte LastBlockHash []byte
LastBlockParts PartSetHeader LastBlockParts PartSetHeader
LastBlockTime time.Time LastBlockTime time.Time
BondedValidators *ValidatorSet BondedValidators *ValidatorSet
UnbondingValidators *ValidatorSet UnbondingValidators *ValidatorSet
accountDetails merkle.Tree // Shouldn't be accessed directly. accounts merkle.Tree // Shouldn't be accessed directly.
validatorInfos merkle.Tree // Shouldn't be accessed directly.
} }
func LoadState(db db_.DB) *State { func LoadState(db db_.DB) *State {
@ -58,20 +54,21 @@ func LoadState(db db_.DB) *State {
if len(buf) == 0 { if len(buf) == 0 {
return nil return nil
} else { } else {
reader := bytes.NewReader(buf) r, n, err := bytes.NewReader(buf), new(int64), new(error)
var n int64 s.LastBlockHeight = ReadUVarInt(r, n, err)
var err error s.LastBlockHash = ReadByteSlice(r, n, err)
s.LastBlockHeight = ReadUInt32(reader, &n, &err) s.LastBlockParts = ReadBinary(PartSetHeader{}, r, n, err).(PartSetHeader)
s.LastBlockHash = ReadByteSlice(reader, &n, &err) s.LastBlockTime = ReadTime(r, n, err)
s.LastBlockParts = ReadPartSetHeader(reader, &n, &err) s.BondedValidators = ReadBinary(&ValidatorSet{}, r, n, err).(*ValidatorSet)
s.LastBlockTime = ReadTime(reader, &n, &err) s.UnbondingValidators = ReadBinary(&ValidatorSet{}, r, n, err).(*ValidatorSet)
s.BondedValidators = ReadValidatorSet(reader, &n, &err) accountsHash := ReadByteSlice(r, n, err)
s.UnbondingValidators = ReadValidatorSet(reader, &n, &err) s.accounts = merkle.NewIAVLTree(BasicCodec, AccountCodec, defaultAccountsCacheCapacity, db)
accountDetailsHash := ReadByteSlice(reader, &n, &err) s.accounts.Load(accountsHash)
s.accountDetails = merkle.NewIAVLTree(BasicCodec, AccountDetailCodec, defaultAccountDetailsCacheCapacity, db) validatorInfosHash := ReadByteSlice(r, n, err)
s.accountDetails.Load(accountDetailsHash) s.validatorInfos = merkle.NewIAVLTree(BasicCodec, ValidatorInfoCodec, 0, db)
if err != nil { s.validatorInfos.Load(validatorInfosHash)
panic(err) if *err != nil {
panic(*err)
} }
// TODO: ensure that buf is completely read. // TODO: ensure that buf is completely read.
} }
@ -80,19 +77,18 @@ func LoadState(db db_.DB) *State {
// Save this state into the db. // Save this state into the db.
func (s *State) Save() { func (s *State) Save() {
s.accountDetails.Save() s.accounts.Save()
var buf bytes.Buffer buf, n, err := new(bytes.Buffer), new(int64), new(error)
var n int64 WriteUVarInt(s.LastBlockHeight, buf, n, err)
var err error WriteByteSlice(s.LastBlockHash, buf, n, err)
WriteUInt32(&buf, s.LastBlockHeight, &n, &err) WriteBinary(s.LastBlockParts, buf, n, err)
WriteByteSlice(&buf, s.LastBlockHash, &n, &err) WriteTime(s.LastBlockTime, buf, n, err)
WriteBinary(&buf, s.LastBlockParts, &n, &err) WriteBinary(s.BondedValidators, buf, n, err)
WriteTime(&buf, s.LastBlockTime, &n, &err) WriteBinary(s.UnbondingValidators, buf, n, err)
WriteBinary(&buf, s.BondedValidators, &n, &err) WriteByteSlice(s.accounts.Hash(), buf, n, err)
WriteBinary(&buf, s.UnbondingValidators, &n, &err) WriteByteSlice(s.validatorInfos.Hash(), buf, n, err)
WriteByteSlice(&buf, s.accountDetails.Hash(), &n, &err) if *err != nil {
if err != nil { panic(*err)
panic(err)
} }
s.DB.Set(stateKey, buf.Bytes()) s.DB.Set(stateKey, buf.Bytes())
} }
@ -106,185 +102,312 @@ func (s *State) Copy() *State {
LastBlockTime: s.LastBlockTime, LastBlockTime: s.LastBlockTime,
BondedValidators: s.BondedValidators.Copy(), BondedValidators: s.BondedValidators.Copy(),
UnbondingValidators: s.UnbondingValidators.Copy(), UnbondingValidators: s.UnbondingValidators.Copy(),
accountDetails: s.accountDetails.Copy(), accounts: s.accounts.Copy(),
validatorInfos: s.validatorInfos.Copy(),
}
}
func (s *State) GetOrMakeAccounts(ins []*TxInput, outs []*TxOutput) (map[string]*Account, error) {
accounts := map[string]*Account{}
for _, in := range ins {
// Account shouldn't be duplicated
if _, ok := accounts[string(in.Address)]; ok {
return nil, ErrTxDuplicateAddress
}
account := s.GetAccount(in.Address)
if account == nil {
return nil, ErrTxInvalidAddress
}
accounts[string(in.Address)] = account
}
for _, out := range outs {
// Account shouldn't be duplicated
if _, ok := accounts[string(out.Address)]; ok {
return nil, ErrTxDuplicateAddress
}
account := s.GetAccount(out.Address)
// output account may be nil (new)
if account == nil {
account = NewAccount(out.Address, PubKeyUnknown{})
}
accounts[string(out.Address)] = account
}
return accounts, nil
}
func (s *State) ValidateInputs(accounts map[string]*Account, signBytes []byte, ins []*TxInput) (total uint64, err error) {
for _, in := range ins {
account := accounts[string(in.Address)]
if account == nil {
panic("ValidateInputs() expects account in accounts")
}
// Check TxInput basic
if err := in.ValidateBasic(); err != nil {
return 0, err
}
// Check amount
if account.Balance < in.Amount {
return 0, ErrTxInsufficientFunds
}
// Check signatures
if !account.PubKey.VerifyBytes(signBytes, in.Signature) {
return 0, ErrTxInvalidSignature
}
// Check sequences
if account.Sequence+1 != in.Sequence {
return 0, ErrTxInvalidSequence
}
// Good. Add amount to total
total += in.Amount
}
return total, nil
}
func (s *State) ValidateOutputs(outs []*TxOutput) (total uint64, err error) {
for _, out := range outs {
// Check TxOutput basic
if err := out.ValidateBasic(); err != nil {
return 0, err
}
// Good. Add amount to total
total += out.Amount
}
return total, nil
}
func (s *State) AdjustByInputs(accounts map[string]*Account, ins []*TxInput) {
for _, in := range ins {
account := accounts[string(in.Address)]
if account == nil {
panic("AdjustByInputs() expects account in accounts")
}
if account.Balance < in.Amount {
panic("AdjustByInputs() expects sufficient funds")
}
account.Balance -= in.Amount
}
}
func (s *State) AdjustByOutputs(accounts map[string]*Account, outs []*TxOutput) {
for _, out := range outs {
account := accounts[string(out.Address)]
if account == nil {
panic("AdjustByInputs() expects account in accounts")
}
account.Balance += out.Amount
} }
} }
// If the tx is invalid, an error will be returned. // If the tx is invalid, an error will be returned.
// Unlike AppendBlock(), state will not be altered. // Unlike AppendBlock(), state will not be altered.
func (s *State) ExecTx(tx Tx) error { func (s *State) ExecTx(tx_ Tx) error {
accDet := s.GetAccountDetail(tx.GetSignature().SignerId)
if accDet == nil { // TODO: do something with fees
return ErrStateInvalidAccountId fees := uint64(0)
}
// Check signature
if !accDet.Verify(tx) {
return ErrStateInvalidSignature
}
// Check and update sequence
if tx.GetSequence() <= accDet.Sequence {
return ErrStateInvalidSequenceNumber
} else {
// TODO consider prevSequence for tx chaining.
accDet.Sequence = tx.GetSequence()
}
// Subtract fee from balance.
if accDet.Balance < tx.GetFee() {
return ErrStateInsufficientFunds
} else {
accDet.Balance -= tx.GetFee()
}
// Exec tx // Exec tx
switch tx.(type) { switch tx_.(type) {
case *SendTx: case *SendTx:
stx := tx.(*SendTx) tx := tx_.(*SendTx)
toAccDet := s.GetAccountDetail(stx.To) accounts, err := s.GetOrMakeAccounts(tx.Inputs, tx.Outputs)
// Accounts must be nominal if err != nil {
if accDet.Status != AccountStatusNominal { return err
return ErrStateInvalidAccountState
} }
if toAccDet.Status != AccountStatusNominal { signBytes := SignBytes(tx)
return ErrStateInvalidAccountState inTotal, err := s.ValidateInputs(accounts, signBytes, tx.Inputs)
if err != nil {
return err
} }
// Check account balance outTotal, err := s.ValidateOutputs(tx.Outputs)
if accDet.Balance < stx.Amount { if err != nil {
return ErrStateInsufficientFunds return err
} }
// Check existence of destination account if outTotal > inTotal {
if toAccDet == nil { return ErrTxInsufficientFunds
return ErrStateInvalidAccountId
} }
// Good! fee := inTotal - outTotal
accDet.Balance -= stx.Amount fees += fee
toAccDet.Balance += stx.Amount
s.SetAccountDetail(accDet) // Good! Adjust accounts
s.SetAccountDetail(toAccDet) s.AdjustByInputs(accounts, tx.Inputs)
s.AdjustByOutputs(accounts, tx.Outputs)
s.SetAccounts(accounts)
return nil return nil
//case *NameTx
case *BondTx: case *BondTx:
//btx := tx.(*BondTx) tx := tx_.(*BondTx)
// Account must be nominal accounts, err := s.GetOrMakeAccounts(tx.Inputs, tx.UnbondTo)
if accDet.Status != AccountStatusNominal { if err != nil {
return ErrStateInvalidAccountState return err
} }
// Check account balance signBytes := SignBytes(tx)
if accDet.Balance < minBondAmount { inTotal, err := s.ValidateInputs(accounts, signBytes, tx.Inputs)
return ErrStateInsufficientFunds if err != nil {
return err
} }
// Good! if err := tx.PubKey.ValidateBasic(); err != nil {
accDet.Status = AccountStatusBonded return err
s.SetAccountDetail(accDet) }
outTotal, err := s.ValidateOutputs(tx.UnbondTo)
if err != nil {
return err
}
if outTotal > inTotal {
return ErrTxInsufficientFunds
}
fee := inTotal - outTotal
fees += fee
// Good! Adjust accounts
s.AdjustByInputs(accounts, tx.Inputs)
s.SetAccounts(accounts)
// Add ValidatorInfo
updated := s.SetValidatorInfo(&ValidatorInfo{
Address: tx.PubKey.Address(),
PubKey: tx.PubKey,
UnbondTo: tx.UnbondTo,
FirstBondHeight: s.LastBlockHeight + 1,
})
if !updated {
panic("Failed to add validator info")
}
// Add Validator
added := s.BondedValidators.Add(&Validator{ added := s.BondedValidators.Add(&Validator{
Account: accDet.Account, Address: tx.PubKey.Address(),
BondHeight: s.LastBlockHeight, PubKey: tx.PubKey,
VotingPower: accDet.Balance, BondHeight: s.LastBlockHeight + 1,
VotingPower: inTotal,
Accum: 0, Accum: 0,
}) })
if !added { if !added {
panic("Failed to add validator") panic("Failed to add validator")
} }
return nil return nil
case *UnbondTx: case *UnbondTx:
//utx := tx.(*UnbondTx) tx := tx_.(*UnbondTx)
// Account must be bonded.
if accDet.Status != AccountStatusBonded { // The validator must be active
return ErrStateInvalidAccountState _, val := s.BondedValidators.GetByAddress(tx.Address)
if val == nil {
return ErrTxInvalidAddress
} }
// Verify the signature
signBytes := SignBytes(tx)
if !val.PubKey.VerifyBytes(signBytes, tx.Signature) {
return ErrTxInvalidSignature
}
// tx.Height must be greater than val.LastCommitHeight
if tx.Height < val.LastCommitHeight {
return errors.New("Invalid bond height")
}
// Good! // Good!
s.unbondValidator(accDet.Id, accDet) s.unbondValidator(val)
s.SetAccountDetail(accDet)
return nil return nil
case *DupeoutTx: case *DupeoutTx:
{ tx := tx_.(*DupeoutTx)
// NOTE: accDet is the one who created this transaction.
// Subtract any fees, save, and forget.
s.SetAccountDetail(accDet)
accDet = nil
}
dtx := tx.(*DupeoutTx)
// Verify the signatures // Verify the signatures
if dtx.VoteA.SignerId != dtx.VoteB.SignerId { _, accused := s.BondedValidators.GetByAddress(tx.Address)
return ErrStateInvalidSignature voteASignBytes := SignBytes(&tx.VoteA)
} voteBSignBytes := SignBytes(&tx.VoteB)
accused := s.GetAccountDetail(dtx.VoteA.SignerId) if !accused.PubKey.VerifyBytes(voteASignBytes, tx.VoteA.Signature) ||
if !accused.Verify(&dtx.VoteA) || !accused.Verify(&dtx.VoteB) { !accused.PubKey.VerifyBytes(voteBSignBytes, tx.VoteB.Signature) {
return ErrStateInvalidSignature return ErrTxInvalidSignature
} }
// Verify equivocation // Verify equivocation
if dtx.VoteA.Height != dtx.VoteB.Height { // TODO: in the future, just require one vote from a previous height that
return errors.New("DupeoutTx height must be the same.") // doesn't exist on this chain.
if tx.VoteA.Height != tx.VoteB.Height {
return errors.New("DupeoutTx heights don't match")
} }
if dtx.VoteA.Type == VoteTypeCommit && dtx.VoteA.Round < dtx.VoteB.Round { if tx.VoteA.Type == VoteTypeCommit && tx.VoteA.Round < tx.VoteB.Round {
// Check special case. // Check special case.
// Validators should not sign another vote after committing. // Validators should not sign another vote after committing.
} else { } else {
if dtx.VoteA.Round != dtx.VoteB.Round { if tx.VoteA.Round != tx.VoteB.Round {
return errors.New("DupeoutTx rounds don't match") return errors.New("DupeoutTx rounds don't match")
} }
if dtx.VoteA.Type != dtx.VoteB.Type { if tx.VoteA.Type != tx.VoteB.Type {
return errors.New("DupeoutTx types don't match") return errors.New("DupeoutTx types don't match")
} }
if bytes.Equal(dtx.VoteA.BlockHash, dtx.VoteB.BlockHash) { if bytes.Equal(tx.VoteA.BlockHash, tx.VoteB.BlockHash) {
return errors.New("DupeoutTx blockhash shouldn't match") return errors.New("DupeoutTx blockhashes shouldn't match")
} }
} }
// Good! (Bad validator!) // Good! (Bad validator!)
if accused.Status == AccountStatusBonded { s.destroyValidator(accused)
_, removed := s.BondedValidators.Remove(accused.Id)
if !removed {
panic("Failed to remove accused validator")
}
} else if accused.Status == AccountStatusUnbonding {
_, removed := s.UnbondingValidators.Remove(accused.Id)
if !removed {
panic("Failed to remove accused validator")
}
} else {
panic("Couldn't find accused validator")
}
accused.Status = AccountStatusDupedOut
updated := s.SetAccountDetail(accused)
if !updated {
panic("Failed to update accused validator account")
}
return nil return nil
default: default:
panic("Unknown Tx type") panic("Unknown Tx type")
} }
} }
// accDet optional func (s *State) unbondValidator(val *Validator) {
func (s *State) unbondValidator(accountId uint64, accDet *AccountDetail) { // Move validator to UnbondingValidators
if accDet == nil { val, removed := s.BondedValidators.Remove(val.Address)
accDet = s.GetAccountDetail(accountId)
}
accDet.Status = AccountStatusUnbonding
s.SetAccountDetail(accDet)
val, removed := s.BondedValidators.Remove(accDet.Id)
if !removed { if !removed {
panic("Failed to remove validator") panic("Couldn't remove validator for unbonding")
} }
val.UnbondHeight = s.LastBlockHeight val.UnbondHeight = s.LastBlockHeight
added := s.UnbondingValidators.Add(val) added := s.UnbondingValidators.Add(val)
if !added { if !added {
panic("Failed to add validator") panic("Couldn't add validator for unbonding")
} }
} }
func (s *State) releaseValidator(accountId uint64) { func (s *State) releaseValidator(val *Validator) {
accDet := s.GetAccountDetail(accountId) // Update validatorInfo
if accDet.Status != AccountStatusUnbonding { valInfo := s.GetValidatorInfo(val.Address)
panic("Cannot release validator") if valInfo == nil {
panic("Couldn't find validatorInfo for release")
} }
accDet.Status = AccountStatusNominal valInfo.ReleasedHeight = s.LastBlockHeight + 1
// TODO: move balance to designated address, UnbondTo. s.SetValidatorInfo(valInfo)
s.SetAccountDetail(accDet)
_, removed := s.UnbondingValidators.Remove(accountId) // Send coins back to UnbondTo outputs
accounts, err := s.GetOrMakeAccounts(nil, valInfo.UnbondTo)
if err != nil {
panic("Couldn't get or make unbondTo accounts")
}
s.AdjustByOutputs(accounts, valInfo.UnbondTo)
s.SetAccounts(accounts)
// Remove validator from UnbondingValidators
_, removed := s.UnbondingValidators.Remove(val.Address)
if !removed { if !removed {
panic("Couldn't release validator") panic("Couldn't remove validator for release")
} }
} }
func (s *State) destroyValidator(val *Validator) {
// Update validatorInfo
valInfo := s.GetValidatorInfo(val.Address)
if valInfo == nil {
panic("Couldn't find validatorInfo for release")
}
valInfo.DestroyedHeight = s.LastBlockHeight + 1
valInfo.DestroyedAmount = val.VotingPower
s.SetValidatorInfo(valInfo)
// Remove validator
_, removed := s.BondedValidators.Remove(val.Address)
if !removed {
_, removed := s.UnbondingValidators.Remove(val.Address)
if !removed {
panic("Couldn't remove validator for destruction")
}
}
}
// "checkStateHash": If false, instead of checking the resulting // "checkStateHash": If false, instead of checking the resulting
// state.Hash() against block.StateHash, it *sets* the block.StateHash. // state.Hash() against block.StateHash, it *sets* the block.StateHash.
// (used for constructing a new proposal) // (used for constructing a new proposal)
@ -308,23 +431,18 @@ func (s *State) AppendBlock(block *Block, blockPartsHeader PartSetHeader, checkS
} }
var sumVotingPower uint64 var sumVotingPower uint64
s.BondedValidators.Iterate(func(index uint, val *Validator) bool { s.BondedValidators.Iterate(func(index uint, val *Validator) bool {
rsig := block.Validation.Commits[index] commit := block.Validation.Commits[index]
if rsig.IsZero() { if commit.IsZero() {
return false return false
} else { } else {
if rsig.SignerId != val.Id {
err = errors.New("Invalid validation order")
return true
}
vote := &Vote{ vote := &Vote{
Height: block.Height - 1, Height: block.Height - 1,
Round: rsig.Round, Round: commit.Round,
Type: VoteTypeCommit, Type: VoteTypeCommit,
BlockHash: block.LastBlockHash, BlockHash: block.LastBlockHash,
BlockParts: block.LastBlockParts, BlockParts: block.LastBlockParts,
Signature: rsig.Signature,
} }
if val.Verify(vote) { if val.PubKey.VerifyBytes(SignBytes(vote), commit.Signature) {
sumVotingPower += val.VotingPower sumVotingPower += val.VotingPower
return false return false
} else { } else {
@ -351,10 +469,13 @@ func (s *State) AppendBlock(block *Block, blockPartsHeader PartSetHeader, checkS
} }
// Update Validator.LastCommitHeight as necessary. // Update Validator.LastCommitHeight as necessary.
for _, rsig := range block.Validation.Commits { for i, commit := range block.Validation.Commits {
_, val := s.BondedValidators.GetById(rsig.SignerId) if commit.IsZero() {
continue
}
_, val := s.BondedValidators.GetByIndex(uint(i))
if val == nil { if val == nil {
return ErrStateInvalidSignature return ErrTxInvalidSignature
} }
val.LastCommitHeight = block.Height - 1 val.LastCommitHeight = block.Height - 1
updated := s.BondedValidators.Update(val) updated := s.BondedValidators.Update(val)
@ -373,7 +494,7 @@ func (s *State) AppendBlock(block *Block, blockPartsHeader PartSetHeader, checkS
return false return false
}) })
for _, val := range toRelease { for _, val := range toRelease {
s.releaseValidator(val.Id) s.releaseValidator(val)
} }
// If any validators haven't signed in a while, // If any validators haven't signed in a while,
@ -386,7 +507,7 @@ func (s *State) AppendBlock(block *Block, blockPartsHeader PartSetHeader, checkS
return false return false
}) })
for _, val := range toTimeout { for _, val := range toTimeout {
s.unbondValidator(val.Id, nil) s.unbondValidator(val)
} }
// Increment validator AccumPowers // Increment validator AccumPowers
@ -415,21 +536,39 @@ func (s *State) AppendBlock(block *Block, blockPartsHeader PartSetHeader, checkS
return nil return nil
} }
// The returned AccountDetail is a copy, so mutating it // The returned Account is a copy, so mutating it
// has no side effects. // has no side effects.
func (s *State) GetAccountDetail(accountId uint64) *AccountDetail { func (s *State) GetAccount(address []byte) *Account {
_, accDet := s.accountDetails.Get(accountId) _, account := s.accounts.Get(address)
if accDet == nil { if account == nil {
return nil return nil
} }
return accDet.(*AccountDetail).Copy() return account.(*Account).Copy()
}
// The accounts are copied before setting, so mutating it
// afterwards has no side effects.
func (s *State) SetAccounts(accounts map[string]*Account) {
for _, account := range accounts {
s.accounts.Set(account.Address, account.Copy())
}
}
// The returned ValidatorInfo is a copy, so mutating it
// has no side effects.
func (s *State) GetValidatorInfo(address []byte) *ValidatorInfo {
_, valInfo := s.validatorInfos.Get(address)
if valInfo == nil {
return nil
}
return valInfo.(*ValidatorInfo).Copy()
} }
// Returns false if new, true if updated. // Returns false if new, true if updated.
// The accDet is copied before setting, so mutating it // The valInfo is copied before setting, so mutating it
// afterwards has no side effects. // afterwards has no side effects.
func (s *State) SetAccountDetail(accDet *AccountDetail) (updated bool) { func (s *State) SetValidatorInfo(valInfo *ValidatorInfo) (updated bool) {
return s.accountDetails.Set(accDet.Id, accDet.Copy()) return s.validatorInfos.Set(valInfo.Address, valInfo.Copy())
} }
// Returns a hash that represents the state data, // Returns a hash that represents the state data,
@ -438,7 +577,7 @@ func (s *State) Hash() []byte {
hashables := []merkle.Hashable{ hashables := []merkle.Hashable{
s.BondedValidators, s.BondedValidators,
s.UnbondingValidators, s.UnbondingValidators,
s.accountDetails, s.accounts,
} }
return merkle.HashFromHashables(hashables) return merkle.HashFromHashables(hashables)
} }

View File

@ -1,34 +1,61 @@
package state package state
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary" . "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
) )
// Holds state for a Validator at a given height+round. // Persistent static data for each Validator
// Meant to be discarded every round of the consensus protocol. type ValidatorInfo struct {
// TODO consider moving this to another common types package. Address []byte
type Validator struct { PubKey PubKeyEd25519
Account UnbondTo []*TxOutput
BondHeight uint32 FirstBondHeight uint
UnbondHeight uint32
LastCommitHeight uint32 // If destroyed:
VotingPower uint64 DestroyedHeight uint
Accum int64 DestroyedAmount uint64
// If released:
ReleasedHeight uint
} }
// Used to persist the state of ConsensusStateControl. func (valInfo *ValidatorInfo) Copy() *ValidatorInfo {
func ReadValidator(r io.Reader, n *int64, err *error) *Validator { valInfoCopy := *valInfo
return &Validator{ return &valInfoCopy
Account: ReadAccount(r, n, err), }
BondHeight: ReadUInt32(r, n, err),
UnbondHeight: ReadUInt32(r, n, err), func ValidatorInfoEncoder(o interface{}, w io.Writer, n *int64, err *error) {
LastCommitHeight: ReadUInt32(r, n, err), WriteBinary(o.(*ValidatorInfo), w, n, err)
VotingPower: ReadUInt64(r, n, err), }
Accum: ReadInt64(r, n, err),
} func ValidatorInfoDecoder(r io.Reader, n *int64, err *error) interface{} {
return ReadBinary(&ValidatorInfo{}, r, n, err)
}
var ValidatorInfoCodec = Codec{
Encode: ValidatorInfoEncoder,
Decode: ValidatorInfoDecoder,
}
//-----------------------------------------------------------------------------
// Volatile state for each Validator
// Also persisted with the state, but fields change
// every height|round so they don't go in merkle.Tree
type Validator struct {
Address []byte
PubKey PubKeyEd25519
BondHeight uint
UnbondHeight uint
LastCommitHeight uint
VotingPower uint64
Accum int64
} }
// Creates a new copy of the validator so we can mutate accum. // Creates a new copy of the validator so we can mutate accum.
@ -37,17 +64,6 @@ func (v *Validator) Copy() *Validator {
return &vCopy return &vCopy
} }
// Used to persist the state of ConsensusStateControl.
func (v *Validator) WriteTo(w io.Writer) (n int64, err error) {
WriteBinary(w, v.Account, &n, &err)
WriteUInt32(w, v.BondHeight, &n, &err)
WriteUInt32(w, v.UnbondHeight, &n, &err)
WriteUInt32(w, v.LastCommitHeight, &n, &err)
WriteUInt64(w, v.VotingPower, &n, &err)
WriteInt64(w, v.Accum, &n, &err)
return
}
// Returns the one with higher Accum. // Returns the one with higher Accum.
func (v *Validator) CompareAccum(other *Validator) *Validator { func (v *Validator) CompareAccum(other *Validator) *Validator {
if v == nil { if v == nil {
@ -58,9 +74,9 @@ func (v *Validator) CompareAccum(other *Validator) *Validator {
} else if v.Accum < other.Accum { } else if v.Accum < other.Accum {
return other return other
} else { } else {
if v.Id < other.Id { if bytes.Compare(v.Address, other.Address) < 0 {
return v return v
} else if v.Id > other.Id { } else if bytes.Compare(v.Address, other.Address) > 0 {
return other return other
} else { } else {
panic("Cannot compare identical validators") panic("Cannot compare identical validators")
@ -69,8 +85,9 @@ func (v *Validator) CompareAccum(other *Validator) *Validator {
} }
func (v *Validator) String() string { func (v *Validator) String() string {
return fmt.Sprintf("Validator{%v %v-%v-%v VP:%v A:%v}", return fmt.Sprintf("Validator{%X %v %v-%v-%v VP:%v A:%v}",
v.Account, v.Address,
v.PubKey,
v.BondHeight, v.BondHeight,
v.LastCommitHeight, v.LastCommitHeight,
v.UnbondHeight, v.UnbondHeight,
@ -79,7 +96,7 @@ func (v *Validator) String() string {
} }
func (v *Validator) Hash() []byte { func (v *Validator) Hash() []byte {
return BinaryHash(v) return BinarySha256(v)
} }
//------------------------------------- //-------------------------------------
@ -89,11 +106,11 @@ var ValidatorCodec = validatorCodec{}
type validatorCodec struct{} type validatorCodec struct{}
func (vc validatorCodec) Encode(o interface{}, w io.Writer, n *int64, err *error) { func (vc validatorCodec) Encode(o interface{}, w io.Writer, n *int64, err *error) {
WriteBinary(w, o.(*Validator), n, err) WriteBinary(o.(*Validator), w, n, err)
} }
func (vc validatorCodec) Decode(r io.Reader, n *int64, err *error) interface{} { func (vc validatorCodec) Decode(r io.Reader, n *int64, err *error) interface{} {
return ReadValidator(r, n, err) return ReadBinary(&Validator{}, r, n, err)
} }
func (vc validatorCodec) Compare(o1 interface{}, o2 interface{}) int { func (vc validatorCodec) Compare(o1 interface{}, o2 interface{}) int {

View File

@ -1,12 +1,11 @@
package state package state
import ( import (
"bytes"
"fmt" "fmt"
"io"
"sort" "sort"
"strings" "strings"
. "github.com/tendermint/tendermint/binary"
"github.com/tendermint/tendermint/merkle" "github.com/tendermint/tendermint/merkle"
) )
@ -20,7 +19,7 @@ func (vs ValidatorSlice) Len() int {
} }
func (vs ValidatorSlice) Less(i, j int) bool { func (vs ValidatorSlice) Less(i, j int) bool {
return vs[i].Id < vs[j].Id return bytes.Compare(vs[i].Address, vs[j].Address) == -1
} }
func (vs ValidatorSlice) Swap(i, j int) { func (vs ValidatorSlice) Swap(i, j int) {
@ -31,10 +30,17 @@ func (vs ValidatorSlice) Swap(i, j int) {
//------------------------------------- //-------------------------------------
// Not goroutine-safe. // ValidatorSet represent a set of *Validator at a given height.
// TODO: consider validator Accum overflow? // The validators can be fetched by address or index.
// The index is in order of .Address, so the index are the same
// for all rounds of a given blockchain height.
// On the other hand, the .AccumPower of each validator and
// the designated .Proposer() of a set changes every round,
// upon calling .IncrementAccum().
// NOTE: Not goroutine-safe.
// NOTE: All get/set to validators should copy the value for safety.
// TODO: consider validator Accum overflow
// TODO: replace validators []*Validator with github.com/jaekwon/go-ibbs? // TODO: replace validators []*Validator with github.com/jaekwon/go-ibbs?
// NOTE: all get/set to validators should copy the value for safety.
type ValidatorSet struct { type ValidatorSet struct {
validators []*Validator validators []*Validator
@ -54,169 +60,149 @@ func NewValidatorSet(vals []*Validator) *ValidatorSet {
} }
} }
func ReadValidatorSet(r io.Reader, n *int64, err *error) *ValidatorSet { func (valSet *ValidatorSet) IncrementAccum() {
size := ReadUVarInt(r, n, err)
validators := []*Validator{}
for i := uint(0); i < size; i++ {
validator := ReadValidator(r, n, err)
validators = append(validators, validator)
}
sort.Sort(ValidatorSlice(validators))
return NewValidatorSet(validators)
}
func (vset *ValidatorSet) WriteTo(w io.Writer) (n int64, err error) {
WriteUVarInt(w, uint(len(vset.validators)), &n, &err)
vset.Iterate(func(index uint, val *Validator) bool {
WriteBinary(w, val, &n, &err)
return false
})
return
}
func (vset *ValidatorSet) IncrementAccum() {
// Decrement from previous proposer // Decrement from previous proposer
oldProposer := vset.Proposer() oldProposer := valSet.Proposer()
oldProposer.Accum -= int64(vset.TotalVotingPower()) oldProposer.Accum -= int64(valSet.TotalVotingPower())
vset.Update(oldProposer) valSet.Update(oldProposer)
var newProposer *Validator var newProposer *Validator
// Increment accum and find new proposer // Increment accum and find new proposer
// NOTE: updates validators in place. // NOTE: updates validators in place.
for _, val := range vset.validators { for _, val := range valSet.validators {
val.Accum += int64(val.VotingPower) val.Accum += int64(val.VotingPower)
newProposer = newProposer.CompareAccum(val) newProposer = newProposer.CompareAccum(val)
} }
vset.proposer = newProposer valSet.proposer = newProposer
} }
func (vset *ValidatorSet) Copy() *ValidatorSet { func (valSet *ValidatorSet) Copy() *ValidatorSet {
validators := make([]*Validator, len(vset.validators)) validators := make([]*Validator, len(valSet.validators))
for i, val := range vset.validators { for i, val := range valSet.validators {
// NOTE: must copy, since IncrementAccum updates in place. // NOTE: must copy, since IncrementAccum updates in place.
validators[i] = val.Copy() validators[i] = val.Copy()
} }
return &ValidatorSet{ return &ValidatorSet{
validators: validators, validators: validators,
proposer: vset.proposer, proposer: valSet.proposer,
totalVotingPower: vset.totalVotingPower, totalVotingPower: valSet.totalVotingPower,
} }
} }
func (vset *ValidatorSet) HasId(id uint64) bool { func (valSet *ValidatorSet) HasAddress(address []byte) bool {
idx := sort.Search(len(vset.validators), func(i int) bool { idx := sort.Search(len(valSet.validators), func(i int) bool {
return id <= vset.validators[i].Id return bytes.Compare(address, valSet.validators[i].Address) <= 0
}) })
return idx != len(vset.validators) && vset.validators[idx].Id == id return idx != len(valSet.validators) && bytes.Compare(valSet.validators[idx].Address, address) == 0
} }
func (vset *ValidatorSet) GetById(id uint64) (index uint, val *Validator) { func (valSet *ValidatorSet) GetByAddress(address []byte) (index uint, val *Validator) {
idx := sort.Search(len(vset.validators), func(i int) bool { idx := sort.Search(len(valSet.validators), func(i int) bool {
return id <= vset.validators[i].Id return bytes.Compare(address, valSet.validators[i].Address) <= 0
}) })
if idx != len(vset.validators) && vset.validators[idx].Id == id { if idx != len(valSet.validators) && bytes.Compare(valSet.validators[idx].Address, address) == 0 {
return uint(idx), vset.validators[idx].Copy() return uint(idx), valSet.validators[idx].Copy()
} else { } else {
return 0, nil return 0, nil
} }
} }
func (vset *ValidatorSet) GetByIndex(index uint) (id uint64, val *Validator) { func (valSet *ValidatorSet) GetByIndex(index uint) (address []byte, val *Validator) {
val = vset.validators[index] val = valSet.validators[index]
return val.Id, val.Copy() return val.Address, val.Copy()
} }
func (vset *ValidatorSet) Size() uint { func (valSet *ValidatorSet) Size() uint {
return uint(len(vset.validators)) return uint(len(valSet.validators))
} }
func (vset *ValidatorSet) TotalVotingPower() uint64 { func (valSet *ValidatorSet) TotalVotingPower() uint64 {
if vset.totalVotingPower == 0 { if valSet.totalVotingPower == 0 {
for _, val := range vset.validators { for _, val := range valSet.validators {
vset.totalVotingPower += val.VotingPower valSet.totalVotingPower += val.VotingPower
} }
} }
return vset.totalVotingPower return valSet.totalVotingPower
} }
func (vset *ValidatorSet) Proposer() (proposer *Validator) { func (valSet *ValidatorSet) Proposer() (proposer *Validator) {
if vset.proposer == nil { if valSet.proposer == nil {
for _, val := range vset.validators { for _, val := range valSet.validators {
vset.proposer = vset.proposer.CompareAccum(val) valSet.proposer = valSet.proposer.CompareAccum(val)
} }
} }
return vset.proposer.Copy() return valSet.proposer.Copy()
} }
func (vset *ValidatorSet) Hash() []byte { func (valSet *ValidatorSet) Hash() []byte {
if len(vset.validators) == 0 { if len(valSet.validators) == 0 {
return nil return nil
} }
hashables := make([]merkle.Hashable, len(vset.validators)) hashables := make([]merkle.Hashable, len(valSet.validators))
for i, val := range vset.validators { for i, val := range valSet.validators {
hashables[i] = val hashables[i] = val
} }
return merkle.HashFromHashables(hashables) return merkle.HashFromHashables(hashables)
} }
func (vset *ValidatorSet) Add(val *Validator) (added bool) { func (valSet *ValidatorSet) Add(val *Validator) (added bool) {
val = val.Copy() val = val.Copy()
idx := sort.Search(len(vset.validators), func(i int) bool { idx := sort.Search(len(valSet.validators), func(i int) bool {
return val.Id <= vset.validators[i].Id return bytes.Compare(val.Address, valSet.validators[i].Address) <= 0
}) })
if idx == len(vset.validators) { if idx == len(valSet.validators) {
vset.validators = append(vset.validators, val) valSet.validators = append(valSet.validators, val)
// Invalidate cache // Invalidate cache
vset.proposer = nil valSet.proposer = nil
vset.totalVotingPower = 0 valSet.totalVotingPower = 0
return true return true
} else if vset.validators[idx].Id == val.Id { } else if bytes.Compare(valSet.validators[idx].Address, val.Address) == 0 {
return false return false
} else { } else {
newValidators := append(vset.validators[:idx], val) newValidators := append(valSet.validators[:idx], val)
newValidators = append(newValidators, vset.validators[idx:]...) newValidators = append(newValidators, valSet.validators[idx:]...)
vset.validators = newValidators valSet.validators = newValidators
// Invalidate cache // Invalidate cache
vset.proposer = nil valSet.proposer = nil
vset.totalVotingPower = 0 valSet.totalVotingPower = 0
return true return true
} }
} }
func (vset *ValidatorSet) Update(val *Validator) (updated bool) { func (valSet *ValidatorSet) Update(val *Validator) (updated bool) {
index, sameVal := vset.GetById(val.Id) index, sameVal := valSet.GetByAddress(val.Address)
if sameVal == nil { if sameVal == nil {
return false return false
} else { } else {
vset.validators[index] = val.Copy() valSet.validators[index] = val.Copy()
// Invalidate cache // Invalidate cache
vset.proposer = nil valSet.proposer = nil
vset.totalVotingPower = 0 valSet.totalVotingPower = 0
return true return true
} }
} }
func (vset *ValidatorSet) Remove(id uint64) (val *Validator, removed bool) { func (valSet *ValidatorSet) Remove(address []byte) (val *Validator, removed bool) {
idx := sort.Search(len(vset.validators), func(i int) bool { idx := sort.Search(len(valSet.validators), func(i int) bool {
return id <= vset.validators[i].Id return bytes.Compare(address, valSet.validators[i].Address) <= 0
}) })
if idx == len(vset.validators) || vset.validators[idx].Id != id { if idx == len(valSet.validators) || bytes.Compare(valSet.validators[idx].Address, address) != 0 {
return nil, false return nil, false
} else { } else {
removedVal := vset.validators[idx] removedVal := valSet.validators[idx]
newValidators := vset.validators[:idx] newValidators := valSet.validators[:idx]
if idx+1 < len(vset.validators) { if idx+1 < len(valSet.validators) {
newValidators = append(newValidators, vset.validators[idx+1:]...) newValidators = append(newValidators, valSet.validators[idx+1:]...)
} }
vset.validators = newValidators valSet.validators = newValidators
// Invalidate cache // Invalidate cache
vset.proposer = nil valSet.proposer = nil
vset.totalVotingPower = 0 valSet.totalVotingPower = 0
return removedVal, true return removedVal, true
} }
} }
func (vset *ValidatorSet) Iterate(fn func(index uint, val *Validator) bool) { func (valSet *ValidatorSet) Iterate(fn func(index uint, val *Validator) bool) {
for i, val := range vset.validators { for i, val := range valSet.validators {
stop := fn(uint(i), val.Copy()) stop := fn(uint(i), val.Copy())
if stop { if stop {
break break
@ -224,13 +210,13 @@ func (vset *ValidatorSet) Iterate(fn func(index uint, val *Validator) bool) {
} }
} }
func (vset *ValidatorSet) String() string { func (valSet *ValidatorSet) String() string {
return vset.StringWithIndent("") return valSet.StringWithIndent("")
} }
func (vset *ValidatorSet) StringWithIndent(indent string) string { func (valSet *ValidatorSet) StringWithIndent(indent string) string {
valStrings := []string{} valStrings := []string{}
vset.Iterate(func(index uint, val *Validator) bool { valSet.Iterate(func(index uint, val *Validator) bool {
valStrings = append(valStrings, val.String()) valStrings = append(valStrings, val.String())
return false return false
}) })
@ -239,7 +225,7 @@ func (vset *ValidatorSet) StringWithIndent(indent string) string {
%s Validators: %s Validators:
%s %v %s %v
%s}`, %s}`,
indent, vset.Proposer().String(), indent, valSet.Proposer().String(),
indent, indent,
indent, strings.Join(valStrings, "\n"+indent+" "), indent, strings.Join(valStrings, "\n"+indent+" "),
indent) indent)

28
wallet/priv_account.go Normal file
View File

@ -0,0 +1,28 @@
package wallet
import (
"github.com/tendermint/go-ed25519"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/common"
)
type PrivAccount struct {
PubKey PubKey
PrivKey PrivKey
}
// Generates a new account with private key.
func GenPrivAccount() *PrivAccount {
privKey := CRandBytes(32)
pubKey := ed25519.MakePubKey(privKey)
return &PrivAccount{
PubKeyEd25519{
PubKey: pubKey,
},
PrivKeyEd25519{
PubKey: pubKey,
PrivKey: privKey,
},
}
}