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 (
. "github.com/tendermint/tendermint/common"

View File

@ -1,23 +1,31 @@
package binary
import "io"
import (
"io"
"reflect"
)
type Binary interface {
WriteTo(w io.Writer) (int64, error)
func ReadBinary(o interface{}, r io.Reader, n *int64, err *error) interface{} {
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) {
if *err != nil {
return
}
n_, err_ := b.WriteTo(w)
*n += int64(n_)
*err = err_
func WriteBinary(o interface{}, w io.Writer, n *int64, err *error) {
rv := reflect.ValueOf(o)
rt := reflect.TypeOf(o)
writeReflect(rv, rt, w, n, err)
}
// Write all of bz to w
// 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 {
return
}
@ -28,7 +36,7 @@ func WriteTo(w io.Writer, bz []byte, n *int64, err *error) {
// Read len(buf) from r
// 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 {
return
}

View File

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

View File

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

View File

@ -8,20 +8,20 @@ import (
// Byte
func WriteByte(w io.Writer, b byte, n *int64, err *error) {
WriteTo(w, []byte{b}, n, err)
func WriteByte(b byte, w io.Writer, n *int64, err *error) {
WriteTo([]byte{b}, w, n, err)
}
func ReadByte(r io.Reader, n *int64, err *error) byte {
buf := make([]byte, 1)
ReadFull(r, buf, n, err)
ReadFull(buf, r, n, err)
return buf[0]
}
// Int8
func WriteInt8(w io.Writer, i int8, n *int64, err *error) {
WriteByte(w, byte(i), n, err)
func WriteInt8(i int8, w io.Writer, n *int64, err *error) {
WriteByte(byte(i), w, n, err)
}
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
func WriteUInt8(w io.Writer, i uint8, n *int64, err *error) {
WriteByte(w, byte(i), n, err)
func WriteUInt8(i uint8, w io.Writer, n *int64, err *error) {
WriteByte(byte(i), w, n, err)
}
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
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)
binary.LittleEndian.PutUint16(buf, uint16(i))
*n += 2
WriteTo(w, buf, n, err)
WriteTo(buf, w, n, err)
}
func ReadInt16(r io.Reader, n *int64, err *error) int16 {
buf := make([]byte, 2)
ReadFull(r, buf, n, err)
ReadFull(buf, r, n, err)
return int16(binary.LittleEndian.Uint16(buf))
}
// 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)
binary.LittleEndian.PutUint16(buf, uint16(i))
*n += 2
WriteTo(w, buf, n, err)
WriteTo(buf, w, n, err)
}
func ReadUInt16(r io.Reader, n *int64, err *error) uint16 {
buf := make([]byte, 2)
ReadFull(r, buf, n, err)
ReadFull(buf, r, n, err)
return uint16(binary.LittleEndian.Uint16(buf))
}
// []UInt16
func WriteUInt16s(w io.Writer, iz []uint16, n *int64, err *error) {
WriteUInt32(w, uint32(len(iz)), n, err)
func WriteUInt16s(iz []uint16, w io.Writer, n *int64, err *error) {
WriteUInt32(uint32(len(iz)), w, n, err)
for _, i := range iz {
WriteUInt16(w, i, n, err)
WriteUInt16(i, w, n, err)
if *err != nil {
return
}
@ -98,71 +98,71 @@ func ReadUInt16s(r io.Reader, n *int64, err *error) []uint16 {
// 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)
binary.LittleEndian.PutUint32(buf, uint32(i))
*n += 4
WriteTo(w, buf, n, err)
WriteTo(buf, w, n, err)
}
func ReadInt32(r io.Reader, n *int64, err *error) int32 {
buf := make([]byte, 4)
ReadFull(r, buf, n, err)
ReadFull(buf, r, n, err)
return int32(binary.LittleEndian.Uint32(buf))
}
// 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)
binary.LittleEndian.PutUint32(buf, uint32(i))
*n += 4
WriteTo(w, buf, n, err)
WriteTo(buf, w, n, err)
}
func ReadUInt32(r io.Reader, n *int64, err *error) uint32 {
buf := make([]byte, 4)
ReadFull(r, buf, n, err)
ReadFull(buf, r, n, err)
return uint32(binary.LittleEndian.Uint32(buf))
}
// 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)
binary.LittleEndian.PutUint64(buf, uint64(i))
*n += 8
WriteTo(w, buf, n, err)
WriteTo(buf, w, n, err)
}
func ReadInt64(r io.Reader, n *int64, err *error) int64 {
buf := make([]byte, 8)
ReadFull(r, buf, n, err)
ReadFull(buf, r, n, err)
return int64(binary.LittleEndian.Uint64(buf))
}
// 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)
binary.LittleEndian.PutUint64(buf, uint64(i))
*n += 8
WriteTo(w, buf, n, err)
WriteTo(buf, w, n, err)
}
func ReadUInt64(r io.Reader, n *int64, err *error) uint64 {
buf := make([]byte, 8)
ReadFull(r, buf, n, err)
ReadFull(buf, r, n, err)
return uint64(binary.LittleEndian.Uint64(buf))
}
// 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)
n_ := int64(binary.PutVarint(buf, int64(i)))
*n += n_
WriteTo(w, buf[:n_], n, err)
WriteTo(buf[:n_], w, n, err)
}
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
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)
n_ := int64(binary.PutUvarint(buf, uint64(i)))
*n += n_
WriteTo(w, buf[:n_], n, err)
WriteTo(buf[:n_], w, n, err)
}
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
func WriteString(w io.Writer, s string, n *int64, err *error) {
WriteUInt32(w, uint32(len(s)), n, err)
WriteTo(w, []byte(s), n, err)
func WriteString(s string, w io.Writer, n *int64, err *error) {
WriteUInt32(uint32(len(s)), w, n, err)
WriteTo([]byte(s), w, n, err)
}
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 ""
}
buf := make([]byte, int(length))
ReadFull(r, buf, n, err)
ReadFull(buf, r, n, err)
return string(buf)
}

View File

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

View File

@ -2,36 +2,46 @@ package binary
import (
"bytes"
"code.google.com/p/go.crypto/ripemd160"
"crypto/sha256"
)
func BinaryBytes(b Binary) []byte {
buf := bytes.NewBuffer(nil)
_, err := b.WriteTo(buf)
if err != nil {
func BinaryBytes(o interface{}) []byte {
w, n, err := new(bytes.Buffer), new(int64), new(error)
WriteBinary(o, w, n, err)
if *err != nil {
panic(err)
}
return buf.Bytes()
return w.Bytes()
}
// 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)
bBytes := BinaryBytes(b)
return bytes.Equal(aBytes, bBytes)
}
// 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)
bBytes := BinaryBytes(b)
return bytes.Compare(aBytes, bBytes)
}
func BinaryHash(b Binary) []byte {
hasher := sha256.New()
_, err := b.WriteTo(hasher)
if err != nil {
func BinarySha256(o interface{}) []byte {
hasher, n, err := sha256.New(), new(int64), new(error)
WriteBinary(o, hasher, n, err)
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)
}
return hasher.Sum(nil)

View File

@ -5,10 +5,10 @@ import (
"crypto/sha256"
"errors"
"fmt"
"io"
"strings"
"time"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
. "github.com/tendermint/tendermint/config"
@ -24,23 +24,8 @@ type Block struct {
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.
func (b *Block) ValidateBasic(lastBlockHeight uint32, lastBlockHash []byte,
func (b *Block) ValidateBasic(lastBlockHeight uint, lastBlockHash []byte,
lastBlockParts PartSetHeader, lastBlockTime time.Time) error {
if b.Network != Config.Network {
return errors.New("Invalid block network")
@ -115,7 +100,7 @@ func (b *Block) Description() string {
type Header struct {
Network string
Height uint32
Height uint
Time time.Time
Fees uint64
LastBlockHash []byte
@ -126,37 +111,11 @@ type Header struct {
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 {
if h.hash == nil {
hasher := sha256.New()
_, err := h.WriteTo(hasher)
if err != nil {
hasher, n, err := sha256.New(), new(int64), new(error)
WriteBinary(h, hasher, n, err)
if *err != nil {
panic(err)
}
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 {
Commits []RoundSignature
Commits []Commit
// Volatile
hash []byte
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 {
if v.hash == nil {
bs := make([]Binary, len(v.Commits))
bs := make([]interface{}, len(v.Commits))
for i, commit := range v.Commits {
bs[i] = Binary(commit)
bs[i] = commit
}
v.hash = merkle.HashFromBinaries(bs)
}
@ -231,8 +195,8 @@ func (v *Validation) StringWithIndent(indent string) string {
func (v *Validation) BitArray() BitArray {
if v.bitArray.IsZero() {
v.bitArray = NewBitArray(uint(len(v.Commits)))
for i, rsig := range v.Commits {
v.bitArray.SetIndex(uint(i), !rsig.IsZero())
for i, commit := range v.Commits {
v.bitArray.SetIndex(uint(i), !commit.IsZero())
}
}
return v.bitArray
@ -247,28 +211,11 @@ type Data struct {
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 {
if data.hash == nil {
bs := make([]Binary, len(data.Txs))
bs := make([]interface{}, len(data.Txs))
for i, tx := range data.Txs {
bs[i] = Binary(tx)
bs[i] = tx
}
data.hash = merkle.HashFromBinaries(bs)
}
@ -278,7 +225,7 @@ func (data *Data) Hash() []byte {
func (data *Data) StringWithIndent(indent string) string {
txStrings := make([]string, len(data.Txs))
for i, tx := range data.Txs {
txStrings[i] = tx.String()
txStrings[i] = fmt.Sprintf("Tx:%v", tx)
}
return fmt.Sprintf(`Data{
%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"
"sync"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
"github.com/tendermint/tendermint/merkle"
)
@ -24,7 +23,7 @@ var (
)
type Part struct {
Index uint16
Index uint
Trail [][]byte
Bytes []byte
@ -32,21 +31,6 @@ type Part struct {
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 {
if part.hash != nil {
return part.hash
@ -84,23 +68,10 @@ func (part *Part) StringWithIndent(indent string) string {
//-------------------------------------
type PartSetHeader struct {
Total uint16
Total uint
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 {
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 {
total uint16
total uint
hash []byte
mtx sync.Mutex
parts []*Part
partsBitArray BitArray
count uint16
count uint
}
// Returns an immutable, full PartSet.
@ -135,7 +106,7 @@ func NewPartSetFromData(data []byte) *PartSet {
partsBitArray := NewBitArray(uint(total))
for i := 0; i < total; i++ {
part := &Part{
Index: uint16(i),
Index: uint(i),
Bytes: data[i*partSize : MinInt(len(data), (i+1)*partSize)],
}
parts[i] = part
@ -148,11 +119,11 @@ func NewPartSetFromData(data []byte) *PartSet {
parts[i].Trail = trails[i].Flatten()
}
return &PartSet{
total: uint16(total),
total: uint(total),
hash: rootTrail.Hash,
parts: parts,
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)
}
func (ps *PartSet) Count() uint16 {
func (ps *PartSet) Count() uint {
if ps == nil {
return 0
}
return ps.count
}
func (ps *PartSet) Total() uint16 {
func (ps *PartSet) Total() uint {
if ps == nil {
return 0
}
@ -247,7 +218,7 @@ func (ps *PartSet) AddPart(part *Part) (bool, error) {
return true, nil
}
func (ps *PartSet) GetPart(index uint16) *Part {
func (ps *PartSet) GetPart(index uint) *Part {
ps.mtx.Lock()
defer ps.mtx.Unlock()
return ps.parts[index]

View File

@ -27,7 +27,7 @@ func TestBasicPartSet(t *testing.T) {
// Test adding parts to a new partSet.
partSet2 := NewPartSetFromHeader(partSet.Header())
for i := uint16(0); i < partSet.Total(); i++ {
for i := uint(0); i < partSet.Total(); i++ {
part := partSet.GetPart(i)
//t.Logf("\n%v", 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 (
"bytes"
"encoding/binary"
"encoding/json"
"fmt"
"io"
. "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).
*/
type BlockStore struct {
height uint32
height uint
db db_.DB
}
@ -30,7 +30,7 @@ func NewBlockStore(db db_.DB) *BlockStore {
}
// Height() returns the last known contiguous block height.
func (bs *BlockStore) Height() uint32 {
func (bs *BlockStore) Height() uint {
return bs.height
}
@ -42,49 +42,49 @@ func (bs *BlockStore) GetReader(key []byte) io.Reader {
return bytes.NewReader(bytez)
}
func (bs *BlockStore) LoadBlock(height uint32) *Block {
func (bs *BlockStore) LoadBlock(height uint) *Block {
var n int64
var err error
meta := ReadBlockMeta(bs.GetReader(calcBlockMetaKey(height)), &n, &err)
meta := ReadBinary(&BlockMeta{}, bs.GetReader(calcBlockMetaKey(height)), &n, &err).(*BlockMeta)
if err != nil {
Panicf("Error reading block meta: %v", err)
}
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)
bytez = append(bytez, part.Bytes...)
}
block := ReadBlock(bytes.NewReader(bytez), &n, &err)
block := ReadBinary(&Block{}, bytes.NewReader(bytez), &n, &err).(*Block)
if err != nil {
Panicf("Error reading block: %v", err)
}
return block
}
func (bs *BlockStore) LoadBlockPart(height uint32, index uint16) *Part {
func (bs *BlockStore) LoadBlockPart(height uint, index uint) *Part {
var n int64
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 {
Panicf("Error reading block part: %v", err)
}
return part
}
func (bs *BlockStore) LoadBlockMeta(height uint32) *BlockMeta {
func (bs *BlockStore) LoadBlockMeta(height uint) *BlockMeta {
var n int64
var err error
meta := ReadBlockMeta(bs.GetReader(calcBlockMetaKey(height)), &n, &err)
meta := ReadBinary(&BlockMeta{}, bs.GetReader(calcBlockMetaKey(height)), &n, &err).(*BlockMeta)
if err != nil {
Panicf("Error reading block meta: %v", err)
}
return meta
}
func (bs *BlockStore) LoadBlockValidation(height uint32) *Validation {
func (bs *BlockStore) LoadBlockValidation(height uint) *Validation {
var n int64
var err error
validation := ReadValidation(bs.GetReader(calcBlockValidationKey(height)), &n, &err)
validation := ReadBinary(&Validation{}, bs.GetReader(calcBlockValidationKey(height)), &n, &err).(*Validation)
if err != nil {
Panicf("Error reading validation: %v", err)
}
@ -108,7 +108,7 @@ func (bs *BlockStore) SaveBlock(block *Block, blockParts *PartSet) {
metaBytes := BinaryBytes(meta)
bs.db.Set(calcBlockMetaKey(height), metaBytes)
// 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))
}
// Save block validation (duplicate and separate)
@ -120,7 +120,7 @@ func (bs *BlockStore) SaveBlock(block *Block, blockParts *PartSet) {
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 {
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
}
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 {
buf := [5]byte{'H'}
binary.BigEndian.PutUint32(buf[1:5], height)
return buf[:]
func calcBlockMetaKey(height uint) []byte {
return []byte(fmt.Sprintf("H:%v", height))
}
func calcBlockPartKey(height uint32, partIndex uint16) []byte {
buf := [7]byte{'P'}
binary.BigEndian.PutUint32(buf[1:5], height)
binary.BigEndian.PutUint16(buf[5:7], partIndex)
return buf[:]
func calcBlockPartKey(height uint, partIndex uint) []byte {
return []byte(fmt.Sprintf("P:%v:%v", height, partIndex))
}
func calcBlockValidationKey(height uint32) []byte {
buf := [5]byte{'V'}
binary.BigEndian.PutUint32(buf[1:5], height)
return buf[:]
func calcBlockValidationKey(height uint) []byte {
return []byte(fmt.Sprintf("V:%v", height))
}
//-----------------------------------------------------------------------------
@ -177,7 +155,7 @@ func calcBlockValidationKey(height uint32) []byte {
var blockStoreKey = []byte("blockStore")
type BlockStoreJSON struct {
Height uint32
Height uint
}
func (bsj BlockStoreJSON) Save(db db_.DB) {

View File

@ -1,35 +1,33 @@
package blocks
import (
"fmt"
"errors"
"io"
"reflect"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
)
/*
Tx (Transaction) is an atomic operation on the ledger state.
Account Txs:
1. Send Send coins to account
2. Name Associate account with a name
1. SendTx Send coins to address
Validation Txs:
3. Bond New validator posts a bond
4. Unbond Validator leaves
5. Dupeout Validator dupes out (signs twice)
1. BondTx New validator posts a bond
2. UnbondTx Validator leaves
3. DupeoutTx Validator dupes out (equivocates)
*/
type Tx interface {
Signable
GetSequence() uint
GetFee() uint64
String() string
WriteSignBytes(w io.Writer, n *int64, err *error)
}
const (
// Account transactions
TxTypeSend = byte(0x01)
TxTypeName = byte(0x02)
// Validation transactions
TxTypeBond = byte(0x11)
@ -37,174 +35,150 @@ const (
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 {
case TxTypeSend:
return &SendTx{
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),
}
return ReadBinary(&SendTx{}, r, n, err)
case TxTypeBond:
return &BondTx{
BaseTx: ReadBaseTx(r, n, err),
//UnbondTo: ReadUInt64(r, n, err),
}
return ReadBinary(&BondTx{}, r, n, err)
case TxTypeUnbond:
return &UnbondTx{
BaseTx: ReadBaseTx(r, n, err),
}
return ReadBinary(&UnbondTx{}, r, n, err)
case TxTypeDupeout:
return &DupeoutTx{
BaseTx: ReadBaseTx(r, n, err),
VoteA: *ReadVote(r, n, err),
VoteB: *ReadVote(r, n, err),
}
return ReadBinary(&DupeoutTx{}, r, n, err)
default:
*err = Errorf("Unknown Tx type %X", t)
return nil
}
}
var _ = RegisterType(&TypeInfo{
Type: reflect.TypeOf((*Tx)(nil)).Elem(),
Decoder: TxDecoder,
})
//-----------------------------------------------------------------------------
type BaseTx struct {
Sequence uint
Fee uint64
Signature Signature
type TxInput struct {
Address []byte // Hash of the PubKey
Amount uint64 // Must not exceed account balance
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 {
return BaseTx{
Sequence: ReadUVarInt(r, n, err),
Fee: ReadUInt64(r, n, err),
Signature: ReadSignature(r, n, err),
func (txIn *TxInput) ValidateBasic() error {
if len(txIn.Address) != 20 {
return ErrTxInvalidAddress
}
if txIn.Amount == 0 {
return ErrTxInvalidAmount
}
return nil
}
func (tx BaseTx) WriteTo(w io.Writer) (n int64, err error) {
WriteUVarInt(w, tx.Sequence, &n, &err)
WriteUInt64(w, tx.Fee, &n, &err)
WriteBinary(w, tx.Signature, &n, &err)
return
func (txIn *TxInput) WriteSignBytes(w io.Writer, n *int64, err *error) {
WriteByteSlice(txIn.Address, w, n, err)
WriteUInt64(txIn.Amount, w, n, err)
WriteUVarInt(txIn.Sequence, w, n, err)
}
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 {
return tx.Signature
func (txOut *TxOutput) ValidateBasic() error {
if len(txOut.Address) != 20 {
return ErrTxInvalidAddress
}
if txOut.Amount == 0 {
return ErrTxInvalidAmount
}
return nil
}
func (tx *BaseTx) GetFee() uint64 {
return tx.Fee
}
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)
func (txOut *TxOutput) WriteSignBytes(w io.Writer, n *int64, err *error) {
WriteByteSlice(txOut.Address, w, n, err)
WriteUInt64(txOut.Amount, w, n, err)
}
//-----------------------------------------------------------------------------
type SendTx struct {
BaseTx
To uint64
Amount uint64
Inputs []*TxInput
Outputs []*TxOutput
}
func (tx *SendTx) WriteTo(w io.Writer) (n int64, err error) {
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) TypeByte() byte { return TxTypeSend }
func (tx *SendTx) WriteSignBytes(w io.Writer, n *int64, err *error) {
WriteUVarInt(uint(len(tx.Inputs)), w, n, err)
for _, in := range tx.Inputs {
in.WriteSignBytes(w, n, err)
}
func (tx *SendTx) String() string {
return fmt.Sprintf("SendTx{%v To:%v Amount:%v}", tx.BaseTx, tx.To, tx.Amount)
WriteUVarInt(uint(len(tx.Outputs)), w, n, err)
for _, out := range tx.Outputs {
out.WriteSignBytes(w, n, err)
}
//-----------------------------------------------------------------------------
type NameTx struct {
BaseTx
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 {
BaseTx
//UnbondTo uint64
PubKey PubKeyEd25519
Inputs []*TxInput
UnbondTo []*TxOutput
}
func (tx *BondTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, TxTypeBond, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err)
//WriteUInt64(w, tx.UnbondTo, &n, &err)
return
}
func (tx *BondTx) TypeByte() byte { return TxTypeBond }
func (tx *BondTx) String() string {
return fmt.Sprintf("BondTx{%v}", tx.BaseTx)
func (tx *BondTx) WriteSignBytes(w io.Writer, n *int64, err *error) {
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 {
BaseTx
Address []byte
Height uint
Signature SignatureEd25519
}
func (tx *UnbondTx) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, TxTypeUnbond, &n, &err)
WriteBinary(w, tx.BaseTx, &n, &err)
return
}
func (tx *UnbondTx) TypeByte() byte { return TxTypeUnbond }
func (tx *UnbondTx) String() string {
return fmt.Sprintf("UnbondTx{%v}", tx.BaseTx)
func (tx *UnbondTx) WriteSignBytes(w io.Writer, n *int64, err *error) {
WriteByteSlice(tx.Address, w, n, err)
WriteUVarInt(tx.Height, w, n, err)
}
//-----------------------------------------------------------------------------
type DupeoutTx struct {
BaseTx
Address []byte
VoteA Vote
VoteB Vote
}
func (tx *DupeoutTx) WriteTo(w io.Writer) (n int64, err error) {
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) TypeByte() byte { return TxTypeDupeout }
func (tx *DupeoutTx) String() string {
return fmt.Sprintf("DupeoutTx{%v VoteA:%v VoteB:%v}", tx.BaseTx, tx.VoteA, tx.VoteB)
func (tx *DupeoutTx) WriteSignBytes(w io.Writer, n *int64, err *error) {
panic("DupeoutTx has no sign bytes")
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"io"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
)
@ -23,53 +24,34 @@ var (
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 {
Height uint32
Round uint16
Height uint
Round uint
Type byte
BlockHash []byte // empty if vote is nil.
BlockParts PartSetHeader // zero if vote is nil.
Signature
Signature SignatureEd25519
}
func ReadVote(r io.Reader, n *int64, err *error) *Vote {
return &Vote{
Height: ReadUInt32(r, n, err),
Round: ReadUInt16(r, n, err),
Type: ReadByte(r, n, err),
BlockHash: ReadByteSlice(r, n, err),
BlockParts: ReadPartSetHeader(r, n, err),
Signature: ReadSignature(r, n, err),
}
func (vote *Vote) WriteSignBytes(w io.Writer, n *int64, err *error) {
WriteUVarInt(vote.Height, w, n, err)
WriteUVarInt(vote.Round, w, n, err)
WriteByte(vote.Type, w, n, err)
WriteByteSlice(vote.BlockHash, w, n, err)
WriteBinary(vote.BlockParts, w, n, err)
}
func (v *Vote) WriteTo(w io.Writer) (n int64, err error) {
WriteUInt32(w, v.Height, &n, &err)
WriteUInt16(w, v.Round, &n, &err)
WriteByte(w, v.Type, &n, &err)
WriteByteSlice(w, v.BlockHash, &n, &err)
WriteBinary(w, v.BlockParts, &n, &err)
WriteBinary(w, v.Signature, &n, &err)
return
func (vote *Vote) Copy() *Vote {
voteCopy := *vote
return &voteCopy
}
func (v *Vote) GetSignature() Signature {
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 {
func (vote *Vote) String() string {
var typeString string
switch v.Type {
switch vote.Type {
case VoteTypePrevote:
typeString = "Prevote"
case VoteTypePrecommit:
@ -80,5 +62,5 @@ func (v *Vote) String() string {
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.
state := state_.LoadState(stateDB)
if state == nil {
state = state_.GenesisStateFromFile(stateDB, config.RootDir+"/genesis.json")
state = state_.GenesisStateFromFile(stateDB, config.GenesisFile())
state.Save()
}
// Get PrivAccount
// Get PrivValidator
var privValidator *consensus.PrivValidator
if _, err := os.Stat(config.RootDir + "/private.json"); err == nil {
privAccount := state_.PrivAccountFromFile(config.RootDir + "/private.json")
privValidatorDB := db_.NewMemDB() // TODO configurable db.
privValidator = consensus.NewPrivValidator(privValidatorDB, privAccount)
if _, err := os.Stat(config.PrivValidatorFile()); err == nil {
privValidator = consensus.LoadPrivValidator()
}
// Get PEXReactor
book := p2p.NewAddrBook(config.RootDir + "/addrbook.json")
book := p2p.NewAddrBook(config.AddrBookFile())
pexReactor := p2p.NewPEXReactor(book)
// Get MempoolReactor

View File

@ -4,13 +4,15 @@ import (
"encoding/base64"
"fmt"
"github.com/tendermint/tendermint/state"
. "github.com/tendermint/tendermint/binary"
"github.com/tendermint/tendermint/wallet"
)
func gen_account() {
// TODO: uh, write better logic.
// Generate private account
privAccount := state.GenPrivAccount()
privAccount := wallet.GenPrivAccount()
fmt.Printf(`Generated account:
Account Public Key: %X
@ -19,7 +21,7 @@ Account Private Key: %X
(base64) %v
`,
privAccount.PubKey,
base64.StdEncoding.EncodeToString(privAccount.PubKey),
base64.StdEncoding.EncodeToString(BinaryBytes(privAccount.PubKey)),
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:
daemon Run the tendermint node daemon
gen_account Generate new account keypair
gen_validator Generate new validator keypair
tendermint --help for command options`)
return
@ -29,5 +30,7 @@ tendermint --help for command options`)
daemon()
case "gen_account":
gen_account()
case "gen_validator":
gen_validator()
}
}

View File

@ -2,11 +2,8 @@ package common
import (
"fmt"
"io"
"math/rand"
"strings"
. "github.com/tendermint/tendermint/binary"
)
// Not goroutine safe
@ -19,47 +16,6 @@ func NewBitArray(bits uint) BitArray {
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 {
return bA.bits
}

View File

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

View File

@ -2,111 +2,81 @@ package consensus
import (
"fmt"
"io"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common"
"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.
// +2/3 of validators' prevotes for a given blockhash (or nil)
type POL struct {
Height uint32
Round uint16
Height uint
Round uint
BlockHash []byte // Could be nil, which makes this a proof of unlock.
BlockParts PartSetHeader // When BlockHash is nil, this is zero.
Votes []Signature // Vote signatures for height/round/hash
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
Votes []POLVoteSignature // Prevote and commit signatures in ValidatorSet order.
}
// 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)
voteDoc := BinaryBytes(&Vote{
prevoteDoc := SignBytes(&Vote{
Height: pol.Height, Round: pol.Round, Type: VoteTypePrevote,
BlockHash: pol.BlockHash,
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
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)
}
_, val := vset.GetById(sig.SignerId)
if val == nil {
return Errorf("Invalid validator for vote %v for POL %v", sig, pol)
}
if !val.VerifyBytes(voteDoc, sig) {
if !val.PubKey.VerifyBytes(voteDoc, sig.Signature.Bytes) {
return Errorf("Invalid signature for vote %v for POL %v", sig, pol)
}
// Tally
seenValidators[val.Id] = struct{}{}
seenValidators[string(val.Address)] = struct{}{}
talliedVotingPower += val.VotingPower
}
for _, rsig := range pol.Commits {
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 {
if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
return nil
} else {
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
// TODO: This logic is crude. Should be more transactional.
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
db_ "github.com/tendermint/tendermint/db"
"github.com/tendermint/tendermint/state"
. "github.com/tendermint/tendermint/common"
. "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
)
func voteToStep(vote *Vote) uint8 {
switch vote.Type {
case VoteTypePrevote:
return stepPrevote
case VoteTypePrecommit:
return stepPrecommit
case VoteTypeCommit:
return stepCommit
default:
panic("Unknown vote type")
}
}
type PrivValidator struct {
db db_.DB
state.PrivAccount
Address []byte
PubKey PubKeyEd25519
PrivKey PrivKeyEd25519
LastHeight uint
LastRound uint
LastStep uint8
}
func NewPrivValidator(db db_.DB, priv *state.PrivAccount) *PrivValidator {
return &PrivValidator{db, *priv}
// 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,
}
}
// Double signing results in a panic.
func (pv *PrivValidator) Sign(o Signable) {
switch o.(type) {
case *Proposal:
//TODO: prevent double signing && test.
pv.PrivAccount.Sign(o.(*Proposal))
case *Vote:
//TODO: prevent double signing && test.
pv.PrivAccount.Sign(o.(*Vote))
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"
"io"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
)
@ -15,15 +16,14 @@ var (
)
type Proposal struct {
Height uint32
Round uint16
Height uint
Round uint
BlockParts 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{
Height: height,
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 {
return fmt.Sprintf("Proposal{%v/%v %v %v %v}", p.Height, p.Round,
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"
"errors"
"fmt"
"io"
"sync"
"sync/atomic"
"time"
@ -123,9 +122,9 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte
msg := msg_.(*NewRoundStepMessage)
ps.ApplyNewRoundStepMessage(msg, rs)
case *CommitMessage:
msg := msg_.(*CommitMessage)
ps.ApplyCommitMessage(msg)
case *CommitStepMessage:
msg := msg_.(*CommitStepMessage)
ps.ApplyCommitStepMessage(msg)
case *HasVoteMessage:
msg := msg_.(*HasVoteMessage)
@ -160,10 +159,17 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte
case VoteCh:
switch msg_.(type) {
case *Vote:
vote := msg_.(*Vote)
added, index, err := conR.conS.AddVote(vote)
case *VoteMessage:
voteMessage := msg_.(*VoteMessage)
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 {
// Probably an invalid signature. Bad peer.
log.Warning("Error attempting to add vote: %v", err)
}
// Initialize Prevotes/Precommits/Commits if needed
@ -220,14 +226,14 @@ func (conR *ConsensusReactor) broadcastNewRoundStepRoutine() {
Height: rs.Height,
Round: rs.Round,
Step: rs.Step,
SecondsSinceStartTime: uint32(timeElapsed.Seconds()),
SecondsSinceStartTime: uint(timeElapsed.Seconds()),
}
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 {
msg := &CommitMessage{
msg := &CommitStepMessage{
Height: rs.Height,
BlockParts: rs.ProposalBlockParts.Header(),
BlockBitArray: rs.ProposalBlockParts.BitArray(),
@ -259,10 +265,10 @@ OUTER_LOOP:
Height: rs.Height,
Round: rs.Round,
Type: partTypeProposalBlock,
Part: rs.ProposalBlockParts.GetPart(uint16(index)),
Part: rs.ProposalBlockParts.GetPart(index),
}
peer.Send(DataCh, msg)
ps.SetHasProposalBlockPart(rs.Height, rs.Round, uint16(index))
ps.SetHasProposalBlockPart(rs.Height, rs.Round, index)
continue OUTER_LOOP
}
}
@ -289,10 +295,10 @@ OUTER_LOOP:
Height: rs.Height,
Round: rs.Round,
Type: partTypeProposalPOL,
Part: rs.ProposalPOLParts.GetPart(uint16(index)),
Part: rs.ProposalPOLParts.GetPart(index),
}
peer.Send(DataCh, msg)
ps.SetHasProposalPOLPart(rs.Height, rs.Round, uint16(index))
ps.SetHasProposalPOLPart(rs.Height, rs.Round, index)
continue OUTER_LOOP
}
}
@ -320,7 +326,7 @@ OUTER_LOOP:
if ok {
vote := voteSet.GetByIndex(index)
// NOTE: vote may be a commit.
msg := p2p.TypedMessage{msgTypeVote, vote}
msg := &VoteMessage{index, vote}
peer.Send(VoteCh, msg)
ps.SetHasVote(vote, index)
return true
@ -399,7 +405,7 @@ OUTER_LOOP:
BlockParts: header.LastBlockParts,
Signature: rsig.Signature,
}
msg := p2p.TypedMessage{msgTypeVote, vote}
msg := &VoteMessage{index, vote}
peer.Send(VoteCh, msg)
ps.SetHasVote(vote, index)
continue OUTER_LOOP
@ -416,8 +422,8 @@ OUTER_LOOP:
// Read only when returned by PeerState.GetRoundState().
type PeerRoundState struct {
Height uint32 // Height peer is at
Round uint16 // Round peer is at
Height uint // Height peer is at
Round uint // Round peer is at
Step RoundStep // Step peer is at
StartTime time.Time // Estimated start of round 0 at this height
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))
}
func (ps *PeerState) SetHasProposalBlockPart(height uint32, round uint16, index uint16) {
func (ps *PeerState) SetHasProposalBlockPart(height uint, round uint, index uint) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
@ -485,7 +491,7 @@ func (ps *PeerState) SetHasProposalBlockPart(height uint32, round uint16, index
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()
defer ps.mtx.Unlock()
@ -496,7 +502,7 @@ func (ps *PeerState) SetHasProposalPOLPart(height uint32, round uint16, index ui
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()
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)
}
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 {
// Special case for LastCommits.
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()
defer ps.mtx.Unlock()
@ -615,10 +621,8 @@ func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) {
const (
msgTypeUnknown = byte(0x00)
// Messages for communicating state changes
msgTypeNewRoundStep = byte(0x01)
msgTypeCommit = byte(0x02)
// Messages of data
msgTypeCommitStep = byte(0x02)
msgTypeProposal = byte(0x11)
msgTypePart = byte(0x12) // both block & POL
msgTypeVote = byte(0x13)
@ -634,18 +638,18 @@ func decodeMessage(bz []byte) (msgType byte, msg interface{}) {
switch msgType {
// Messages for communicating state changes
case msgTypeNewRoundStep:
msg = readNewRoundStepMessage(r, n, err)
case msgTypeCommit:
msg = readCommitMessage(r, n, err)
msg = ReadBinary(&NewRoundStepMessage{}, r, n, err)
case msgTypeCommitStep:
msg = ReadBinary(&CommitStepMessage{}, r, n, err)
// Messages of data
case msgTypeProposal:
msg = ReadProposal(r, n, err)
msg = ReadBinary(&Proposal{}, r, n, err)
case msgTypePart:
msg = readPartMessage(r, n, err)
msg = ReadBinary(&PartMessage{}, r, n, err)
case msgTypeVote:
msg = ReadVote(r, n, err)
msg = ReadBinary(&VoteMessage{}, r, n, err)
case msgTypeHasVote:
msg = readHasVoteMessage(r, n, err)
msg = ReadBinary(&HasVoteMessage{}, r, n, err)
default:
msg = nil
}
@ -655,29 +659,13 @@ func decodeMessage(bz []byte) (msgType byte, msg interface{}) {
//-------------------------------------
type NewRoundStepMessage struct {
Height uint32
Round uint16
Height uint
Round uint
Step RoundStep
SecondsSinceStartTime uint32
SecondsSinceStartTime uint
}
func readNewRoundStepMessage(r io.Reader, n *int64, err *error) *NewRoundStepMessage {
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) TypeByte() byte { return msgTypeNewRoundStep }
func (m *NewRoundStepMessage) String() string {
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 {
Height uint32
type CommitStepMessage struct {
Height uint
BlockParts PartSetHeader
BlockBitArray BitArray
}
func readCommitMessage(r io.Reader, n *int64, err *error) *CommitMessage {
return &CommitMessage{
Height: ReadUInt32(r, n, err),
BlockParts: ReadPartSetHeader(r, n, err),
BlockBitArray: ReadBitArray(r, n, err),
}
}
func (m *CommitStepMessage) TypeByte() byte { return msgTypeCommitStep }
func (m *CommitMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, msgTypeCommit, &n, &err)
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)
func (m *CommitStepMessage) String() string {
return fmt.Sprintf("[CommitStep %v %v %v]", m.Height, m.BlockParts, m.BlockBitArray)
}
//-------------------------------------
@ -719,29 +693,13 @@ const (
)
type PartMessage struct {
Height uint32
Round uint16
Height uint
Round uint
Type byte
Part *Part
}
func readPartMessage(r io.Reader, n *int64, err *error) *PartMessage {
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) TypeByte() byte { return msgTypePart }
func (m *PartMessage) String() string {
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 {
Height uint32
Round uint16
Height uint
Round uint
Type byte
Index uint
}
func readHasVoteMessage(r io.Reader, n *int64, err *error) *HasVoteMessage {
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) TypeByte() byte { return msgTypeHasVote }
func (m *HasVoteMessage) String() string {
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
import (
"bytes"
"errors"
"fmt"
"math"
@ -59,6 +60,7 @@ import (
"sync/atomic"
"time"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common"
@ -82,7 +84,7 @@ const (
RoundActionPrevote = RoundActionType(0xA1) // Prevote and goto RoundStepPrevote
RoundActionPrecommit = RoundActionType(0xA2) // Precommit and goto RoundStepPrecommit
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.
roundDuration0 = 60 * time.Second // The first round is 60 seconds long.
@ -97,8 +99,8 @@ var (
)
type RoundAction struct {
Height uint32 // The block height for which consensus is reaching for.
Round uint16 // The round number at given height.
Height uint // The block height for which consensus is reaching for.
Round uint // The round number at given height.
Action RoundActionType // Action to perform.
}
@ -106,8 +108,8 @@ type RoundAction struct {
// Immutable when returned from ConsensusState.GetRoundState()
type RoundState struct {
Height uint32 // Height we are working on
Round uint16
Height uint // Height we are working on
Round uint
Step RoundStep
StartTime time.Time
CommitTime time.Time // Time when +2/3 commits were found
@ -347,7 +349,6 @@ ACTION_LOOP:
if rs.Precommits.HasTwoThirdsMajority() {
// Enter RoundStepCommit and commit.
cs.RunActionCommit(rs.Height)
cs.queueAction(RoundAction{rs.Height, rs.Round, RoundActionTryFinalize})
continue ACTION_LOOP
} else {
// Could not commit, move onto next round.
@ -363,7 +364,6 @@ ACTION_LOOP:
}
// Enter RoundStepCommit and commit.
cs.RunActionCommit(rs.Height)
cs.queueAction(RoundAction{rs.Height, rs.Round, RoundActionTryFinalize})
continue ACTION_LOOP
case RoundActionTryFinalize:
@ -435,7 +435,7 @@ func (cs *ConsensusState) updateToState(state *state.State) {
}
// After the call cs.Step becomes RoundStepNewRound.
func (cs *ConsensusState) setupNewRound(round uint16) {
func (cs *ConsensusState) setupNewRound(round uint) {
// Sanity check
if 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
func (cs *ConsensusState) SetupNewRound(height uint32, desiredRound uint16) bool {
func (cs *ConsensusState) SetupNewRound(height uint, desiredRound uint) bool {
cs.mtx.Lock()
defer cs.mtx.Unlock()
if cs.Height != height {
@ -485,7 +485,7 @@ func (cs *ConsensusState) SetupNewRound(height uint32, desiredRound uint16) bool
return true
}
func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
func (cs *ConsensusState) RunActionPropose(height uint, round uint) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
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.
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
}
@ -560,7 +560,7 @@ func (cs *ConsensusState) RunActionPropose(height uint32, round uint16) {
// Make proposal
proposal := NewProposal(cs.Height, cs.Round, blockParts.Header(), polParts.Header())
cs.PrivValidator.Sign(proposal)
proposal.Signature = cs.PrivValidator.SignProposal(proposal)
// Set fields
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.
// Otherwise vote nil.
func (cs *ConsensusState) RunActionPrevote(height uint32, round uint16) {
func (cs *ConsensusState) RunActionPrevote(height uint, round uint) {
cs.mtx.Lock()
defer cs.mtx.Unlock()
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,
// 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()
defer cs.mtx.Unlock()
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.
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()
defer cs.mtx.Unlock()
if cs.Height != height {
@ -679,6 +683,7 @@ func (cs *ConsensusState) RunActionCommit(height uint32) {
cs.newStepCh <- cs.getRoundState()
}()
// Sanity check.
// There are two ways to enter:
// 1. +2/3 precommits at the end of RoundStepPrecommit
// 2. +2/3 commits at any time
@ -712,15 +717,20 @@ func (cs *ConsensusState) RunActionCommit(height uint32) {
}
} else {
// 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
// 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()
defer cs.mtx.Unlock()
@ -754,6 +764,7 @@ func (cs *ConsensusState) TryFinalizeCommit(height uint32) bool {
return true
} else {
// Prevent zombies.
// TODO: Does this ever happen?
Panicf("+2/3 committed an invalid block: %v", err)
}
}
@ -782,7 +793,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
}
// Verify signature
if !cs.Validators.Proposer().Verify(proposal) {
if !cs.Validators.Proposer().PubKey.VerifyBytes(SignBytes(proposal), proposal.Signature) {
return ErrInvalidProposalSignature
}
@ -794,7 +805,7 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error {
// NOTE: block is not necessarily valid.
// 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()
defer cs.mtx.Unlock()
@ -815,8 +826,11 @@ func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part
if added && cs.ProposalBlockParts.IsComplete() {
var n int64
var err error
cs.ProposalBlock = ReadBlock(cs.ProposalBlockParts.GetReader(), &n, &err)
cs.ProposalBlock = ReadBinary(&Block{}, cs.ProposalBlockParts.GetReader(), &n, &err).(*Block)
// 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.
return true, err
}
@ -824,7 +838,7 @@ func (cs *ConsensusState) AddProposalBlockPart(height uint32, round uint16, part
}
// 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()
defer cs.mtx.Unlock()
@ -844,21 +858,21 @@ func (cs *ConsensusState) AddProposalPOLPart(height uint32, round uint16, part *
if added && cs.ProposalPOLParts.IsComplete() {
var n int64
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, 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()
defer cs.mtx.Unlock()
return cs.addVote(vote)
return cs.addVote(address, vote)
}
// 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)
if meta == 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 {
case VoteTypePrevote:
// Prevotes checks for height+round match.
return cs.Prevotes.Add(vote)
return cs.Prevotes.Add(address, vote)
case VoteTypePrecommit:
// Precommits checks for height+round match.
return cs.Precommits.Add(vote)
return cs.Precommits.Add(address, vote)
case VoteTypeCommit:
if vote.Height == cs.Height {
// No need to check if vote.Round < cs.Round ...
// Prevotes && Precommits already checks that.
cs.Prevotes.Add(vote)
cs.Precommits.Add(vote)
added, index, err = cs.Commits.Add(vote)
cs.Prevotes.Add(address, vote)
cs.Precommits.Add(address, vote)
added, index, err = cs.Commits.Add(address, vote)
if added && cs.Commits.HasTwoThirdsMajority() && cs.CommitTime.IsZero() {
cs.CommitTime = time.Now()
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
}
if vote.Height+1 == cs.Height {
return cs.LastCommits.Add(vote)
return cs.LastCommits.Add(address, vote)
}
return false, 0, nil
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 {
if cs.PrivValidator == nil || !cs.Validators.HasId(cs.PrivValidator.Id) {
if cs.PrivValidator == nil || !cs.Validators.HasAddress(cs.PrivValidator.Address) {
return nil
}
vote := &Vote{
@ -940,12 +954,12 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header PartSetHea
BlockHash: hash,
BlockParts: header,
}
cs.PrivValidator.Sign(vote)
cs.addVote(vote)
vote.Signature = cs.PrivValidator.SignVote(vote)
cs.addVote(cs.PrivValidator.Address, vote)
return vote
}
func (cs *ConsensusState) processBlockForCommit(block *Block, blockParts *PartSet) {
func (cs *ConsensusState) saveCommitVoteBlock(block *Block, blockParts *PartSet) {
// The proposal must be valid.
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
func calcRoundDuration(round uint16) time.Duration {
func calcRoundDuration(round uint) time.Duration {
return roundDuration0 + roundDurationDelta*time.Duration(round)
}
// 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) +
roundDurationDelta*(time.Duration((int64(round)*int64(round)-int64(round))/2)))
}
// calculates the current round given startTime of round zero.
// 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()
if now.Before(startTime) {
return 0
@ -997,18 +1011,18 @@ func calcRound(startTime time.Time) uint16 {
if math.IsNaN(R) {
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)
}
if R < 0 {
return 0
}
return uint16(R)
return uint(R)
}
// convenience
// 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) {
round = calcRound(startTime)
roundStartTime = calcRoundStartTime(round, startTime)

View File

@ -6,23 +6,27 @@ import (
// Common test methods
func makeValidator(id uint64, votingPower uint64) (*state.Validator, *state.PrivAccount) {
privAccount := state.GenPrivAccount()
privAccount.Id = id
func makeValidator(votingPower uint64) (*state.Validator, *PrivValidator) {
privValidator := GenPrivValidator()
return &state.Validator{
Account: privAccount.Account,
Address: privValidator.Address,
PubKey: privValidator.PubKey,
BondHeight: 0,
UnbondHeight: 0,
LastCommitHeight: 0,
VotingPower: votingPower,
}, privAccount
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)
privAccounts := make([]*state.PrivAccount, numValidators)
privValidators := make([]*PrivValidator, numValidators)
for i := 0; i < numValidators; i++ {
val, privAccount := makeValidator(uint64(i), votingPower)
val, privValidator := makeValidator(votingPower)
vals[i] = val
privAccounts[i] = privAccount
privValidators[i] = privValidator
}
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"
"sync"
. "github.com/tendermint/tendermint/account"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/common"
@ -16,17 +17,16 @@ import (
// for a predefined vote type.
// 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.
// 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 {
height uint32
round uint16
height uint
round uint
type_ byte
mtx sync.Mutex
vset *state.ValidatorSet
votes map[uint64]*Vote
votesBitArray BitArray
valSet *state.ValidatorSet
votes []*Vote // validator index -> vote
votesBitArray BitArray // validator index -> has vote?
votesByBlock map[string]uint64 // string(blockHash)+string(blockParts) -> vote sum.
totalVotes uint64
maj23Hash []byte
@ -35,7 +35,7 @@ type VoteSet struct {
}
// 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 {
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,
round: round,
type_: type_,
vset: vset,
votes: make(map[uint64]*Vote, vset.Size()),
votesBitArray: NewBitArray(vset.Size()),
valSet: valSet,
votes: make([]*Vote, valSet.Size()),
votesBitArray: NewBitArray(valSet.Size()),
votesByBlock: make(map[string]uint64),
totalVotes: 0,
}
}
func (vs *VoteSet) Size() uint {
if vs == nil {
func (voteSet *VoteSet) Size() uint {
if voteSet == nil {
return 0
} else {
return vs.vset.Size()
return voteSet.valSet.Size()
}
}
// True if added, false if not.
// Returns ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature]
// NOTE: vote should not be mutated after adding.
func (vs *VoteSet) Add(vote *Vote) (bool, uint, error) {
vs.mtx.Lock()
defer vs.mtx.Unlock()
func (voteSet *VoteSet) Add(address []byte, vote *Vote) (bool, uint, error) {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
// Make sure the step matches. (or that vote is commit && round < vs.round)
if vote.Height != vs.height ||
(vote.Type != VoteTypeCommit && vote.Round != vs.round) ||
(vote.Type != VoteTypeCommit && vote.Type != vs.type_) ||
(vote.Type == VoteTypeCommit && vs.type_ != VoteTypeCommit && vote.Round >= vs.round) {
// Make sure the step matches. (or that vote is commit && round < voteSet.round)
if vote.Height != voteSet.height ||
(vote.Type != VoteTypeCommit && vote.Round != voteSet.round) ||
(vote.Type != VoteTypeCommit && vote.Type != voteSet.type_) ||
(vote.Type == VoteTypeCommit && voteSet.type_ != VoteTypeCommit && vote.Round >= voteSet.round) {
return false, 0, ErrVoteUnexpectedStep
}
// Ensure that signer is a validator.
_, val := vs.vset.GetById(vote.SignerId)
valIndex, val := voteSet.valSet.GetByAddress(address)
if val == nil {
return false, 0, ErrVoteInvalidAccount
}
// Check signature.
if !val.Verify(vote) {
if !val.PubKey.VerifyBytes(SignBytes(vote), vote.Signature) {
// Bad signature.
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 existingVote, ok := vs.votes[vote.SignerId]; ok {
if existingVote := voteSet.votes[valIndex]; existingVote != nil {
if bytes.Equal(existingVote.BlockHash, vote.BlockHash) {
return false, 0, nil
} else {
@ -103,170 +103,154 @@ func (vs *VoteSet) addVote(vote *Vote) (bool, uint, error) {
}
// Add vote.
vs.votes[vote.SignerId] = vote
voterIndex, val := vs.vset.GetById(vote.SignerId)
_, val := voteSet.valSet.GetByIndex(valIndex)
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))
totalBlockHashVotes := vs.votesByBlock[blockKey] + val.VotingPower
vs.votesByBlock[blockKey] = totalBlockHashVotes
vs.totalVotes += val.VotingPower
totalBlockHashVotes := voteSet.votesByBlock[blockKey] + val.VotingPower
voteSet.votesByBlock[blockKey] = totalBlockHashVotes
voteSet.totalVotes += val.VotingPower
// If we just nudged it up to two thirds majority, add it.
if totalBlockHashVotes > vs.vset.TotalVotingPower()*2/3 &&
(totalBlockHashVotes-val.VotingPower) <= vs.vset.TotalVotingPower()*2/3 {
vs.maj23Hash = vote.BlockHash
vs.maj23Parts = vote.BlockParts
vs.maj23Exists = true
if totalBlockHashVotes > voteSet.valSet.TotalVotingPower()*2/3 &&
(totalBlockHashVotes-val.VotingPower) <= voteSet.valSet.TotalVotingPower()*2/3 {
voteSet.maj23Hash = vote.BlockHash
voteSet.maj23Parts = vote.BlockParts
voteSet.maj23Exists = true
}
return true, voterIndex, nil
return true, valIndex, nil
}
// Assumes that commits VoteSet is valid.
func (vs *VoteSet) AddFromCommits(commits *VoteSet) {
commitVotes := commits.AllVotes()
for _, commit := range commitVotes {
if commit.Round < vs.round {
vs.addVote(commit)
func (voteSet *VoteSet) AddFromCommits(commits *VoteSet) {
for valIndex, commit := range commits.votes {
if commit.Round < voteSet.round {
voteSet.addVote(uint(valIndex), commit)
}
}
}
func (vs *VoteSet) BitArray() BitArray {
if vs == nil {
func (voteSet *VoteSet) BitArray() BitArray {
if voteSet == nil {
return BitArray{}
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
return vs.votesBitArray.Copy()
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.votesBitArray.Copy()
}
func (vs *VoteSet) GetByIndex(index uint) *Vote {
vs.mtx.Lock()
defer vs.mtx.Unlock()
func (voteSet *VoteSet) GetByIndex(valIndex uint) *Vote {
voteSet.mtx.Lock()
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 {
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 {
vs.mtx.Lock()
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 {
func (voteSet *VoteSet) HasTwoThirdsMajority() bool {
if voteSet == nil {
return false
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
return vs.maj23Exists
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
return voteSet.maj23Exists
}
// Returns either a blockhash (or nil) that received +2/3 majority.
// If there exists no such majority, returns (nil, false).
func (vs *VoteSet) TwoThirdsMajority() (hash []byte, parts PartSetHeader, ok bool) {
vs.mtx.Lock()
defer vs.mtx.Unlock()
if vs.maj23Exists {
return vs.maj23Hash, vs.maj23Parts, true
func (voteSet *VoteSet) TwoThirdsMajority() (hash []byte, parts PartSetHeader, ok bool) {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if voteSet.maj23Exists {
return voteSet.maj23Hash, voteSet.maj23Parts, true
} else {
return nil, PartSetHeader{}, false
}
}
func (vs *VoteSet) MakePOL() *POL {
if vs.type_ != VoteTypePrevote {
func (voteSet *VoteSet) MakePOL() *POL {
if voteSet.type_ != VoteTypePrevote {
panic("Cannot MakePOL() unless VoteSet.Type is VoteTypePrevote")
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
if !vs.maj23Exists {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if !voteSet.maj23Exists {
return nil
}
pol := &POL{
Height: vs.height,
Round: vs.round,
BlockHash: vs.maj23Hash,
BlockParts: vs.maj23Parts,
Height: voteSet.height,
Round: voteSet.round,
BlockHash: voteSet.maj23Hash,
BlockParts: voteSet.maj23Parts,
Votes: make([]POLVoteSignature, voteSet.valSet.Size()),
}
for _, vote := range vs.votes {
if !bytes.Equal(vote.BlockHash, vs.maj23Hash) {
for valIndex, vote := range voteSet.votes {
if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) {
continue
}
if !vote.BlockParts.Equals(vs.maj23Parts) {
if !vote.BlockParts.Equals(voteSet.maj23Parts) {
continue
}
if vote.Type == VoteTypePrevote {
pol.Votes = append(pol.Votes, vote.Signature)
} else if vote.Type == VoteTypeCommit {
pol.Commits = append(pol.Commits, RoundSignature{vote.Round, vote.Signature})
} else {
Panicf("Unexpected vote type %X", vote.Type)
pol.Votes[valIndex] = POLVoteSignature{
Round: vote.Round,
Signature: vote.Signature,
}
}
return pol
}
func (vs *VoteSet) MakeValidation() *Validation {
if vs.type_ != VoteTypeCommit {
func (voteSet *VoteSet) MakeValidation() *Validation {
if voteSet.type_ != VoteTypeCommit {
panic("Cannot MakeValidation() unless VoteSet.Type is VoteTypeCommit")
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
if len(vs.maj23Hash) == 0 {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if len(voteSet.maj23Hash) == 0 {
panic("Cannot MakeValidation() unless a blockhash has +2/3")
}
rsigs := make([]RoundSignature, vs.vset.Size())
vs.vset.Iterate(func(index uint, val *state.Validator) bool {
vote := vs.votes[val.Id]
commits := make([]Commit, voteSet.valSet.Size())
voteSet.valSet.Iterate(func(valIndex uint, val *state.Validator) bool {
vote := voteSet.votes[valIndex]
if vote == nil {
return false
}
if !bytes.Equal(vote.BlockHash, vs.maj23Hash) {
if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) {
return false
}
if !vote.BlockParts.Equals(vs.maj23Parts) {
if !vote.BlockParts.Equals(voteSet.maj23Parts) {
return false
}
rsigs[index] = RoundSignature{vote.Round, vote.Signature}
commits[valIndex] = Commit{vote.Round, vote.Signature}
return false
})
return &Validation{
Commits: rsigs,
Commits: commits,
}
}
func (vs *VoteSet) String() string {
return vs.StringWithIndent("")
func (voteSet *VoteSet) String() string {
return voteSet.StringWithIndent("")
}
func (vs *VoteSet) StringWithIndent(indent string) string {
vs.mtx.Lock()
defer vs.mtx.Unlock()
func (voteSet *VoteSet) StringWithIndent(indent string) string {
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
voteStrings := make([]string, len(vs.votes))
voteStrings := make([]string, len(voteSet.votes))
counter := 0
for _, vote := range vs.votes {
for _, vote := range voteSet.votes {
voteStrings[counter] = vote.String()
counter++
}
@ -275,18 +259,18 @@ func (vs *VoteSet) StringWithIndent(indent string) string {
%s %v
%s %v
%s}`,
indent, vs.height, vs.round, vs.type_,
indent, voteSet.height, voteSet.round, voteSet.type_,
indent, strings.Join(voteStrings, "\n"+indent+" "),
indent, vs.votesBitArray,
indent, voteSet.votesBitArray,
indent)
}
func (vs *VoteSet) Description() string {
if vs == nil {
func (voteSet *VoteSet) Description() string {
if voteSet == nil {
return "nil-VoteSet"
}
vs.mtx.Lock()
defer vs.mtx.Unlock()
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
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 {
Get([]byte) []byte
Set([]byte, []byte)
SetSync([]byte, []byte)
Delete([]byte)
DeleteSync([]byte)
}

View File

@ -3,6 +3,7 @@ package db
import (
"fmt"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt"
"path"
)
@ -20,13 +21,6 @@ func NewLevelDB(name string) (*LevelDB, error) {
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 {
res, err := db.db.Get(key, nil)
if err != nil {
@ -35,6 +29,20 @@ func (db *LevelDB) Get(key []byte) []byte {
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) {
err := db.db.Delete(key, 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 {
return db.db
}

View File

@ -13,18 +13,26 @@ func NewMemDB() *MemDB {
return database
}
func (db *MemDB) Set(key []byte, value []byte) {
db.db[string(key)] = value
}
func (db *MemDB) Get(key []byte) []byte {
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) {
delete(db.db, string(key))
}
func (db *MemDB) DeleteSync(key []byte) {
delete(db.db, string(key))
}
func (db *MemDB) Print() {
for key, value := range db.db {
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.
blockTxsMap := make(map[string]struct{})
for _, tx := range block.Data.Txs {
txHash := BinaryHash(tx)
txHash := BinarySha256(tx)
blockTxsMap[string(txHash)] = struct{}{}
}
// Next, filter all txs from mem.txs that are in blockTxsMap
txs := []Tx{}
for _, tx := range mem.txs {
txHash := BinaryHash(tx)
txHash := BinarySha256(tx)
if _, ok := blockTxsMap[string(txHash)]; ok {
continue
} else {

View File

@ -3,7 +3,6 @@ package mempool
import (
"bytes"
"fmt"
"io"
"sync/atomic"
. "github.com/tendermint/tendermint/binary"
@ -119,12 +118,10 @@ const (
// TODO: check for unnecessary extra bytes at the end.
func decodeMessage(bz []byte) (msgType byte, msg interface{}) {
n, err := new(int64), new(error)
// log.Debug("decoding msg bytes: %X", bz)
msgType = bz[0]
switch msgType {
case msgTypeTx:
msg = readTxMessage(bytes.NewReader(bz[1:]), n, err)
// case ...:
msg = ReadBinary(&TxMessage{}, bytes.NewReader(bz[1:]), n, err)
default:
msg = nil
}
@ -137,17 +134,7 @@ type TxMessage struct {
Tx Tx
}
func readTxMessage(r io.Reader, n *int64, err *error) *TxMessage {
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) TypeByte() byte { return msgTypeTx }
func (m *TxMessage) String() string {
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
func (node *IAVLNode) writeToCountHashes(t *IAVLTree, w io.Writer) (n int64, hashCount uint64, err error) {
// height & size & key
WriteUInt8(w, node.height, &n, &err)
WriteUInt64(w, node.size, &n, &err)
WriteUInt8(node.height, w, &n, &err)
WriteUInt64(node.size, w, &n, &err)
t.keyCodec.Encode(node.key, w, &n, &err)
if err != nil {
return
@ -273,7 +273,7 @@ func (node *IAVLNode) writeToCountHashes(t *IAVLTree, w io.Writer) (n int64, has
if node.leftHash == nil {
panic("node.leftHash was nil in save")
}
WriteByteSlice(w, node.leftHash, &n, &err)
WriteByteSlice(node.leftHash, w, &n, &err)
// right
if node.rightNode != nil {
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 {
panic("node.rightHash was nil in save")
}
WriteByteSlice(w, node.rightHash, &n, &err)
WriteByteSlice(node.rightHash, w, &n, &err)
}
return
}

View File

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

View File

@ -17,7 +17,7 @@ import (
)
const (
numBatchPackets = 10
numBatchMsgPackets = 10
minReadBufferSize = 1024
minWriteBufferSize = 1024
flushThrottleMS = 50
@ -35,7 +35,7 @@ type errorCbFunc func(interface{})
/*
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.
*/
type MConnection struct {
@ -158,7 +158,7 @@ func (c *MConnection) stopForError(r interface{}) {
}
// 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 {
return false
}
@ -185,7 +185,7 @@ func (c *MConnection) Send(chId byte, msg Binary) bool {
// Queues a message to be sent to channel.
// 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 {
return false
}
@ -242,18 +242,18 @@ FOR_LOOP:
channel.updateStats()
}
case <-c.pingTimer.Ch:
WriteByte(c.bufWriter, packetTypePing, &n, &err)
WriteByte(packetTypePing, c.bufWriter, &n, &err)
c.sendMonitor.Update(int(n))
c.flush()
case <-c.pong:
WriteByte(c.bufWriter, packetTypePong, &n, &err)
WriteByte(packetTypePing, c.bufWriter, &n, &err)
c.sendMonitor.Update(int(n))
c.flush()
case <-c.quit:
break FOR_LOOP
case <-c.send:
// Send some packets
eof := c.sendSomePackets()
// Send some msgPackets
eof := c.sendSomeMsgPackets()
if !eof {
// Keep sendRoutine awake.
select {
@ -278,15 +278,15 @@ FOR_LOOP:
// Returns true if messages from channels were exhausted.
// Blocks in accordance to .sendMonitor throttling.
func (c *MConnection) sendSomePackets() bool {
func (c *MConnection) sendSomeMsgPackets() bool {
// Block until .sendMonitor says we can write.
// Once we're ready we send more than we asked for,
// 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.
for i := 0; i < numBatchPackets; i++ {
if c.sendPacket() {
// Now send some msgPackets.
for i := 0; i < numBatchMsgPackets; i++ {
if c.sendMsgPacket() {
return true
}
}
@ -294,8 +294,8 @@ func (c *MConnection) sendSomePackets() bool {
}
// Returns true if messages from channels were exhausted.
func (c *MConnection) sendPacket() bool {
// Choose a channel to create a packet from.
func (c *MConnection) sendMsgPacket() bool {
// Choose a channel to create a msgPacket from.
// The chosen channel will be the one whose recentlySent/priority is the least.
var leastRatio float32 = math.MaxFloat32
var leastChannel *Channel
@ -316,13 +316,13 @@ func (c *MConnection) sendPacket() bool {
if leastChannel == nil {
return true
} else {
// log.Debug("Found a packet to send")
// log.Debug("Found a msgPacket to send")
}
// Make & send a packet from this channel
n, err := leastChannel.writePacketTo(c.bufWriter)
// Make & send a msgPacket from this channel
n, err := leastChannel.writeMsgPacketTo(c.bufWriter)
if err != nil {
log.Warning("Failed to write packet. Error: %v", err)
log.Warning("Failed to write msgPacket. Error: %v", err)
c.stopForError(err)
return true
}
@ -331,7 +331,7 @@ func (c *MConnection) sendPacket() bool {
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().
// Blocks depending on how the connection is throttled.
func (c *MConnection) recvRoutine() {
@ -340,7 +340,7 @@ func (c *MConnection) recvRoutine() {
FOR_LOOP:
for {
// 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
var n int64
@ -359,7 +359,7 @@ FOR_LOOP:
if log.IsEnabledFor(logging.DEBUG) {
numBytes := c.bufReader.Buffered()
bytes, err := c.bufReader.Peek(MinInt(numBytes, 100))
if err != nil {
if err == nil {
log.Debug("recvRoutine packet type %X, peeked: %X", pktType, bytes)
}
}
@ -371,10 +371,11 @@ FOR_LOOP:
c.pong <- struct{}{}
case packetTypePong:
// do nothing
case packetTypeMessage:
pkt, n, err := readPacketSafe(c.bufReader)
c.recvMonitor.Update(int(n))
if err != nil {
case packetTypeMsg:
pkt, n, err := msgPacket{}, new(int64), new(error)
ReadBinary(&pkt, c.bufReader, n, err)
c.recvMonitor.Update(int(*n))
if *err != nil {
if atomic.LoadUint32(&c.stopped) != 1 {
log.Info("%v failed @ recvRoutine", c)
c.Stop()
@ -385,7 +386,7 @@ FOR_LOOP:
if !ok || channel == nil {
Panicf("Unknown channel %X", pkt.ChannelId)
}
msgBytes := channel.recvPacket(pkt)
msgBytes := channel.recvMsgPacket(pkt)
if msgBytes != nil {
c.onReceive(pkt.ChannelId, msgBytes)
}
@ -394,7 +395,7 @@ FOR_LOOP:
}
// 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()
}
@ -471,8 +472,8 @@ func (ch *Channel) canSend() bool {
return ch.loadSendQueueSize() < defaultSendQueueCapacity
}
// Returns true if any packets are pending to be sent.
// Call before calling nextPacket()
// Returns true if any msgPackets are pending to be sent.
// Call before calling nextMsgPacket()
// Goroutine-safe
func (ch *Channel) isSendPending() bool {
if len(ch.sending) == 0 {
@ -484,38 +485,37 @@ func (ch *Channel) isSendPending() bool {
return true
}
// Creates a new packet to send.
// Creates a new msgPacket to send.
// Not goroutine-safe
func (ch *Channel) nextPacket() packet {
packet := packet{}
func (ch *Channel) nextMsgPacket() msgPacket {
packet := msgPacket{}
packet.ChannelId = byte(ch.id)
packet.Bytes = ch.sending[:MinInt(maxPacketSize, len(ch.sending))]
if len(ch.sending) <= maxPacketSize {
packet.Bytes = ch.sending[:MinInt(maxMsgPacketSize, len(ch.sending))]
if len(ch.sending) <= maxMsgPacketSize {
packet.EOF = byte(0x01)
ch.sending = nil
atomic.AddUint32(&ch.sendQueueSize, ^uint32(0)) // decrement sendQueueSize
} else {
packet.EOF = byte(0x00)
ch.sending = ch.sending[MinInt(maxPacketSize, len(ch.sending)):]
ch.sending = ch.sending[MinInt(maxMsgPacketSize, len(ch.sending)):]
}
return packet
}
// Writes next packet to w.
// Writes next msgPacket to w.
// Not goroutine-safe
func (ch *Channel) writePacketTo(w io.Writer) (n int64, err error) {
packet := ch.nextPacket()
WriteByte(w, packetTypeMessage, &n, &err)
WriteBinary(w, packet, &n, &err)
func (ch *Channel) writeMsgPacketTo(w io.Writer) (n int64, err error) {
packet := ch.nextMsgPacket()
WriteBinary(packet, w, &n, &err)
if err != nil {
ch.recentlySent += n
}
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
func (ch *Channel) recvPacket(pkt packet) []byte {
func (ch *Channel) recvMsgPacket(pkt msgPacket) []byte {
ch.recving = append(ch.recving, pkt.Bytes...)
if pkt.EOF == byte(0x01) {
msgBytes := ch.recving
@ -536,36 +536,23 @@ func (ch *Channel) updateStats() {
//-----------------------------------------------------------------------------
const (
maxPacketSize = 1024
maxMsgPacketSize = 1024
packetTypePing = byte(0x00)
packetTypePong = byte(0x01)
packetTypeMessage = byte(0x10)
packetTypeMsg = byte(0x10)
)
// Messages in channels are chopped into smaller packets for multiplexing.
type packet struct {
// Messages in channels are chopped into smaller msgPackets for multiplexing.
type msgPacket struct {
ChannelId byte
EOF byte // 1 means message ends here.
Bytes []byte
}
func (p packet) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, p.ChannelId, &n, &err)
WriteByte(w, p.EOF, &n, &err)
WriteByteSlice(w, p.Bytes, &n, &err)
return
}
func (p msgPacket) TypeByte() byte { return packetTypeMsg }
func (p packet) String() string {
return fmt.Sprintf("Packet{%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
func (p msgPacket) String() string {
return fmt.Sprintf("MsgPacket{%X:%X}", p.ChannelId, p.Bytes)
}
//-----------------------------------------------------------------------------
@ -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.
type TypedMessage struct {
Type byte
Msg Binary
}
func (tm TypedMessage) WriteTo(w io.Writer) (n int64, err error) {
WriteByte(w, tm.Type, &n, &err)
WriteBinary(w, tm.Msg, &n, &err)
return
Msg interface{}
}
func (tm TypedMessage) String() string {
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() {
logging.SetFormatter(logging.MustStringFormatter("[%{level:.1s}] %{message}"))
logging.SetLevel(logging.WARNING, "p2p")
logging.SetLevel(logging.DEBUG, "p2p")
}
func SetP2PLogger(l *logging.Logger) {

View File

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

View File

@ -72,14 +72,14 @@ func (p *Peer) IsOutbound() bool {
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 {
return false
}
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 {
return false
}
@ -94,7 +94,7 @@ func (p *Peer) CanSend(chId byte) bool {
}
func (p *Peer) WriteTo(w io.Writer) (n int64, err error) {
WriteString(w, p.Key, &n, &err)
WriteString(p.Key, w, &n, &err)
return
}

View File

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

View File

@ -2,11 +2,11 @@ package p2p
import (
"errors"
"fmt"
"net"
"sync/atomic"
"time"
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/common"
)
@ -67,7 +67,7 @@ func NewSwitch(reactors []Reactor) *Switch {
for _, chDesc := range reactorChannels {
chId := chDesc.Id
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)
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.
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 {
return
}

View File

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

View File

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

View File

@ -1,177 +1,47 @@
package state
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"github.com/tendermint/go-ed25519"
. "github.com/tendermint/tendermint/account"
. "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 {
Id uint64 // Numeric id of account, incrementing.
PubKey []byte
}
func ReadAccount(r io.Reader, n *int64, err *error) Account {
return Account{
Id: ReadUInt64(r, n, err),
PubKey: ReadByteSlice(r, n, err),
}
}
func (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
Address []byte
PubKey PubKey
Sequence uint
Balance uint64
Status byte
}
func ReadAccountDetail(r io.Reader, n *int64, err *error) *AccountDetail {
return &AccountDetail{
Account: ReadAccount(r, n, err),
Sequence: ReadUVarInt(r, n, err),
Balance: ReadUInt64(r, n, err),
Status: ReadByte(r, n, err),
}
}
func (accDet *AccountDetail) WriteTo(w io.Writer) (n int64, err error) {
WriteBinary(w, accDet.Account, &n, &err)
WriteUVarInt(w, accDet.Sequence, &n, &err)
WriteUInt64(w, accDet.Balance, &n, &err)
WriteByte(w, accDet.Status, &n, &err)
return
}
func (accDet *AccountDetail) String() string {
return fmt.Sprintf("AccountDetail{%v:%X Sequence:%v Balance:%v Status:%X}",
accDet.Id, accDet.PubKey, accDet.Sequence, accDet.Balance, accDet.Status)
}
func (accDet *AccountDetail) Copy() *AccountDetail {
accDetCopy := *accDet
return &accDetCopy
}
//-------------------------------------
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{} {
return ReadAccountDetail(r, n, err)
}
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),
func NewAccount(address []byte, pubKey PubKey) *Account {
return &Account{
Address: address,
PubKey: pubKey,
},
PrivKey: privKey,
Sequence: uint(0),
Balance: uint64(0),
}
}
// 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
func (account *Account) Copy() *Account {
accountCopy := *account
return &accountCopy
}
// 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 (account *Account) String() string {
return fmt.Sprintf("Account{%X:%v}", account.Address, account.PubKey)
}
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 AccountEncoder(o interface{}, w io.Writer, n *int64, err *error) {
WriteBinary(o.(*Account), w, n, err)
}
func (pa *PrivAccount) Sign(o Signable) {
if !o.GetSignature().IsZero() {
panic("Cannot sign: already signed")
func AccountDecoder(r io.Reader, n *int64, err *error) interface{} {
return ReadBinary(&Account{}, r, n, err)
}
msg := BinaryBytes(o)
sig := pa.SignBytes(msg)
o.SetSignature(sig)
var AccountCodec = Codec{
Encode: AccountEncoder,
Decode: AccountDecoder,
}

View File

@ -14,7 +14,8 @@ import (
type GenesisDoc struct {
GenesisTime time.Time
AccountDetails []*AccountDetail
Accounts []*Account
Validators []*Validator
}
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)
}
genDoc := GenesisDocFromJSON(jsonBlob)
return GenesisStateFromDoc(db, genDoc)
return GenesisState(db, genDoc)
}
func GenesisStateFromDoc(db db_.DB, genDoc *GenesisDoc) *State {
return GenesisState(db, genDoc.GenesisTime, genDoc.AccountDetails)
}
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 {
func GenesisState(db db_.DB, genDoc *GenesisDoc) *State {
if len(genDoc.Validators) == 0 {
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{
DB: db,
LastBlockHeight: 0,
LastBlockHash: nil,
LastBlockParts: PartSetHeader{},
LastBlockTime: genesisTime,
BondedValidators: NewValidatorSet(validators),
LastBlockTime: genDoc.GenesisTime,
BondedValidators: NewValidatorSet(genDoc.Validators),
UnbondingValidators: NewValidatorSet(nil),
accountDetails: accountDetails,
accounts: accounts,
}
}

View File

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

View File

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

View File

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