mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-13 21:31:23 +00:00
Refactor Tx, Validator, and Account structure
This commit is contained in:
19
account/account.go
Normal file
19
account/account.go
Normal 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
62
account/privkey.go
Normal 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
87
account/pubkey.go
Normal 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
52
account/signature.go
Normal 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
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package state
|
||||
package account
|
||||
|
||||
import (
|
||||
. "github.com/tendermint/tendermint/common"
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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
198
binary/reflect.go
Normal 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()))
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
113
blocks/block.go
113
blocks/block.go
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -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]
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
220
blocks/tx.go
220
blocks/tx.go
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
23
cmd/gen_validator.go
Normal 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)
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
110
consensus/pol.go
110
consensus/pol.go
@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
3
db/db.go
3
db/db.go
@ -3,4 +3,7 @@ package db
|
||||
type DB interface {
|
||||
Get([]byte) []byte
|
||||
Set([]byte, []byte)
|
||||
SetSync([]byte, []byte)
|
||||
Delete([]byte)
|
||||
DeleteSync([]byte)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
16
db/mem_db.go
16
db/mem_db.go
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
172
state/account.go
172
state/account.go
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
499
state/state.go
499
state/state.go
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
28
wallet/priv_account.go
Normal 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,
|
||||
},
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user