diff --git a/DOCKER/Dockerfile b/DOCKER/Dockerfile index ab1e16315bff452e7791217c19effe0fab4b1c5f..99ddc8916839b375b2eac87b52d542f5b23171cd 100644 --- a/DOCKER/Dockerfile +++ b/DOCKER/Dockerfile @@ -2,6 +2,9 @@ FROM eris/base MAINTAINER Eris Industries <support@erisindustries.com> +#----------------------------------------------------------------------------- +# dependencies + # Set the env variables to non-interactive ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_PRIORITY critical @@ -15,9 +18,15 @@ RUN apt-get update && \ libgmp3-dev && \ rm -rf /var/lib/apt/lists/* +#----------------------------------------------------------------------------- +# start script + # install the wrapper/start script COPY DOCKER/start.sh /usr/local/bin/erisdb-wrapper +#----------------------------------------------------------------------------- +# install tendermint + # set the repo and install tendermint ENV REPO github.com/tendermint/tendermint ENV BRANCH f2f437a328cb3e277bc4ac74e4a1180f1c28efbc @@ -29,6 +38,9 @@ RUN git clone --quiet https://$REPO . && \ git checkout --quiet $BRANCH && \ go build -o /usr/local/bin/tendermint ./cmd/tendermint +#----------------------------------------------------------------------------- +# install erisdb + # set the repo and install erisdb ENV REPO $GOPATH/src/github.com/eris-ltd/eris-db COPY . $REPO @@ -36,6 +48,9 @@ WORKDIR $REPO RUN go build -o /usr/local/bin/erisdb ./cmd/erisdb && \ go build -o /usr/local/bin/erisdbss ./cmd/erisdbss +#----------------------------------------------------------------------------- +# install mint-client tools + # set the repo and install mint-client ENV REPO github.com/eris-ltd/mint-client ENV BRANCH master @@ -47,17 +62,24 @@ RUN git clone --quiet https://$REPO . && \ mv $GOPATH/bin/mint* /usr/local/bin && \ mv ./mint-client /usr/local/bin/ +#----------------------------------------------------------------------------- # cleanup + RUN rm -rf $GOPATH/src/* && \ unset REPO && \ unset BRANCH -# install chain manager scripts and default mint config +#----------------------------------------------------------------------------- +# chain manager scripts and default mint config + ENV ECM_PATH /usr/local/lib/ecm/ RUN mkdir -p $ECM_PATH COPY ./DOCKER/chain_* $ECM_PATH COPY ./DOCKER/config.toml $ECM_PATH +#----------------------------------------------------------------------------- +# root dir + # persist data, set user VOLUME /home/$USER/.eris WORKDIR /home/$USER/.eris diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 30e3d8c3fb9a9875a8864bb99e22e3c2d68323ae..e4021c24a9f3c7c3eadbcb23e3c9608bb61d348e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -85,87 +85,87 @@ }, { "ImportPath": "github.com/tendermint/tendermint/account", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/alert", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/binary", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/blockchain", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/common", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/config", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/consensus", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/db", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/events", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/logger", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/mempool", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/merkle", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/node", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/p2p", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/permission/types", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/rpc/core", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/rpc/server", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/rpc/types", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/state", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/types", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tendermint/tendermint/vm", - "Rev": "1d61dbc86b4b9e084b28b6b52412ab0a3ceab2e6" + "Rev": "d2355ce9569420b866c9dbd1de133cf088438c86" }, { "ImportPath": "github.com/tommy351/gin-cors", diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/account/account.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/account/account.go index 99c8cba5be13fbdf665b7801d4d32180fd6efe3f..85a19ea4f41e5b7151c151ef947ef49f38b7f96c 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/account/account.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/account/account.go @@ -21,6 +21,7 @@ func SignBytes(chainID string, o Signable) []byte { buf, n, err := new(bytes.Buffer), new(int64), new(error) o.WriteSignBytes(chainID, buf, n, err) if *err != nil { + // SOMETHING HAS GONE HORRIBLY WRONG panic(err) } return buf.Bytes() @@ -28,7 +29,7 @@ func SignBytes(chainID string, o Signable) []byte { // HashSignBytes is a convenience method for getting the hash of the bytes of a signable func HashSignBytes(chainID string, o Signable) []byte { - return merkle.HashFromBinary(SignBytes(chainID, o)) + return merkle.SimpleHashFromBinary(SignBytes(chainID, o)) } //----------------------------------------------------------------------------- @@ -39,12 +40,12 @@ func HashSignBytes(chainID string, o Signable) []byte { type Account struct { Address []byte `json:"address"` PubKey PubKey `json:"pub_key"` - Sequence uint `json:"sequence"` - Balance uint64 `json:"balance"` + Sequence int `json:"sequence"` + Balance int64 `json:"balance"` Code []byte `json:"code"` // VM code StorageRoot []byte `json:"storage_root"` // VM storage merkle root. - Permissions *ptypes.AccountPermissions `json:"permissions"` + Permissions ptypes.AccountPermissions `json:"permissions"` } func (acc *Account) Copy() *Account { @@ -54,7 +55,7 @@ func (acc *Account) Copy() *Account { func (acc *Account) String() string { // return fmt.Sprintf("Account{%X:%v C:%v S:%X}", acc.Address, acc.PubKey, len(acc.Code), acc.StorageRoot) - return fmt.Sprintf("Account{%X:%v C:%v S:%X P:(%s)}", acc.Address, acc.PubKey, len(acc.Code), acc.StorageRoot, acc.Permissions) + return fmt.Sprintf("Account{%X:%v C:%v S:%X P:%s}", acc.Address, acc.PubKey, len(acc.Code), acc.StorageRoot, acc.Permissions) } func AccountEncoder(o interface{}, w io.Writer, n *int64, err *error) { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/account/priv_account.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/account/priv_account.go index 6ec8db4e7c6c7fa543adf24b44e68788ac7859cc..35a9e1dee7340c740896603265d4f295fe2cdfea 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/account/priv_account.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/account/priv_account.go @@ -28,7 +28,7 @@ func GenPrivAccount() *PrivAccount { // Generates a new account with private key from SHA256 hash of a secret func GenPrivAccountFromSecret(secret []byte) *PrivAccount { - privKey32 := binary.BinarySha256(secret) + privKey32 := binary.BinarySha256(secret) // Not Ripemd160 because we want 32 bytes. privKeyBytes := new([64]byte) copy(privKeyBytes[:32], privKey32) pubKeyBytes := ed25519.MakePublicKey(privKeyBytes) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/account/pub_key.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/account/pub_key.go index 7f0686e2f115c7af84aaf778e09ac93d5097e0a1..0f4bf36cf95b46724f17df2147b2782a84424f01 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/account/pub_key.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/account/pub_key.go @@ -35,10 +35,11 @@ func (pubKey PubKeyEd25519) IsNil() bool { return false } // TODO: Or should this just be BinaryRipemd160(key)? (The difference is the TypeByte.) func (pubKey PubKeyEd25519) Address() []byte { return binary.BinaryRipemd160(pubKey) } +// TODO: Consider returning a reason for failure, or logging a runtime type mismatch. func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool { sig, ok := sig_.(SignatureEd25519) if !ok { - panic("PubKeyEd25519 expects an SignatureEd25519 signature") + return false } pubKeyBytes := new([32]byte) copy(pubKeyBytes[:], pubKey) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/README.md b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/README.md index c490d0e577f07fc6fd2e8e97c20bc6f9a029937c..9a4fa714f676924dfed17556d4857712492b6b80 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/README.md +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/README.md @@ -78,7 +78,7 @@ WriteBinary(foo, buf, n, err) foo2 := ReadBinary(Foo{}, buf, n, err).(Foo) // Or, to decode onto a pointer: -foo2 := ReadBinary(&Foo{}, buf, n, err).(*Foo) +foo2 := ReadBinaryPtr(&Foo{}, buf, n, err).(*Foo) ``` WriteBinary and ReadBinary can encode/decode structs recursively. However, interface field diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/binary.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/binary.go index 890ac76aadfdd42c013a510bf0dc4558fa1a7acd..962eef98cbf822d3b310f32c767a184c0cdad5e0 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/binary.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/binary.go @@ -2,14 +2,21 @@ package binary import ( "encoding/json" + "errors" "io" "reflect" ) +// TODO document and maybe make it configurable. +const MaxBinaryReadSize = 21 * 1024 * 1024 + +var ErrBinaryReadSizeOverflow = errors.New("Error: binary read size overflow") +var ErrBinaryReadSizeUnderflow = errors.New("Error: binary read size underflow") + 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 { - readReflectBinary(rv.Elem(), rt.Elem(), Options{}, r, n, err) + readReflectBinary(rv, rt, Options{}, r, n, err) return o } else { ptrRv := reflect.New(rt) @@ -18,12 +25,19 @@ func ReadBinary(o interface{}, r io.Reader, n *int64, err *error) interface{} { } } +func ReadBinaryPtr(o interface{}, r io.Reader, n *int64, err *error) interface{} { + rv, rt := reflect.ValueOf(o), reflect.TypeOf(o) + if rv.Kind() == reflect.Ptr { + readReflectBinary(rv.Elem(), rt.Elem(), Options{}, r, n, err) + return o + } else { + panic("ReadBinaryPtr expects o to be a pointer") + } +} + func WriteBinary(o interface{}, w io.Writer, n *int64, err *error) { rv := reflect.ValueOf(o) rt := reflect.TypeOf(o) - if rv.Kind() == reflect.Ptr { - rv, rt = rv.Elem(), rt.Elem() - } writeReflectBinary(rv, rt, Options{}, w, n, err) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/byteslice.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/byteslice.go index 052b786ea6503130bdb8fc8b827d2b5918b24c7f..e797eb63531779ea26b7f49a6d97ac432ef189a3 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/byteslice.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/byteslice.go @@ -1,42 +1,39 @@ package binary import ( - . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" "io" -) -const ( - ByteSliceChunk = 1024 + . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" ) func WriteByteSlice(bz []byte, w io.Writer, n *int64, err *error) { - WriteUvarint(uint(len(bz)), w, n, err) + WriteVarint(len(bz), w, n, err) WriteTo(bz, w, n, err) } func ReadByteSlice(r io.Reader, n *int64, err *error) []byte { - length := int(ReadUvarint(r, n, err)) + length := ReadVarint(r, n, err) if *err != nil { return nil } - - var buf, tmpBuf []byte - // read one ByteSliceChunk at a time and append - for i := 0; i*ByteSliceChunk < length; i++ { - tmpBuf = make([]byte, MinInt(ByteSliceChunk, length-i*ByteSliceChunk)) - ReadFull(tmpBuf, r, n, err) - if *err != nil { - return nil - } - buf = append(buf, tmpBuf...) + if length < 0 { + *err = ErrBinaryReadSizeUnderflow + return nil } + if MaxBinaryReadSize < MaxInt64(int64(length), *n+int64(length)) { + *err = ErrBinaryReadSizeOverflow + return nil + } + + buf := make([]byte, length) + ReadFull(buf, r, n, err) return buf } //----------------------------------------------------------------------------- func WriteByteSlices(bzz [][]byte, w io.Writer, n *int64, err *error) { - WriteUvarint(uint(len(bzz)), w, n, err) + WriteVarint(len(bzz), w, n, err) for _, bz := range bzz { WriteByteSlice(bz, w, n, err) if *err != nil { @@ -46,10 +43,19 @@ func WriteByteSlices(bzz [][]byte, w io.Writer, n *int64, err *error) { } func ReadByteSlices(r io.Reader, n *int64, err *error) [][]byte { - length := int(ReadUvarint(r, n, err)) + length := ReadVarint(r, n, err) if *err != nil { return nil } + if length < 0 { + *err = ErrBinaryReadSizeUnderflow + return nil + } + if MaxBinaryReadSize < MaxInt64(int64(length), *n+int64(length)) { + *err = ErrBinaryReadSizeOverflow + return nil + } + bzz := make([][]byte, length) for i := 0; i < length; i++ { bz := ReadByteSlice(r, n, err) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/codec.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/codec.go index 85c9a0cfc40cc85a5a29ff5371a7159953088d83..a60569b18afcd5711e0a5856c64018ce21e4f375 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/codec.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/codec.go @@ -2,7 +2,9 @@ package binary import ( "bytes" + "errors" "fmt" + . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" "io" "reflect" "time" @@ -38,6 +40,7 @@ const ( func BasicCodecEncoder(o interface{}, w io.Writer, n *int64, err *error) { switch o := o.(type) { case nil: + // SANITY CHECK panic("nil type unsupported") case byte: WriteByte(typeByte, w, n, err) @@ -82,12 +85,16 @@ func BasicCodecEncoder(o interface{}, w io.Writer, n *int64, err *error) { WriteByte(typeTime, w, n, err) WriteTime(o, w, n, err) default: + // SANITY CHECK panic(fmt.Sprintf("Unsupported type: %v", reflect.TypeOf(o))) } } func BasicCodecDecoder(r io.Reader, n *int64, err *error) (o interface{}) { type_ := ReadByte(r, n, err) + if *err != nil { + return + } switch type_ { case typeByte: o = ReadByte(r, n, err) @@ -118,15 +125,12 @@ func BasicCodecDecoder(r io.Reader, n *int64, err *error) (o interface{}) { case typeTime: o = ReadTime(r, n, err) default: - if *err != nil { - panic(*err) - } else { - panic(fmt.Sprintf("Unsupported type byte: %X", type_)) - } + *err = errors.New(Fmt("Unsupported type byte: %X", type_)) } - return o + return } +// Contract: Caller must ensure that types match. func BasicCodecComparator(o1 interface{}, o2 interface{}) int { switch o1.(type) { case byte: @@ -157,8 +161,10 @@ func BasicCodecComparator(o1 interface{}, o2 interface{}) int { case time.Time: return int(o1.(time.Time).UnixNano() - o2.(time.Time).UnixNano()) default: - panic(fmt.Sprintf("Unsupported type: %v", reflect.TypeOf(o1))) + // SANITY CHECK + panic(Fmt("Unsupported type: %v", reflect.TypeOf(o1))) } + return 0 } var BasicCodec = Codec{ diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/int.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/int.go index 90f3f4323ebba86a7c1490e9786fcdbe24fee82b..3be5f224a8567604699e97130eb88667aace6098 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/int.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/int.go @@ -158,8 +158,7 @@ func ReadUint64(r io.Reader, n *int64, err *error) uint64 { // Varint -func uvarintSize(i_ uint) int { - i := uint64(i_) +func uvarintSize(i uint64) int { if i == 0 { return 0 } @@ -193,7 +192,7 @@ func WriteVarint(i int, w io.Writer, n *int64, err *error) { negate = true i = -i } - var size = uvarintSize(uint(i)) + var size = uvarintSize(uint64(i)) if negate { // e.g. 0xF1 for a single negative byte WriteUint8(uint8(size+0xF0), w, n, err) @@ -220,6 +219,9 @@ func ReadVarint(r io.Reader, n *int64, err *error) int { return 0 } if size == 0 { + if negate { + setFirstErr(err, errors.New("Varint does not allow negative zero")) + } return 0 } buf := make([]byte, 8) @@ -236,7 +238,7 @@ func ReadVarint(r io.Reader, n *int64, err *error) int { // Uvarint func WriteUvarint(i uint, w io.Writer, n *int64, err *error) { - var size = uvarintSize(i) + var size = uvarintSize(uint64(i)) WriteUint8(uint8(size), w, n, err) if size > 0 { buf := make([]byte, 8) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/reflect.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/reflect.go index 7a2468df6a581064a1cf09f4c105bdcf84ff9ec1..461ffead9f3ebde40effba76efcfdabe50acf919 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/reflect.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/reflect.go @@ -66,10 +66,11 @@ func (info StructFieldInfo) unpack() (int, reflect.Type, Options) { return info.Index, info.Type, info.Options } -// e.g. If o is struct{Foo}{}, return is the Foo interface type. +// e.g. If o is struct{Foo}{}, return is the Foo reflection type. func GetTypeFromStructDeclaration(o interface{}) reflect.Type { rt := reflect.TypeOf(o) if rt.NumField() != 1 { + // SANITY CHECK panic("Unexpected number of fields in struct-wrapped declaration of type") } return rt.Field(0).Type @@ -78,6 +79,7 @@ func GetTypeFromStructDeclaration(o interface{}) reflect.Type { func SetByteForType(typeByte byte, rt reflect.Type) { typeInfo := GetTypeInfo(rt) if typeInfo.Byte != 0x00 && typeInfo.Byte != typeByte { + // SANITY CHECK panic(Fmt("Type %v already registered with type byte %X", rt, typeByte)) } typeInfo.Byte = typeByte @@ -122,6 +124,7 @@ type ConcreteType struct { func RegisterInterface(o interface{}, ctypes ...ConcreteType) *TypeInfo { it := GetTypeFromStructDeclaration(o) if it.Kind() != reflect.Interface { + // SANITY CHECK panic("RegisterInterface expects an interface") } toType := make(map[byte]reflect.Type, 0) @@ -131,9 +134,11 @@ func RegisterInterface(o interface{}, ctypes ...ConcreteType) *TypeInfo { typeByte := ctype.Byte SetByteForType(typeByte, crt) if typeByte == 0x00 { + // SANITY CHECK panic(Fmt("Byte of 0x00 is reserved for nil (%v)", ctype)) } if toType[typeByte] != nil { + // SANITY CHECK panic(Fmt("Duplicate Byte for type %v and %v", ctype, toType[typeByte])) } toType[typeByte] = crt @@ -177,6 +182,8 @@ func MakeTypeInfo(rt reflect.Type) *TypeInfo { return info } +// Contract: Caller must ensure that rt is supported +// (e.g. is recursively composed of supported native types, and structs and slices.) func readReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, r io.Reader, n *int64, err *error) { // Get typeInfo @@ -226,6 +233,9 @@ func readReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, r io.Rea typeInfo = GetTypeInfo(rt) if typeInfo.Byte != 0x00 { r = NewPrefixedReader([]byte{typeByte}, r) + } else if typeByte != 0x01 { + *err = errors.New(Fmt("Unexpected type byte %X for ptr of untyped thing", typeByte)) + return } // continue... } @@ -250,7 +260,7 @@ func readReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, r io.Rea } else { var sliceRv reflect.Value // Read length - length := int(ReadUvarint(r, n, err)) + length := ReadVarint(r, n, err) log.Debug(Fmt("Read length: %v", length)) sliceRv = reflect.MakeSlice(rt, 0, 0) // read one ReflectSliceChunk at a time and append @@ -263,6 +273,10 @@ func readReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, r io.Rea if *err != nil { return } + if MaxBinaryReadSize < *n { + *err = ErrBinaryReadSizeOverflow + return + } } sliceRv = reflect.AppendSlice(sliceRv, tmpSliceRv) } @@ -322,7 +336,7 @@ func readReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, r io.Rea case reflect.Uint64: if opts.Varint { - num := ReadUvarint(r, n, err) + num := ReadVarint(r, n, err) log.Debug(Fmt("Read num: %v", num)) rv.SetUint(uint64(num)) } else { @@ -347,7 +361,7 @@ func readReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, r io.Rea rv.SetUint(uint64(num)) case reflect.Uint: - num := ReadUvarint(r, n, err) + num := ReadVarint(r, n, err) log.Debug(Fmt("Read num: %v", num)) rv.SetUint(uint64(num)) @@ -357,6 +371,7 @@ func readReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, r io.Rea rv.SetBool(num > 0) default: + // SANITY CHECK panic(Fmt("Unknown field type %v", rt.Kind())) } } @@ -432,7 +447,7 @@ func writeReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, w io.Wr } else { // Write length length := rv.Len() - WriteUvarint(uint(length), w, n, err) + WriteVarint(length, w, n, err) // Write elems for i := 0; i < length; i++ { elemRv := rv.Index(i) @@ -501,6 +516,7 @@ func writeReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, w io.Wr } default: + // SANITY CHECK panic(Fmt("Unknown field type %v", rt.Kind())) } } @@ -523,6 +539,8 @@ func readByteJSON(o interface{}) (typeByte byte, rest interface{}, err error) { return } +// Contract: Caller must ensure that rt is supported +// (e.g. is recursively composed of supported native types, and structs and slices.) func readReflectJSON(rv reflect.Value, rt reflect.Type, o interface{}, err *error) { // Get typeInfo @@ -693,6 +711,7 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, o interface{}, err *erro rv.SetBool(bl) default: + // SANITY CHECK panic(Fmt("Unknown field type %v", rt.Kind())) } } @@ -818,6 +837,7 @@ func writeReflectJSON(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64, WriteTo(jsonBytes, w, n, err) default: + // SANITY CHECK panic(Fmt("Unknown field type %v", rt.Kind())) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/reflect_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/reflect_test.go index b92866b589b870ab9a2efbb7acc1d3fca7ff8f2f..5d35fff041ccf333e27a723dd9f910d1f48d32c6 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/reflect_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/reflect_test.go @@ -72,13 +72,14 @@ func TestAnimalInterface(t *testing.T) { ptr := reflect.New(rte).Interface() fmt.Printf("ptr: %v", ptr) - // Make a binary byteslice that represents a snake. - snakeBytes := BinaryBytes(Snake([]byte("snake"))) + // Make a binary byteslice that represents a *snake. + foo = Snake([]byte("snake")) + snakeBytes := BinaryBytes(foo) snakeReader := bytes.NewReader(snakeBytes) // Now you can read it. n, err := new(int64), new(error) - it := *ReadBinary(ptr, snakeReader, n, err).(*Animal) + it := ReadBinary(foo, snakeReader, n, err).(Animal) fmt.Println(it, reflect.TypeOf(it)) } @@ -374,7 +375,7 @@ func TestBinary(t *testing.T) { // Read onto a pointer n, err = new(int64), new(error) - res = ReadBinary(instancePtr, bytes.NewReader(data), n, err) + res = ReadBinaryPtr(instancePtr, bytes.NewReader(data), n, err) if *err != nil { t.Fatalf("Failed to read into instance: %v", *err) } @@ -455,7 +456,7 @@ func TestJSONFieldNames(t *testing.T) { func TestBadAlloc(t *testing.T) { n, err := new(int64), new(error) instance := new([]byte) - data := RandBytes(ByteSliceChunk * 100) + data := RandBytes(100 * 1024) b := new(bytes.Buffer) // this slice of data claims to be much bigger than it really is WriteUvarint(uint(10000000000000000), b, n, err) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/string.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/string.go index c7ebd7f7718c3f7b274e0a1162dbc22b95e4a791..f1085619bc4bd2b500a51a4c06b80aa04a6b4a1c 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/string.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/string.go @@ -1,20 +1,33 @@ package binary -import "io" +import ( + "io" + + . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" +) // String func WriteString(s string, w io.Writer, n *int64, err *error) { - WriteUvarint(uint(len(s)), w, n, err) + WriteVarint(len(s), w, n, err) WriteTo([]byte(s), w, n, err) } func ReadString(r io.Reader, n *int64, err *error) string { - length := ReadUvarint(r, n, err) + length := ReadVarint(r, n, err) if *err != nil { return "" } - buf := make([]byte, int(length)) + if length < 0 { + *err = ErrBinaryReadSizeUnderflow + return "" + } + if MaxBinaryReadSize < MaxInt64(int64(length), *n+int64(length)) { + *err = ErrBinaryReadSizeOverflow + return "" + } + + buf := make([]byte, length) ReadFull(buf, r, n, err) return string(buf) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/util.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/util.go index b72d4952a5026ec6fce149f194913c3b4eda98dd..18b5229b2a4bccd84350f2d1206869fff8f8977f 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/util.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/binary/util.go @@ -6,6 +6,8 @@ import ( "github.com/eris-ltd/eris-db/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160" ) +// THESE PANICS ARE SANITY CHECKS + func BinaryBytes(o interface{}) []byte { w, n, err := new(bytes.Buffer), new(int64), new(error) WriteBinary(o, w, n, err) @@ -38,6 +40,7 @@ func BinaryCompare(a, b interface{}) int { return bytes.Compare(aBytes, bBytes) } +// NOTE: only use this if you need 32 bytes. func BinarySha256(o interface{}) []byte { hasher, n, err := sha256.New(), new(int64), new(error) WriteBinary(o, hasher, n, err) @@ -47,6 +50,7 @@ func BinarySha256(o interface{}) []byte { return hasher.Sum(nil) } +// NOTE: The default hash function is Ripemd160. func BinaryRipemd160(o interface{}) []byte { hasher, n, err := ripemd160.New(), new(int64), new(error) WriteBinary(o, hasher, n, err) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool.go index 65588d769be683d79ce67737bfdeba3812486222..5d452c8e05f587a253152d6b32b288cd376ab286 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool.go @@ -23,8 +23,8 @@ var ( ) /* - Peers self report their heights when a new peer joins the block pool. - Starting from pool.height (inclusive), we request blocks + Peers self report their heights when we join the block pool. + Starting from our latest pool.height, we request blocks in sequence from peers that reported higher heights than ours. Every so often we ask peers what height they're on so we can keep going. @@ -36,8 +36,8 @@ var ( type BlockPool struct { // block requests requestsMtx sync.Mutex - requests map[uint]*bpRequest - height uint // the lowest key in requests. + requests map[int]*bpRequest + height int // the lowest key in requests. numUnassigned int32 // number of requests not yet assigned to a peer numPending int32 // number of requests pending assignment or block response @@ -52,11 +52,11 @@ type BlockPool struct { running int32 // atomic } -func NewBlockPool(start uint, requestsCh chan<- BlockRequest, timeoutsCh chan<- string) *BlockPool { +func NewBlockPool(start int, requestsCh chan<- BlockRequest, timeoutsCh chan<- string) *BlockPool { return &BlockPool{ peers: make(map[string]*bpPeer), - requests: make(map[uint]*bpRequest), + requests: make(map[int]*bpRequest), height: start, numUnassigned: 0, numPending: 0, @@ -94,7 +94,7 @@ RUN_LOOP: if atomic.LoadInt32(&pool.running) == 0 { break RUN_LOOP } - _, numPending := pool.GetStatus() + _, numPending, _ := pool.GetStatus() if numPending >= maxPendingRequests { // sleep for a bit. time.Sleep(requestIntervalMS * time.Millisecond) @@ -108,11 +108,11 @@ RUN_LOOP: } } -func (pool *BlockPool) GetStatus() (uint, int32) { +func (pool *BlockPool) GetStatus() (int, int32, int32) { pool.requestsMtx.Lock() // Lock defer pool.requestsMtx.Unlock() - return pool.height, pool.numPending + return pool.height, pool.numPending, pool.numUnassigned } // We need to see the second block's Validation to validate the first block. @@ -136,9 +136,11 @@ func (pool *BlockPool) PopRequest() { pool.requestsMtx.Lock() // Lock defer pool.requestsMtx.Unlock() + // SANITY CHECK if r := pool.requests[pool.height]; r == nil || r.block == nil { panic("PopRequest() requires a valid block") } + // SANITY CHECK END delete(pool.requests, pool.height) pool.height++ @@ -146,14 +148,16 @@ func (pool *BlockPool) PopRequest() { // Invalidates the block at pool.height. // Remove the peer and request from others. -func (pool *BlockPool) RedoRequest(height uint) { +func (pool *BlockPool) RedoRequest(height int) { pool.requestsMtx.Lock() // Lock defer pool.requestsMtx.Unlock() request := pool.requests[height] + // SANITY CHECK if request.block == nil { panic("Expected block to be non-nil") } + // SANITY CHECK END // TODO: record this malfeasance // maybe punish peer on switch (an invalid block!) pool.RemovePeer(request.peerId) // Lock on peersMtx. @@ -165,7 +169,7 @@ func (pool *BlockPool) RedoRequest(height uint) { go requestRoutine(pool, height) } -func (pool *BlockPool) hasBlock(height uint) bool { +func (pool *BlockPool) hasBlock(height int) bool { pool.requestsMtx.Lock() // Lock defer pool.requestsMtx.Unlock() @@ -173,7 +177,7 @@ func (pool *BlockPool) hasBlock(height uint) bool { return request != nil && request.block != nil } -func (pool *BlockPool) setPeerForRequest(height uint, peerId string) { +func (pool *BlockPool) setPeerForRequest(height int, peerId string) { pool.requestsMtx.Lock() // Lock defer pool.requestsMtx.Unlock() @@ -185,7 +189,7 @@ func (pool *BlockPool) setPeerForRequest(height uint, peerId string) { request.peerId = peerId } -func (pool *BlockPool) removePeerForRequest(height uint, peerId string) { +func (pool *BlockPool) removePeerForRequest(height int, peerId string) { pool.requestsMtx.Lock() // Lock defer pool.requestsMtx.Unlock() @@ -224,7 +228,7 @@ func (pool *BlockPool) getPeer(peerId string) *bpPeer { } // Sets the peer's alleged blockchain height. -func (pool *BlockPool) SetPeerHeight(peerId string, height uint) { +func (pool *BlockPool) SetPeerHeight(peerId string, height int) { pool.peersMtx.Lock() // Lock defer pool.peersMtx.Unlock() @@ -250,7 +254,7 @@ func (pool *BlockPool) RemovePeer(peerId string) { // Pick an available peer with at least the given minHeight. // If no peers are available, returns nil. -func (pool *BlockPool) pickIncrAvailablePeer(minHeight uint) *bpPeer { +func (pool *BlockPool) pickIncrAvailablePeer(minHeight int) *bpPeer { pool.peersMtx.Lock() defer pool.peersMtx.Unlock() @@ -282,7 +286,7 @@ func (pool *BlockPool) makeNextRequest() { pool.requestsMtx.Lock() // Lock defer pool.requestsMtx.Unlock() - nextHeight := pool.height + uint(len(pool.requests)) + nextHeight := pool.height + len(pool.requests) request := &bpRequest{ height: nextHeight, peerId: "", @@ -296,7 +300,7 @@ func (pool *BlockPool) makeNextRequest() { go requestRoutine(pool, nextHeight) } -func (pool *BlockPool) sendRequest(height uint, peerId string) { +func (pool *BlockPool) sendRequest(height int, peerId string) { if atomic.LoadInt32(&pool.running) == 0 { return } @@ -315,7 +319,7 @@ func (pool *BlockPool) debug() string { defer pool.requestsMtx.Unlock() str := "" - for h := pool.height; h < pool.height+uint(len(pool.requests)); h++ { + for h := pool.height; h < pool.height+len(pool.requests); h++ { if pool.requests[h] == nil { str += Fmt("H(%v):X ", h) } else { @@ -330,12 +334,12 @@ func (pool *BlockPool) debug() string { type bpPeer struct { id string - height uint + height int numRequests int32 } type bpRequest struct { - height uint + height int peerId string block *types.Block } @@ -344,7 +348,7 @@ type bpRequest struct { // Responsible for making more requests as necessary // Returns only when a block is found (e.g. AddBlock() is called) -func requestRoutine(pool *BlockPool, height uint) { +func requestRoutine(pool *BlockPool, height int) { for { var peer *bpPeer = nil PICK_LOOP: @@ -374,7 +378,7 @@ func requestRoutine(pool *BlockPool, height uint) { return } // or already processed and we've moved past it - bpHeight, _ := pool.GetStatus() + bpHeight, _, _ := pool.GetStatus() if height < bpHeight { pool.decrPeer(peer.id) return @@ -393,6 +397,6 @@ func requestRoutine(pool *BlockPool, height uint) { //------------------------------------- type BlockRequest struct { - Height uint + Height int PeerId string } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool_test.go index 0e45ca4d940454300043d4191a7f0a361b434855..7e8f2f0472a973458a62161e4e0c60081b340cae 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/pool_test.go @@ -11,14 +11,14 @@ import ( type testPeer struct { id string - height uint + height int } -func makePeers(numPeers int, minHeight, maxHeight uint) map[string]testPeer { +func makePeers(numPeers int, minHeight, maxHeight int) map[string]testPeer { peers := make(map[string]testPeer, numPeers) for i := 0; i < numPeers; i++ { peerId := RandStr(12) - height := minHeight + uint(rand.Intn(int(maxHeight-minHeight))) + height := minHeight + rand.Intn(maxHeight-minHeight) peers[peerId] = testPeer{peerId, height} } return peers @@ -26,7 +26,7 @@ func makePeers(numPeers int, minHeight, maxHeight uint) map[string]testPeer { func TestBasic(t *testing.T) { peers := makePeers(10, 0, 1000) - start := uint(42) + start := 42 timeoutsCh := make(chan string, 100) requestsCh := make(chan BlockRequest, 100) pool := NewBlockPool(start, requestsCh, timeoutsCh) @@ -78,7 +78,7 @@ func TestBasic(t *testing.T) { func TestTimeout(t *testing.T) { peers := makePeers(10, 0, 1000) - start := uint(42) + start := 42 timeoutsCh := make(chan string, 100) requestsCh := make(chan BlockRequest, 100) pool := NewBlockPool(start, requestsCh, timeoutsCh) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/reactor.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/reactor.go index fb07f3a404c1ae0b93844a0e9cd7536de76133c7..3496d4c4f3887991c577fb809f53bd4afb1d8f10 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/reactor.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/reactor.go @@ -54,10 +54,12 @@ type BlockchainReactor struct { } func NewBlockchainReactor(state *sm.State, store *BlockStore, sync bool) *BlockchainReactor { + // SANITY CHECK if state.LastBlockHeight != store.Height() && state.LastBlockHeight != store.Height()-1 { // XXX double check this logic. panic(Fmt("state (%v) and store (%v) height mismatch", state.LastBlockHeight, store.Height())) } + // SANITY CHECK END requestsCh := make(chan BlockRequest, defaultChannelCapacity) timeoutsCh := make(chan string, defaultChannelCapacity) pool := NewBlockPool( @@ -197,19 +199,18 @@ FOR_LOOP: // ask for status updates go bcR.BroadcastStatusRequest() case _ = <-switchToConsensusTicker.C: - // not thread safe access for numUnassigned and numPending but should be fine - // TODO make threadsafe and use exposed functions + height, numUnassigned, numPending := bcR.pool.GetStatus() outbound, inbound, _ := bcR.sw.NumPeers() - log.Debug("Consensus ticker", "numUnassigned", bcR.pool.numUnassigned, "numPending", bcR.pool.numPending, + log.Debug("Consensus ticker", "numUnassigned", numUnassigned, "numPending", numPending, "total", len(bcR.pool.requests), "outbound", outbound, "inbound", inbound) // NOTE: this condition is very strict right now. may need to weaken // If all `maxPendingRequests` requests are unassigned // and we have some peers (say >= 3), then we're caught up - maxPending := bcR.pool.numPending == maxPendingRequests - allUnassigned := bcR.pool.numPending == bcR.pool.numUnassigned + maxPending := numPending == maxPendingRequests + allUnassigned := numPending == numUnassigned enoughPeers := outbound+inbound >= 3 if maxPending && allUnassigned && enoughPeers { - log.Info("Time to switch to consensus reactor!", "height", bcR.pool.height) + log.Info("Time to switch to consensus reactor!", "height", height) bcR.pool.Stop() conR := bcR.sw.Reactor("CONSENSUS").(consensusReactor) @@ -232,7 +233,7 @@ FOR_LOOP: firstPartsHeader := firstParts.Header() // Finally, verify the first block using the second's validation. err := bcR.state.BondedValidators.VerifyValidation( - bcR.state.ChainID, first.Hash(), firstPartsHeader, first.Height, second.Validation) + bcR.state.ChainID, first.Hash(), firstPartsHeader, first.Height, second.LastValidation) if err != nil { log.Debug("error in validation", "error", err) bcR.pool.RedoRequest(first.Height) @@ -244,7 +245,7 @@ FOR_LOOP: // TODO This is bad, are we zombie? panic(Fmt("Failed to process committed block: %v", err)) } - bcR.store.SaveBlock(first, firstParts, second.Validation) + bcR.store.SaveBlock(first, firstParts, second.LastValidation) bcR.state.Save() } } @@ -301,7 +302,7 @@ func DecodeMessage(bz []byte) (msgType byte, msg BlockchainMessage, err error) { //------------------------------------- type bcBlockRequestMessage struct { - Height uint + Height int } func (m *bcBlockRequestMessage) String() string { @@ -321,7 +322,7 @@ func (m *bcBlockResponseMessage) String() string { //------------------------------------- type bcStatusRequestMessage struct { - Height uint + Height int } func (m *bcStatusRequestMessage) String() string { @@ -331,7 +332,7 @@ func (m *bcStatusRequestMessage) String() string { //------------------------------------- type bcStatusResponseMessage struct { - Height uint + Height int } func (m *bcStatusResponseMessage) String() string { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/store.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/store.go index 497572c2d8c71cb849e236f164ae633545e0aacb..33e92d3a71926b0a41b24447e7ae83f8cda03fab 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/store.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain/store.go @@ -18,14 +18,16 @@ Simple low level store for blocks. There are three types of information stored: - BlockMeta: Meta information about each block - Block part: Parts of each block, aggregated w/ PartSet - - Validation: The Validation part of each block, for gossiping commit votes + - Validation: The Validation part of each block, for gossiping precommit votes -Currently the commit signatures are duplicated in the Block parts as +Currently the precommit signatures are duplicated in the Block parts as well as the Validation. In the future this may change, perhaps by moving the Validation data outside the Block. + +Panics indicate probable corruption in the data */ type BlockStore struct { - height uint + height int db dbm.DB } @@ -38,7 +40,7 @@ func NewBlockStore(db dbm.DB) *BlockStore { } // Height() returns the last known contiguous block height. -func (bs *BlockStore) Height() uint { +func (bs *BlockStore) Height() int { return bs.height } @@ -50,102 +52,107 @@ func (bs *BlockStore) GetReader(key []byte) io.Reader { return bytes.NewReader(bytez) } -func (bs *BlockStore) LoadBlock(height uint) *types.Block { +func (bs *BlockStore) LoadBlock(height int) *types.Block { var n int64 var err error r := bs.GetReader(calcBlockMetaKey(height)) if r == nil { - panic(Fmt("Block does not exist at height %v", height)) + return nil } meta := binary.ReadBinary(&types.BlockMeta{}, r, &n, &err).(*types.BlockMeta) if err != nil { + // SOMETHING HAS GONE HORRIBLY WRONG panic(Fmt("Error reading block meta: %v", err)) } bytez := []byte{} - for i := uint(0); i < meta.Parts.Total; i++ { + for i := 0; i < meta.PartsHeader.Total; i++ { part := bs.LoadBlockPart(height, i) bytez = append(bytez, part.Bytes...) } block := binary.ReadBinary(&types.Block{}, bytes.NewReader(bytez), &n, &err).(*types.Block) if err != nil { + // SOMETHING HAS GONE HORRIBLY WRONG panic(Fmt("Error reading block: %v", err)) } return block } -func (bs *BlockStore) LoadBlockPart(height uint, index uint) *types.Part { +func (bs *BlockStore) LoadBlockPart(height int, index int) *types.Part { var n int64 var err error r := bs.GetReader(calcBlockPartKey(height, index)) if r == nil { - panic(Fmt("BlockPart does not exist for height %v index %v", height, index)) + return nil } part := binary.ReadBinary(&types.Part{}, r, &n, &err).(*types.Part) if err != nil { + // SOMETHING HAS GONE HORRIBLY WRONG panic(Fmt("Error reading block part: %v", err)) } return part } -func (bs *BlockStore) LoadBlockMeta(height uint) *types.BlockMeta { +func (bs *BlockStore) LoadBlockMeta(height int) *types.BlockMeta { var n int64 var err error r := bs.GetReader(calcBlockMetaKey(height)) if r == nil { - panic(Fmt("BlockMeta does not exist for height %v", height)) + return nil } meta := binary.ReadBinary(&types.BlockMeta{}, r, &n, &err).(*types.BlockMeta) if err != nil { + // SOMETHING HAS GONE HORRIBLY WRONG panic(Fmt("Error reading block meta: %v", err)) } return meta } -// NOTE: the Commit-vote heights are for the block at `height-1` -// Since these are included in the subsequent block, the height -// is off by 1. -func (bs *BlockStore) LoadBlockValidation(height uint) *types.Validation { +// The +2/3 and other Precommit-votes for block at `height`. +// This Validation comes from block.LastValidation for `height+1`. +func (bs *BlockStore) LoadBlockValidation(height int) *types.Validation { var n int64 var err error r := bs.GetReader(calcBlockValidationKey(height)) if r == nil { - panic(Fmt("BlockValidation does not exist for height %v", height)) + return nil } validation := binary.ReadBinary(&types.Validation{}, r, &n, &err).(*types.Validation) if err != nil { + // SOMETHING HAS GONE HORRIBLY WRONG panic(Fmt("Error reading validation: %v", err)) } return validation } -// NOTE: the Commit-vote heights are for the block at `height` -func (bs *BlockStore) LoadSeenValidation(height uint) *types.Validation { +// NOTE: the Precommit-vote heights are for the block at `height` +func (bs *BlockStore) LoadSeenValidation(height int) *types.Validation { var n int64 var err error r := bs.GetReader(calcSeenValidationKey(height)) if r == nil { - panic(Fmt("SeenValidation does not exist for height %v", height)) + return nil } validation := binary.ReadBinary(&types.Validation{}, r, &n, &err).(*types.Validation) if err != nil { + // SOMETHING HAS GONE HORRIBLY WRONG panic(Fmt("Error reading validation: %v", err)) } return validation } // blockParts: Must be parts of the block -// seenValidation: The +2/3 commits that were seen which finalized the height. +// seenValidation: The +2/3 precommits that were seen which committed at height. // If all the nodes restart after committing a block, -// we need this to reload the commits to catch-up nodes to the +// we need this to reload the precommits to catch-up nodes to the // most recent height. Otherwise they'd stall at H-1. -// Also good to have to debug consensus issues & punish wrong-signers -// whose commits weren't included in the block. func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenValidation *types.Validation) { height := block.Height if height != bs.height+1 { + // SANITY CHECK panic(Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height)) } if !blockParts.IsComplete() { + // SANITY CHECK panic(Fmt("BlockStore can only save complete block part sets")) } @@ -155,15 +162,15 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s bs.db.Set(calcBlockMetaKey(height), metaBytes) // Save block parts - for i := uint(0); i < blockParts.Total(); i++ { + for i := 0; i < blockParts.Total(); i++ { bs.saveBlockPart(height, i, blockParts.GetPart(i)) } // Save block validation (duplicate and separate from the Block) - blockValidationBytes := binary.BinaryBytes(block.Validation) - bs.db.Set(calcBlockValidationKey(height), blockValidationBytes) + blockValidationBytes := binary.BinaryBytes(block.LastValidation) + bs.db.Set(calcBlockValidationKey(height-1), blockValidationBytes) - // Save seen validation (seen +2/3 commits) + // Save seen validation (seen +2/3 precommits for block) seenValidationBytes := binary.BinaryBytes(seenValidation) bs.db.Set(calcSeenValidationKey(height), seenValidationBytes) @@ -174,29 +181,31 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s bs.height = height } -func (bs *BlockStore) saveBlockPart(height uint, index uint, part *types.Part) { +func (bs *BlockStore) saveBlockPart(height int, index int, part *types.Part) { + // SANITY CHECK if height != bs.height+1 { panic(Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.height+1, height)) } + // SANITY CHECK END partBytes := binary.BinaryBytes(part) bs.db.Set(calcBlockPartKey(height, index), partBytes) } //----------------------------------------------------------------------------- -func calcBlockMetaKey(height uint) []byte { +func calcBlockMetaKey(height int) []byte { return []byte(fmt.Sprintf("H:%v", height)) } -func calcBlockPartKey(height uint, partIndex uint) []byte { +func calcBlockPartKey(height int, partIndex int) []byte { return []byte(fmt.Sprintf("P:%v:%v", height, partIndex)) } -func calcBlockValidationKey(height uint) []byte { +func calcBlockValidationKey(height int) []byte { return []byte(fmt.Sprintf("V:%v", height)) } -func calcSeenValidationKey(height uint) []byte { +func calcSeenValidationKey(height int) []byte { return []byte(fmt.Sprintf("SV:%v", height)) } @@ -205,12 +214,13 @@ func calcSeenValidationKey(height uint) []byte { var blockStoreKey = []byte("blockStore") type BlockStoreStateJSON struct { - Height uint + Height int } func (bsj BlockStoreStateJSON) Save(db dbm.DB) { bytes, err := json.Marshal(bsj) if err != nil { + // SANITY CHECK panic(Fmt("Could not marshal state bytes: %v", err)) } db.Set(blockStoreKey, bytes) @@ -226,6 +236,7 @@ func LoadBlockStoreStateJSON(db dbm.DB) BlockStoreStateJSON { bsj := BlockStoreStateJSON{} err := json.Unmarshal(bytes, &bsj) if err != nil { + // SOMETHING HAS GONE HORRIBLY WRONG panic(Fmt("Could not unmarshal bytes: %X", bytes)) } return bsj diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/bit_array.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/bit_array.go index eb314335bad8f1f9ca0c3301e38183090f961d55..320b629ef12bfd0b8e4844542f946cf0c11691a5 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/bit_array.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/bit_array.go @@ -9,18 +9,22 @@ import ( type BitArray struct { mtx sync.Mutex - Bits uint `json:"bits"` // NOTE: persisted via reflect, must be exported + Bits int `json:"bits"` // NOTE: persisted via reflect, must be exported Elems []uint64 `json:"elems"` // NOTE: persisted via reflect, must be exported } -func NewBitArray(bits uint) *BitArray { +// There is no BitArray whose Size is 0. Use nil instead. +func NewBitArray(bits int) *BitArray { + if bits == 0 { + return nil + } return &BitArray{ Bits: bits, Elems: make([]uint64, (bits+63)/64), } } -func (bA *BitArray) Size() uint { +func (bA *BitArray) Size() int { if bA == nil { return 0 } @@ -28,7 +32,7 @@ func (bA *BitArray) Size() uint { } // NOTE: behavior is undefined if i >= bA.Bits -func (bA *BitArray) GetIndex(i uint) bool { +func (bA *BitArray) GetIndex(i int) bool { if bA == nil { return false } @@ -37,15 +41,15 @@ func (bA *BitArray) GetIndex(i uint) bool { return bA.getIndex(i) } -func (bA *BitArray) getIndex(i uint) bool { +func (bA *BitArray) getIndex(i int) bool { if i >= bA.Bits { return false } - return bA.Elems[i/64]&(uint64(1)<<(i%64)) > 0 + return bA.Elems[i/64]&(uint64(1)<<uint(i%64)) > 0 } // NOTE: behavior is undefined if i >= bA.Bits -func (bA *BitArray) SetIndex(i uint, v bool) bool { +func (bA *BitArray) SetIndex(i int, v bool) bool { if bA == nil { return false } @@ -54,14 +58,14 @@ func (bA *BitArray) SetIndex(i uint, v bool) bool { return bA.setIndex(i, v) } -func (bA *BitArray) setIndex(i uint, v bool) bool { +func (bA *BitArray) setIndex(i int, v bool) bool { if i >= bA.Bits { return false } if v { - bA.Elems[i/64] |= (uint64(1) << (i % 64)) + bA.Elems[i/64] |= (uint64(1) << uint(i%64)) } else { - bA.Elems[i/64] &= ^(uint64(1) << (i % 64)) + bA.Elems[i/64] &= ^(uint64(1) << uint(i%64)) } return true } @@ -84,7 +88,7 @@ func (bA *BitArray) copy() *BitArray { } } -func (bA *BitArray) copyBits(bits uint) *BitArray { +func (bA *BitArray) copyBits(bits int) *BitArray { c := make([]uint64, (bits+63)/64) copy(c, bA.Elems) return &BitArray{ @@ -100,7 +104,7 @@ func (bA *BitArray) Or(o *BitArray) *BitArray { } bA.mtx.Lock() defer bA.mtx.Unlock() - c := bA.copyBits(MaxUint(bA.Bits, o.Bits)) + c := bA.copyBits(MaxInt(bA.Bits, o.Bits)) for i := 0; i < len(c.Elems); i++ { c.Elems[i] |= o.Elems[i] } @@ -118,7 +122,7 @@ func (bA *BitArray) And(o *BitArray) *BitArray { } func (bA *BitArray) and(o *BitArray) *BitArray { - c := bA.copyBits(MinUint(bA.Bits, o.Bits)) + c := bA.copyBits(MinInt(bA.Bits, o.Bits)) for i := 0; i < len(c.Elems); i++ { c.Elems[i] &= o.Elems[i] } @@ -149,9 +153,10 @@ func (bA *BitArray) Sub(o *BitArray) *BitArray { for i := 0; i < len(o.Elems)-1; i++ { c.Elems[i] &= ^c.Elems[i] } - i := uint(len(o.Elems) - 1) + i := len(o.Elems) - 1 if i >= 0 { for idx := i * 64; idx < o.Bits; idx++ { + // NOTE: each individual GetIndex() call to o is safe. c.setIndex(idx, c.getIndex(idx) && !o.GetIndex(idx)) } } @@ -168,10 +173,6 @@ func (bA *BitArray) IsFull() bool { bA.mtx.Lock() defer bA.mtx.Unlock() - if bA.Bits == 0 { - return false - } - // Check all elements except the last for _, elem := range bA.Elems[:len(bA.Elems)-1] { if (^elem) != 0 { @@ -182,10 +183,10 @@ func (bA *BitArray) IsFull() bool { // Check that the last element has (lastElemBits) 1's lastElemBits := (bA.Bits+63)%64 + 1 lastElem := bA.Elems[len(bA.Elems)-1] - return (lastElem+1)&((uint64(1)<<lastElemBits)-1) == 0 + return (lastElem+1)&((uint64(1)<<uint(lastElemBits))-1) == 0 } -func (bA *BitArray) PickRandom() (uint, bool) { +func (bA *BitArray) PickRandom() (int, bool) { if bA == nil { return 0, false } @@ -205,14 +206,14 @@ func (bA *BitArray) PickRandom() (uint, bool) { for j := 0; j < 64; j++ { bitIdx := ((j + randBitStart) % 64) if (bA.Elems[elemIdx] & (uint64(1) << uint(bitIdx))) > 0 { - return 64*uint(elemIdx) + uint(bitIdx), true + return 64*elemIdx + bitIdx, true } } panic("should not happen") } } else { // Special case for last elem, to ignore straggler bits - elemBits := int(bA.Bits) % 64 + elemBits := bA.Bits % 64 if elemBits == 0 { elemBits = 64 } @@ -220,7 +221,7 @@ func (bA *BitArray) PickRandom() (uint, bool) { for j := 0; j < elemBits; j++ { bitIdx := ((j + randBitStart) % elemBits) if (bA.Elems[elemIdx] & (uint64(1) << uint(bitIdx))) > 0 { - return 64*uint(elemIdx) + uint(bitIdx), true + return 64*elemIdx + bitIdx, true } } } @@ -250,7 +251,7 @@ func (bA *BitArray) stringIndented(indent string) string { lines := []string{} bits := "" - for i := uint(0); i < bA.Bits; i++ { + for i := 0; i < bA.Bits; i++ { if bA.getIndex(i) { bits += "X" } else { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/bit_array_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/bit_array_test.go index a0a1f397f842592aa059985588a18dc4cbf2adcb..93274aab033d90c68767b67801335ec09b24f6cb 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/bit_array_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/bit_array_test.go @@ -4,15 +4,15 @@ import ( "testing" ) -func randBitArray(bits uint) (*BitArray, []byte) { - src := RandBytes(int((bits + 7) / 8)) +func randBitArray(bits int) (*BitArray, []byte) { + src := RandBytes((bits + 7) / 8) bA := NewBitArray(bits) - for i := uint(0); i < uint(len(src)); i++ { - for j := uint(0); j < 8; j++ { + for i := 0; i < len(src); i++ { + for j := 0; j < 8; j++ { if i*8+j >= bits { return bA, src } - setBit := src[i]&(1<<j) > 0 + setBit := src[i]&(1<<uint(j)) > 0 bA.SetIndex(i*8+j, setBit) } } @@ -31,7 +31,7 @@ func TestAnd(t *testing.T) { if len(bA3.Elems) != len(bA2.Elems) { t.Error("Expected min elems length") } - for i := uint(0); i < bA3.Bits; i++ { + for i := 0; i < bA3.Bits; i++ { expected := bA1.GetIndex(i) && bA2.GetIndex(i) if bA3.GetIndex(i) != expected { t.Error("Wrong bit from bA3", i, bA1.GetIndex(i), bA2.GetIndex(i), bA3.GetIndex(i)) @@ -51,7 +51,7 @@ func TestOr(t *testing.T) { if len(bA3.Elems) != len(bA1.Elems) { t.Error("Expected max elems length") } - for i := uint(0); i < bA3.Bits; i++ { + for i := 0; i < bA3.Bits; i++ { expected := bA1.GetIndex(i) || bA2.GetIndex(i) if bA3.GetIndex(i) != expected { t.Error("Wrong bit from bA3", i, bA1.GetIndex(i), bA2.GetIndex(i), bA3.GetIndex(i)) @@ -71,7 +71,7 @@ func TestSub1(t *testing.T) { if len(bA3.Elems) != len(bA1.Elems) { t.Error("Expected bA1 elems length") } - for i := uint(0); i < bA3.Bits; i++ { + for i := 0; i < bA3.Bits; i++ { expected := bA1.GetIndex(i) if bA2.GetIndex(i) { expected = false @@ -94,7 +94,7 @@ func TestSub2(t *testing.T) { if len(bA3.Elems) != len(bA1.Elems) { t.Error("Expected bA1 elems length") } - for i := uint(0); i < bA3.Bits; i++ { + for i := 0; i < bA3.Bits; i++ { expected := bA1.GetIndex(i) if i < bA2.Bits && bA2.GetIndex(i) { expected = false @@ -108,12 +108,12 @@ func TestSub2(t *testing.T) { func TestPickRandom(t *testing.T) { for idx := 0; idx < 123; idx++ { bA1 := NewBitArray(123) - bA1.SetIndex(uint(idx), true) + bA1.SetIndex(idx, true) index, ok := bA1.PickRandom() if !ok { t.Fatal("Expected to pick element but got none") } - if index != uint(idx) { + if index != idx { t.Fatalf("Expected to pick element at %v but got wrong index", idx) } } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/int.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/int.go index c6e85dc62d169c67365996c6c6e2f0e865f7fa9e..50e86a0722596c27829fa2a52ee174ddcd5a6a13 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/int.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/int.go @@ -37,3 +37,19 @@ func PutUint64BE(dest []byte, i uint64) { func GetUint64BE(src []byte) uint64 { return binary.BigEndian.Uint64(src) } + +func PutInt64LE(dest []byte, i int64) { + binary.LittleEndian.PutUint64(dest, uint64(i)) +} + +func GetInt64LE(src []byte) int64 { + return int64(binary.LittleEndian.Uint64(src)) +} + +func PutInt64BE(dest []byte, i int64) { + binary.BigEndian.PutUint64(dest, uint64(i)) +} + +func GetInt64BE(src []byte) int64 { + return int64(binary.BigEndian.Uint64(src)) +} diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/os.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/os.go index 53febecbe5abed683b417c7c625f3795d9afc2c9..51c6d2b6ab075c88a9e1bcb464b64afc8cb2d43e 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/os.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/os.go @@ -5,6 +5,14 @@ import ( "io/ioutil" "os" "os/signal" + "path" + "sync" + "time" +) + +var ( + GoPath = os.Getenv("GOPATH") + TendermintRepo = path.Join(GoPath, "src", "github.com", "tendermint", "tendermint") ) func TrapSignal(cb func()) { @@ -96,3 +104,99 @@ func WriteFileAtomic(filePath string, newBytes []byte) error { err = os.Rename(filePath+".new", filePath) return err } + +//-------------------------------------------------------------------------------- + +/* AutoFile usage + +// Create/Append to ./autofile_test +af, err := OpenAutoFile("autofile_test") +if err != nil { + panic(err) +} + +// Stream of writes. +// During this time, the file may be moved e.g. by logRotate. +for i := 0; i < 60; i++ { + af.Write([]byte(Fmt("LOOP(%v)", i))) + time.Sleep(time.Second) +} + +// Close the AutoFile +err = af.Close() +if err != nil { + panic(err) +} +*/ + +const autoFileOpenDuration = 1000 * time.Millisecond + +// Automatically closes and re-opens file for writing. +// This is useful for using a log file with the logrotate tool. +type AutoFile struct { + Path string + ticker *time.Ticker + mtx sync.Mutex + file *os.File +} + +func OpenAutoFile(path string) (af *AutoFile, err error) { + af = &AutoFile{ + Path: path, + ticker: time.NewTicker(autoFileOpenDuration), + } + if err = af.openFile(); err != nil { + return + } + go af.processTicks() + return +} + +func (af *AutoFile) Close() error { + af.ticker.Stop() + af.mtx.Lock() + err := af.closeFile() + af.mtx.Unlock() + return err +} + +func (af *AutoFile) processTicks() { + for { + _, ok := <-af.ticker.C + if !ok { + return // Done. + } + af.mtx.Lock() + af.closeFile() + af.mtx.Unlock() + } +} + +func (af *AutoFile) closeFile() (err error) { + file := af.file + if file == nil { + return nil + } + af.file = nil + return file.Close() +} + +func (af *AutoFile) Write(b []byte) (n int, err error) { + af.mtx.Lock() + defer af.mtx.Unlock() + if af.file == nil { + if err = af.openFile(); err != nil { + return + } + } + return af.file.Write(b) +} + +func (af *AutoFile) openFile() error { + file, err := os.OpenFile(af.Path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) + if err != nil { + return err + } + af.file = file + return nil +} diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/random.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/random.go index 62a5092203b58df5be9b300dd59aa94332a660f2..e1d6046d668de40323cb20626e20329b32846573 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/random.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/random.go @@ -62,6 +62,18 @@ func RandUint() uint { return uint(rand.Int()) } +func RandInt16() int16 { + return int16(rand.Uint32() & (1<<16 - 1)) +} + +func RandInt32() int32 { + return int32(rand.Uint32()) +} + +func RandInt64() int64 { + return int64(rand.Uint32())<<32 + int64(rand.Uint32()) +} + func RandInt() int { return rand.Int() } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/word.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/word.go index 264a7dcc1281186d01a01374e33998a6f15a2cbf..39e4dbb1c7bb02a72d2ec119d8b3f421fc72d23f 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/common/word.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/common/word.go @@ -34,6 +34,12 @@ func Uint64ToWord256(i uint64) Word256 { return LeftPadWord256(buf[:]) } +func Int64ToWord256(i int64) Word256 { + buf := [8]byte{} + PutInt64BE(buf[:], i) + return LeftPadWord256(buf[:]) +} + func RightPadWord256(bz []byte) (word Word256) { copy(word[:], bz) return @@ -49,6 +55,11 @@ func Uint64FromWord256(word Word256) uint64 { return GetUint64BE(buf) } +func Int64FromWord256(word Word256) int64 { + buf := word.Postfix(8) + return GetInt64BE(buf) +} + //------------------------------------- type Tuple256 struct { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/config.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/config.go index b207135c0d42b15f3c5b9f5a52224dbb1611e11b..7b98f3d9187c44451e51a6135de0b5aeb1de1f1d 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/config.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint/config.go @@ -57,8 +57,8 @@ func GetConfig(rootDir string) cfg.Config { if mapConfig.IsSet("version") { Exit("Cannot set 'version' via config.toml") } - mapConfig.SetDefault("chain_id", "tendermint_testnet_5") - mapConfig.SetDefault("version", "0.3.0") // JAE: changed merkle tree persistence format for merkle proofs. + mapConfig.SetDefault("chain_id", "tendermint_testnet_6") + mapConfig.SetDefault("version", "0.4.0") // JAE: async consensus! mapConfig.SetDefault("genesis_file", rootDir+"/genesis.json") mapConfig.SetDefault("moniker", "anonymous") mapConfig.SetDefault("node_laddr", "0.0.0.0:46656") diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test/config.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test/config.go index 770e1e332efbf863a8f6713ecb06a0d845a7e953..9345870968c2db7d33ed2ae9edc4cb7c011123aa 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test/config.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test/config.go @@ -67,7 +67,7 @@ func GetConfig(rootDir string) cfg.Config { Exit("Cannot set 'version' via config.toml") } mapConfig.SetDefault("chain_id", "tendermint_test") - mapConfig.SetDefault("version", "0.3.0") + mapConfig.SetDefault("version", "0.4.0") mapConfig.SetDefault("genesis_file", rootDir+"/genesis.json") mapConfig.SetDefault("moniker", "anonymous") mapConfig.SetDefault("node_laddr", "0.0.0.0:36656") diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/pol.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/pol.go deleted file mode 100644 index 176aa9d66bd7316d85d4532e1cf353e822939ddc..0000000000000000000000000000000000000000 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/pol.go +++ /dev/null @@ -1,101 +0,0 @@ -package consensus - -import ( - "fmt" - - "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/account" - "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/binary" - . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" - sm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state" - "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types" -) - -// 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 `json:"round"` - Signature account.SignatureEd25519 `json:"signature"` -} - -// Proof of lock. -// +2/3 of validators' prevotes for a given blockhash (or nil) -type POL struct { - Height uint `json:"height"` - Round uint `json:"round"` - BlockHash []byte `json:"block_hash"` // Could be nil, which makes this a proof of unlock. - BlockParts types.PartSetHeader `json:"block_parts"` // When BlockHash is nil, this is zero. - Votes []POLVoteSignature `json:"votes"` // Prevote and commit signatures in ValidatorSet order. -} - -// Returns whether +2/3 have prevoted/committed for BlockHash. -func (pol *POL) Verify(valSet *sm.ValidatorSet) error { - - if uint(len(pol.Votes)) != valSet.Size() { - return fmt.Errorf("Invalid POL votes count: Expected %v, got %v", - valSet.Size(), len(pol.Votes)) - } - - talliedVotingPower := uint64(0) - prevoteDoc := account.SignBytes(config.GetString("chain_id"), &types.Vote{ - Height: pol.Height, Round: pol.Round, Type: types.VoteTypePrevote, - BlockHash: pol.BlockHash, - BlockParts: pol.BlockParts, - }) - seenValidators := map[string]struct{}{} - - for idx, vote := range pol.Votes { - // vote may be zero, in which case skip. - if vote.Signature.IsZero() { - continue - } - voteDoc := prevoteDoc - _, val := valSet.GetByIndex(uint(idx)) - - // Commit vote? - if vote.Round < pol.Round { - voteDoc = account.SignBytes(config.GetString("chain_id"), &types.Vote{ - Height: pol.Height, Round: vote.Round, Type: types.VoteTypeCommit, - BlockHash: pol.BlockHash, - BlockParts: pol.BlockParts, - }) - } else if vote.Round > pol.Round { - return fmt.Errorf("Invalid commit round %v for POL %v", vote.Round, pol) - } - - // Validate - if _, seen := seenValidators[string(val.Address)]; seen { - return fmt.Errorf("Duplicate validator for vote %v for POL %v", vote, pol) - } - - if !val.PubKey.VerifyBytes(voteDoc, vote.Signature) { - return fmt.Errorf("Invalid signature for vote %v for POL %v", vote, pol) - } - - // Tally - seenValidators[string(val.Address)] = struct{}{} - talliedVotingPower += val.VotingPower - } - - if talliedVotingPower > valSet.TotalVotingPower()*2/3 { - return nil - } else { - return fmt.Errorf("Invalid POL, insufficient voting power %v, needed %v", - talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1)) - } - -} - -func (pol *POL) StringShort() string { - if pol == nil { - return "nil-POL" - } else { - return fmt.Sprintf("POL{H:%v R:%v BH:%X}", pol.Height, pol.Round, - Fingerprint(pol.BlockHash), pol.BlockParts) - } -} - -func (pol *POL) MakePartSet() *types.PartSet { - return types.NewPartSetFromData(binary.BinaryBytes(pol)) -} diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/pol_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/pol_test.go deleted file mode 100644 index 52ce3ae634b0b5230c082707e0c53d49774ad360..0000000000000000000000000000000000000000 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/pol_test.go +++ /dev/null @@ -1,213 +0,0 @@ -package consensus - -import ( - "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/binary" - . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" - _ "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test" - sm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state" - "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types" - - "bytes" - "testing" -) - -// NOTE: see consensus/test.go for common test methods. - -// Convenience method. -// Signs the vote and sets the POL's vote at the desired index -// Returns the POLVoteSignature pointer, so you can modify it afterwards. -func signAddPOLVoteSignature(val *sm.PrivValidator, valSet *sm.ValidatorSet, vote *types.Vote, pol *POL) *POLVoteSignature { - vote = vote.Copy() - err := val.SignVote(config.GetString("chain_id"), vote) - if err != nil { - panic(err) - } - idx, _ := valSet.GetByAddress(val.Address) // now we have the index - pol.Votes[idx] = POLVoteSignature{vote.Round, vote.Signature} - return &pol.Votes[idx] -} - -func TestVerifyVotes(t *testing.T) { - height, round := uint(1), uint(0) - _, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) - - // Make a POL with -2/3 votes. - blockHash := RandBytes(32) - pol := &POL{ - Height: height, Round: round, BlockHash: blockHash, - Votes: make([]POLVoteSignature, valSet.Size()), - } - voteProto := &types.Vote{ - Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: blockHash, - } - for i := 0; i < 6; i++ { - signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol) - } - - // Check that validation fails. - if err := pol.Verify(valSet); err == nil { - t.Errorf("Expected POL.Verify() to fail, not enough votes.") - } - - // Insert another vote to make +2/3 - signAddPOLVoteSignature(privValidators[7], valSet, voteProto, pol) - - // Check that validation succeeds. - if err := pol.Verify(valSet); err != nil { - t.Errorf("POL.Verify() failed: %v", err) - } -} - -func TestVerifyInvalidVote(t *testing.T) { - height, round := uint(1), uint(0) - _, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) - - // Make a POL with +2/3 votes with the wrong signature. - blockHash := RandBytes(32) - pol := &POL{ - Height: height, Round: round, BlockHash: blockHash, - Votes: make([]POLVoteSignature, valSet.Size()), - } - voteProto := &types.Vote{ - Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: blockHash, - } - for i := 0; i < 7; i++ { - polVoteSig := signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol) - polVoteSig.Signature[0] += byte(0x01) // mutated! - } - - // Check that validation fails. - if err := pol.Verify(valSet); err == nil { - t.Errorf("Expected POL.Verify() to fail, wrong signatures.") - } -} - -func TestVerifyCommits(t *testing.T) { - height, round := uint(1), uint(2) - _, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) - - // Make a POL with +2/3 votes. - blockHash := RandBytes(32) - pol := &POL{ - Height: height, Round: round, BlockHash: blockHash, - Votes: make([]POLVoteSignature, valSet.Size()), - } - voteProto := &types.Vote{ - Height: height, Round: round - 1, Type: types.VoteTypeCommit, BlockHash: blockHash, - } - for i := 0; i < 7; i++ { - signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol) - } - - // Check that validation succeeds. - if err := pol.Verify(valSet); err != nil { - t.Errorf("POL.Verify() failed: %v", err) - } -} - -func TestVerifyInvalidCommits(t *testing.T) { - height, round := uint(1), uint(2) - _, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) - - // Make a POL with +2/3 votes with the wrong signature. - blockHash := RandBytes(32) - pol := &POL{ - Height: height, Round: round, BlockHash: blockHash, - Votes: make([]POLVoteSignature, valSet.Size()), - } - voteProto := &types.Vote{ - Height: height, Round: round - 1, Type: types.VoteTypeCommit, BlockHash: blockHash, - } - for i := 0; i < 7; i++ { - polVoteSig := signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol) - polVoteSig.Signature[0] += byte(0x01) - } - - // Check that validation fails. - if err := pol.Verify(valSet); err == nil { - t.Errorf("Expected POL.Verify() to fail, wrong signatures.") - } -} - -func TestVerifyInvalidCommitRounds(t *testing.T) { - height, round := uint(1), uint(2) - _, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) - - // Make a POL with +2/3 commits for the current round. - blockHash := RandBytes(32) - pol := &POL{ - Height: height, Round: round, BlockHash: blockHash, - Votes: make([]POLVoteSignature, valSet.Size()), - } - voteProto := &types.Vote{ - Height: height, Round: round, Type: types.VoteTypeCommit, BlockHash: blockHash, - } - for i := 0; i < 7; i++ { - signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol) - } - - // Check that validation fails. - if err := pol.Verify(valSet); err == nil { - t.Errorf("Expected POL.Verify() to fail, same round.") - } -} - -func TestVerifyInvalidCommitRounds2(t *testing.T) { - height, round := uint(1), uint(2) - _, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) - - // Make a POL with +2/3 commits for future round. - blockHash := RandBytes(32) - pol := &POL{ - Height: height, Round: round, BlockHash: blockHash, - Votes: make([]POLVoteSignature, valSet.Size()), - } - voteProto := &types.Vote{ - Height: height, Round: round + 1, Type: types.VoteTypeCommit, BlockHash: blockHash, - } - for i := 0; i < 7; i++ { - polVoteSig := signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol) - polVoteSig.Round += 1 // mutate round - } - - // Check that validation fails. - if err := pol.Verify(valSet); err == nil { - t.Errorf("Expected POL.Verify() to fail, future round.") - } -} - -func TestReadWrite(t *testing.T) { - height, round := uint(1), uint(2) - _, valSet, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) - - // Make a POL with +2/3 votes. - blockHash := RandBytes(32) - pol := &POL{ - Height: height, Round: round, BlockHash: blockHash, - Votes: make([]POLVoteSignature, valSet.Size()), - } - voteProto := &types.Vote{ - Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: blockHash, - } - for i := 0; i < 7; i++ { - signAddPOLVoteSignature(privValidators[i], valSet, voteProto, pol) - } - - // Write it to a buffer. - buf, n, err := new(bytes.Buffer), new(int64), new(error) - binary.WriteBinary(pol, buf, n, err) - if *err != nil { - t.Fatalf("Failed to write POL: %v", *err) - } - - // Read from buffer. - pol2 := binary.ReadBinary(&POL{}, buf, n, err).(*POL) - if *err != nil { - t.Fatalf("Failed to read POL: %v", *err) - } - - // Check that validation succeeds. - if err := pol2.Verify(valSet); err != nil { - t.Errorf("POL.Verify() failed: %v", err) - } -} diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/reactor.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/reactor.go index c2589ee1ab1b99131ea1bf4da6f2b2582b359069..2bd5c504144a2328c2b02b0b2b34bf019440211b 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/reactor.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/reactor.go @@ -31,8 +31,6 @@ const ( //----------------------------------------------------------------------------- -// The reactor's underlying ConsensusState may change state at any time. -// We atomically copy the RoundState struct before using it. type ConsensusReactor struct { sw *p2p.Switch running uint32 @@ -40,19 +38,17 @@ type ConsensusReactor struct { blockStore *bc.BlockStore conS *ConsensusState - - // if fast sync is running we don't really do anything - sync bool + fastSync bool evsw events.Fireable } -func NewConsensusReactor(consensusState *ConsensusState, blockStore *bc.BlockStore, sync bool) *ConsensusReactor { +func NewConsensusReactor(consensusState *ConsensusState, blockStore *bc.BlockStore, fastSync bool) *ConsensusReactor { conR := &ConsensusReactor{ - blockStore: blockStore, quit: make(chan struct{}), + blockStore: blockStore, conS: consensusState, - sync: sync, + fastSync: fastSync, } return conR } @@ -60,9 +56,9 @@ func NewConsensusReactor(consensusState *ConsensusState, blockStore *bc.BlockSto // Implements Reactor func (conR *ConsensusReactor) Start(sw *p2p.Switch) { if atomic.CompareAndSwapUint32(&conR.running, 0, 1) { - log.Info("Starting ConsensusReactor") + log.Info("Starting ConsensusReactor", "fastSync", conR.fastSync) conR.sw = sw - if !conR.sync { + if !conR.fastSync { conR.conS.Start() } go conR.broadcastNewRoundStepRoutine() @@ -119,7 +115,10 @@ func (conR *ConsensusReactor) AddPeer(peer *p2p.Peer) { go conR.gossipVotesRoutine(peer, peerState) // Send our state to peer. - conR.sendNewRoundStep(peer) + // If we're fast_syncing, broadcast a RoundStepMessage later upon SwitchToConsensus(). + if !conR.fastSync { + conR.sendNewRoundStepMessage(peer) + } } // Implements Reactor @@ -127,12 +126,15 @@ func (conR *ConsensusReactor) RemovePeer(peer *p2p.Peer, reason interface{}) { if !conR.IsRunning() { return } + // TODO //peer.Data.Get(PeerStateKey).(*PeerState).Disconnect() } // Implements Reactor +// NOTE: We process these messages even when we're fast_syncing. func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte) { - if conR.sync || !conR.IsRunning() { + log.Debug("Receive", "channel", chId, "peer", peer, "bytes", msgBytes) + if !conR.IsRunning() { return } @@ -144,7 +146,7 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte log.Warn("Error decoding message", "channel", chId, "peer", peer, "msg", msg_, "error", err, "bytes", msgBytes) return } - log.Debug("Receive", "channel", chId, "peer", peer, "msg", msg_) //, "bytes", msgBytes) + log.Debug("Receive", "channel", chId, "peer", peer, "msg", msg_, "rsHeight", rs.Height) //, "bytes", msgBytes) switch chId { case StateChannel: @@ -160,42 +162,47 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte } case DataChannel: + if conR.fastSync { + log.Warn("Ignoring message received during fastSync", "msg", msg_) + return + } switch msg := msg_.(type) { case *ProposalMessage: ps.SetHasProposal(msg.Proposal) err = conR.conS.SetProposal(msg.Proposal) - - case *PartMessage: - if msg.Type == partTypeProposalBlock { - ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Index) - _, err = conR.conS.AddProposalBlockPart(msg.Height, msg.Round, msg.Part) - } else if msg.Type == partTypeProposalPOL { - ps.SetHasProposalPOLPart(msg.Height, msg.Round, msg.Part.Index) - _, err = conR.conS.AddProposalPOLPart(msg.Height, msg.Round, msg.Part) - } else { - log.Warn(Fmt("Unknown part type %v", msg.Type)) - } - + case *ProposalPOLMessage: + ps.ApplyProposalPOLMessage(msg) + case *BlockPartMessage: + ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Proof.Index) + _, err = conR.conS.AddProposalBlockPart(msg.Height, msg.Part) default: log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg))) } case VoteChannel: + if conR.fastSync { + log.Warn("Ignoring message received during fastSync", "msg", msg_) + return + } switch msg := msg_.(type) { case *VoteMessage: vote := msg.Vote - if rs.Height != vote.Height { - if rs.Height == vote.Height+1 { - if rs.Step == RoundStepNewHeight && vote.Type == types.VoteTypeCommit { - goto VOTE_PASS // *ducks* - } + var validators *sm.ValidatorSet + if rs.Height == vote.Height { + validators = rs.Validators + } else if rs.Height == vote.Height+1 { + if !(rs.Step == RoundStepNewHeight && vote.Type == types.VoteTypePrecommit) { + return // Wrong height, not a LastCommit straggler commit. } + validators = rs.LastValidators + } else { return // Wrong height. Not necessarily a bad peer. } - VOTE_PASS: - validatorIndex := msg.ValidatorIndex - address, _ := rs.Validators.GetByIndex(validatorIndex) - added, index, err := conR.conS.AddVote(address, vote) + + // We have vote/validators. Height may not be rs.Height + + address, _ := validators.GetByIndex(msg.ValidatorIndex) + added, index, err := conR.conS.AddVote(address, vote, peer.Key) if err != nil { // If conflicting sig, broadcast evidence tx for slashing. Else punish peer. if errDupe, ok := err.(*types.ErrVoteConflictingSignature); ok { @@ -209,21 +216,17 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte } else { // Probably an invalid signature. Bad peer. log.Warn("Error attempting to add vote", "error", err) - // TODO: punish peer } } - // Initialize Prevotes/Precommits/Commits if needed ps.EnsureVoteBitArrays(rs.Height, rs.Validators.Size()) + ps.EnsureVoteBitArrays(rs.Height-1, rs.LastCommit.Size()) ps.SetHasVote(vote, index) if added { - msg := &HasVoteMessage{ - Height: vote.Height, - Round: vote.Round, - Type: vote.Type, - Index: index, - } - conR.sw.Broadcast(StateChannel, msg) + // If rs.Height == vote.Height && rs.Round < vote.Round, + // the peer is sending us CatchupCommit precommits. + // We could make note of this and help filter in broadcastHasVoteMessage(). + conR.broadcastHasVoteMessage(vote, index) } default: @@ -238,17 +241,45 @@ func (conR *ConsensusReactor) Receive(chId byte, peer *p2p.Peer, msgBytes []byte } } +// Broadcasts HasVoteMessage to peers that care. +func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote, index int) { + msg := &HasVoteMessage{ + Height: vote.Height, + Round: vote.Round, + Type: vote.Type, + Index: index, + } + conR.sw.Broadcast(StateChannel, msg) + /* + // TODO: Make this broadcast more selective. + for _, peer := range conR.sw.Peers().List() { + ps := peer.Data.Get(PeerStateKey).(*PeerState) + prs := ps.GetRoundState() + if prs.Height == vote.Height { + // TODO: Also filter on round? + peer.TrySend(StateChannel, msg) + } else { + // Height doesn't match + // TODO: check a field, maybe CatchupCommitRound? + // TODO: But that requires changing the struct field comment. + } + } + */ +} + // Sets our private validator account for signing votes. func (conR *ConsensusReactor) SetPrivValidator(priv *sm.PrivValidator) { conR.conS.SetPrivValidator(priv) } -// Switch from the fast sync to the consensus: -// reset the state, turn off fast sync, start the consensus-state-machine +// Switch from the fast_sync to the consensus: +// reset the state, turn off fast_sync, start the consensus-state-machine func (conR *ConsensusReactor) SwitchToConsensus(state *sm.State) { + log.Info("SwitchToConsensus") + // NOTE: The line below causes broadcastNewRoundStepRoutine() to + // broadcast a NewRoundStepMessage. conR.conS.updateToState(state, false) - conR.conS.newStepCh <- conR.conS.getRoundState() - conR.sync = false + conR.fastSync = false conR.conS.Start() } @@ -261,26 +292,20 @@ func (conR *ConsensusReactor) SetFireable(evsw events.Fireable) { //-------------------------------------- func makeRoundStepMessages(rs *RoundState) (nrsMsg *NewRoundStepMessage, csMsg *CommitStepMessage) { - // Get seconds since beginning of height. - timeElapsed := time.Now().Sub(rs.StartTime) - - // Broadcast NewRoundStepMessage nrsMsg = &NewRoundStepMessage{ Height: rs.Height, Round: rs.Round, Step: rs.Step, - SecondsSinceStartTime: uint(timeElapsed.Seconds()), + SecondsSinceStartTime: int(time.Now().Sub(rs.StartTime).Seconds()), + LastCommitRound: rs.LastCommit.Round(), } - - // If the step is commit, then also broadcast a CommitStepMessage. if rs.Step == RoundStepCommit { csMsg = &CommitStepMessage{ - Height: rs.Height, - BlockParts: rs.ProposalBlockParts.Header(), - BlockBitArray: rs.ProposalBlockParts.BitArray(), + Height: rs.Height, + BlockPartsHeader: rs.ProposalBlockParts.Header(), + BlockParts: rs.ProposalBlockParts.BitArray(), } } - return } @@ -306,7 +331,7 @@ func (conR *ConsensusReactor) broadcastNewRoundStepRoutine() { } } -func (conR *ConsensusReactor) sendNewRoundStep(peer *p2p.Peer) { +func (conR *ConsensusReactor) sendNewRoundStepMessage(peer *p2p.Peer) { rs := conR.conS.GetRoundState() nrsMsg, csMsg := makeRoundStepMessages(rs) if nrsMsg != nil { @@ -318,6 +343,7 @@ func (conR *ConsensusReactor) sendNewRoundStep(peer *p2p.Peer) { } func (conR *ConsensusReactor) gossipDataRoutine(peer *p2p.Peer, ps *PeerState) { + log := log.New("peer", peer.Key) OUTER_LOOP: for { @@ -330,33 +356,30 @@ OUTER_LOOP: prs := ps.GetRoundState() // Send proposal Block parts? - // NOTE: if we or peer is at RoundStepCommit*, the round - // won't necessarily match, but that's OK. - if rs.ProposalBlockParts.HasHeader(prs.ProposalBlockParts) { + if rs.ProposalBlockParts.HasHeader(prs.ProposalBlockPartsHeader) { //log.Debug("ProposalBlockParts matched", "blockParts", prs.ProposalBlockParts) - if index, ok := rs.ProposalBlockParts.BitArray().Sub(prs.ProposalBlockBitArray.Copy()).PickRandom(); ok { + if index, ok := rs.ProposalBlockParts.BitArray().Sub(prs.ProposalBlockParts.Copy()).PickRandom(); ok { part := rs.ProposalBlockParts.GetPart(index) - msg := &PartMessage{ - Height: rs.Height, - Round: rs.Round, - Type: partTypeProposalBlock, + msg := &BlockPartMessage{ + Height: rs.Height, // This tells peer that this part applies to us. + Round: rs.Round, // This tells peer that this part applies to us. Part: part, } peer.Send(DataChannel, msg) - ps.SetHasProposalBlockPart(rs.Height, rs.Round, index) + ps.SetHasProposalBlockPart(prs.Height, prs.Round, index) continue OUTER_LOOP } } // If the peer is on a previous height, help catch up. - if 0 < prs.Height && prs.Height < rs.Height { - //log.Debug("Data catchup", "height", rs.Height, "peerHeight", prs.Height, "peerProposalBlockBitArray", prs.ProposalBlockBitArray) - if index, ok := prs.ProposalBlockBitArray.Not().PickRandom(); ok { + if (0 < prs.Height) && (prs.Height < rs.Height) { + //log.Debug("Data catchup", "height", rs.Height, "peerHeight", prs.Height, "peerProposalBlockParts", prs.ProposalBlockParts) + if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok { // Ensure that the peer's PartSetHeader is correct blockMeta := conR.blockStore.LoadBlockMeta(prs.Height) - if !blockMeta.Parts.Equals(prs.ProposalBlockParts) { - log.Debug("Peer ProposalBlockParts mismatch, sleeping", - "peerHeight", prs.Height, "blockParts", blockMeta.Parts, "peerBlockParts", prs.ProposalBlockParts) + if !blockMeta.PartsHeader.Equals(prs.ProposalBlockPartsHeader) { + log.Debug("Peer ProposalBlockPartsHeader mismatch, sleeping", + "peerHeight", prs.Height, "blockPartsHeader", blockMeta.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } @@ -364,15 +387,14 @@ OUTER_LOOP: part := conR.blockStore.LoadBlockPart(prs.Height, index) if part == nil { log.Warn("Could not load part", "index", index, - "peerHeight", prs.Height, "blockParts", blockMeta.Parts, "peerBlockParts", prs.ProposalBlockParts) + "peerHeight", prs.Height, "blockPartsHeader", blockMeta.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } // Send the part - msg := &PartMessage{ - Height: prs.Height, - Round: prs.Round, - Type: partTypeProposalBlock, + msg := &BlockPartMessage{ + Height: prs.Height, // Not our height, so it doesn't matter. + Round: prs.Round, // Not our height, so it doesn't matter. Part: part, } peer.Send(DataChannel, msg) @@ -386,33 +408,38 @@ OUTER_LOOP: } // If height and round don't match, sleep. - if rs.Height != prs.Height || rs.Round != prs.Round { + if (rs.Height != prs.Height) || (rs.Round != prs.Round) { //log.Debug("Peer Height|Round mismatch, sleeping", "peerHeight", prs.Height, "peerRound", prs.Round, "peer", peer) time.Sleep(peerGossipSleepDuration) continue OUTER_LOOP } - // Send proposal? - if rs.Proposal != nil && !prs.Proposal { - msg := &ProposalMessage{Proposal: rs.Proposal} - peer.Send(DataChannel, msg) - ps.SetHasProposal(rs.Proposal) - continue OUTER_LOOP - } + // By here, height and round match. + // Proposal block parts were already matched and sent if any were wanted. + // (These can match on hash so the round doesn't matter) + // Now consider sending other things, like the Proposal itself. - // Send proposal POL parts? - if rs.ProposalPOLParts.HasHeader(prs.ProposalPOLParts) { - if index, ok := rs.ProposalPOLParts.BitArray().Sub(prs.ProposalPOLBitArray.Copy()).PickRandom(); ok { - msg := &PartMessage{ - Height: rs.Height, - Round: rs.Round, - Type: partTypeProposalPOL, - Part: rs.ProposalPOLParts.GetPart(index), + // Send Proposal && ProposalPOL BitArray? + if rs.Proposal != nil && !prs.Proposal { + // Proposal + { + msg := &ProposalMessage{Proposal: rs.Proposal} + peer.Send(DataChannel, msg) + ps.SetHasProposal(rs.Proposal) + } + // ProposalPOL. + // Peer must receive ProposalMessage first. + // rs.Proposal was validated, so rs.Proposal.POLRound <= rs.Round, + // so we definitely have rs.Votes.Prevotes(rs.Proposal.POLRound). + if 0 <= rs.Proposal.POLRound { + msg := &ProposalPOLMessage{ + Height: rs.Height, + ProposalPOLRound: rs.Proposal.POLRound, + ProposalPOL: rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(), } peer.Send(DataChannel, msg) - ps.SetHasProposalPOLPart(rs.Height, rs.Round, index) - continue OUTER_LOOP } + continue OUTER_LOOP } // Nothing to do. Sleep. @@ -422,6 +449,7 @@ OUTER_LOOP: } func (conR *ConsensusReactor) gossipVotesRoutine(peer *p2p.Peer, ps *PeerState) { + log := log.New("peer", peer.Key) // Simple hack to throttle logs upon sleep. var sleeping = 0 @@ -443,126 +471,76 @@ OUTER_LOOP: sleeping = 0 } - // Returns true when useful work was done. - trySendVote := func(height uint, voteSet *VoteSet, peerVoteSet *BitArray) (sent bool) { - if voteSet == nil { - return false - } else if peerVoteSet == nil { - ps.EnsureVoteBitArrays(height, voteSet.Size()) - return true - } - // TODO: give priority to our vote. - if index, ok := voteSet.BitArray().Sub(peerVoteSet.Copy()).PickRandom(); ok { - vote := voteSet.GetByIndex(index) - // NOTE: vote may be a commit. - msg := &VoteMessage{index, vote} - peer.Send(VoteChannel, msg) - ps.SetHasVote(vote, index) - return true - } - return false - } + log.Debug("gossipVotesRoutine", "rsHeight", rs.Height, "rsRound", rs.Round, + "prsHeight", prs.Height, "prsRound", prs.Round, "prsStep", prs.Step) - // Returns true when useful work was done. - trySendCommitFromValidation := func(blockMeta *types.BlockMeta, validation *types.Validation, peerVoteSet *BitArray) (sent bool) { - if validation == nil { - return false - } else if peerVoteSet == nil { - ps.EnsureVoteBitArrays(blockMeta.Header.Height, uint(len(validation.Commits))) - return true - } - if index, ok := validation.BitArray().Sub(prs.Commits.Copy()).PickRandom(); ok { - commit := validation.Commits[index] - log.Debug("Picked commit to send", "index", index, "commit", commit) - // Reconstruct vote. - vote := &types.Vote{ - Height: prs.Height, - Round: commit.Round, - Type: types.VoteTypeCommit, - BlockHash: blockMeta.Hash, - BlockParts: blockMeta.Parts, - Signature: commit.Signature, - } - msg := &VoteMessage{index, vote} - peer.Send(VoteChannel, msg) - ps.SetHasVote(vote, index) - return true - } - return false - } - - // If height matches, then send LastCommits, Prevotes, Precommits, or Commits. + // If height matches, then send LastCommit, Prevotes, Precommits. if rs.Height == prs.Height { - - // If there are lastcommits to send... - if prs.Round == 0 && prs.Step == RoundStepNewHeight { - if trySendVote(rs.Height-1, rs.LastCommits, prs.LastCommits) { + // If there are lastCommits to send... + if prs.Step == RoundStepNewHeight { + if ps.PickSendVote(rs.LastCommit) { + log.Debug("Picked rs.LastCommit to send") continue OUTER_LOOP } } - // If there are prevotes to send... if rs.Round == prs.Round && prs.Step <= RoundStepPrevote { - if trySendVote(rs.Height, rs.Prevotes, prs.Prevotes) { + if ps.PickSendVote(rs.Votes.Prevotes(rs.Round)) { + log.Debug("Picked rs.Prevotes(rs.Round) to send") continue OUTER_LOOP } } - // If there are precommits to send... if rs.Round == prs.Round && prs.Step <= RoundStepPrecommit { - if trySendVote(rs.Height, rs.Precommits, prs.Precommits) { + if ps.PickSendVote(rs.Votes.Precommits(rs.Round)) { + log.Debug("Picked rs.Precommits(rs.Round) to send") continue OUTER_LOOP } } - - // If there are any commits to send... - if trySendVote(rs.Height, rs.Commits, prs.Commits) { - continue OUTER_LOOP - } - } - - // Catchup logic - if prs.Height != 0 && !prs.HasAllCatchupCommits { - - // If peer is lagging by height 1, match our LastCommits or SeenValidation to peer's Commits. - if rs.Height == prs.Height+1 && rs.LastCommits.Size() > 0 { - // If there are lastcommits to send... - if trySendVote(prs.Height, rs.LastCommits, prs.Commits) { + // If there are prevotes to send for the last round... + if rs.Round == prs.Round+1 && prs.Step <= RoundStepPrevote { + if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) { + log.Debug("Picked rs.Prevotes(prs.Round) to send") continue OUTER_LOOP - } else { - ps.SetHasAllCatchupCommits(prs.Height) } } - - // Or, if peer is lagging by 1 and we don't have LastCommits, send SeenValidation. - if rs.Height == prs.Height+1 && rs.LastCommits.Size() == 0 { - // Load the blockMeta for block at prs.Height - blockMeta := conR.blockStore.LoadBlockMeta(prs.Height) - // Load the seen validation for prs.Height - validation := conR.blockStore.LoadSeenValidation(prs.Height) - log.Debug("Loaded SeenValidation for catch-up", "height", prs.Height, "blockMeta", blockMeta, "validation", validation) - - if trySendCommitFromValidation(blockMeta, validation, prs.Commits) { + // If there are precommits to send for the last round... + if rs.Round == prs.Round+1 && prs.Step <= RoundStepPrecommit { + if ps.PickSendVote(rs.Votes.Precommits(prs.Round)) { + log.Debug("Picked rs.Precommits(prs.Round) to send") continue OUTER_LOOP - } else { - ps.SetHasAllCatchupCommits(prs.Height) } } + // If there are POLPrevotes to send... + if 0 <= prs.ProposalPOLRound { + if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil { + if ps.PickSendVote(polPrevotes) { + log.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send") + continue OUTER_LOOP + } + } + } + } - // If peer is lagging by more than 1, send Validation. - if rs.Height >= prs.Height+2 { - // Load the blockMeta for block at prs.Height - blockMeta := conR.blockStore.LoadBlockMeta(prs.Height) - // Load the block validation for prs.Height+1, - // which contains commit signatures for prs.Height. - validation := conR.blockStore.LoadBlockValidation(prs.Height + 1) - log.Debug("Loaded BlockValidation for catch-up", "height", prs.Height+1, "blockMeta", blockMeta, "validation", validation) + // Special catchup logic. + // If peer is lagging by height 1, send LastCommit. + if prs.Height != 0 && rs.Height == prs.Height+1 { + if ps.PickSendVote(rs.LastCommit) { + log.Debug("Picked rs.LastCommit to send") + continue OUTER_LOOP + } + } - if trySendCommitFromValidation(blockMeta, validation, prs.Commits) { - continue OUTER_LOOP - } else { - ps.SetHasAllCatchupCommits(prs.Height) - } + // Catchup logic + // If peer is lagging by more than 1, send Validation. + if prs.Height != 0 && rs.Height >= prs.Height+2 { + // Load the block validation for prs.Height, + // which contains precommit signatures for prs.Height. + validation := conR.blockStore.LoadBlockValidation(prs.Height) + log.Debug("Loaded BlockValidation for catch-up", "height", prs.Height, "validation", validation) + if ps.PickSendVote(validation) { + log.Debug("Picked Catchup validation to send") + continue OUTER_LOOP } } @@ -570,9 +548,8 @@ OUTER_LOOP: // We sent nothing. Sleep... sleeping = 1 log.Debug("No votes to send, sleeping", "peer", peer, - "localPV", rs.Prevotes.BitArray(), "peerPV", prs.Prevotes, - "localPC", rs.Precommits.BitArray(), "peerPC", prs.Precommits, - "localCM", rs.Commits.BitArray(), "peerCM", prs.Commits) + "localPV", rs.Votes.Prevotes(rs.Round).BitArray(), "peerPV", prs.Prevotes, + "localPC", rs.Votes.Precommits(rs.Round).BitArray(), "peerPC", prs.Precommits) } else if sleeping == 2 { // Continued sleep... sleeping = 1 @@ -587,20 +564,21 @@ OUTER_LOOP: // Read only when returned by PeerState.GetRoundState(). type PeerRoundState struct { - Height uint // Height peer is at - Round uint // Round peer is at - Step RoundStepType // 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 - ProposalBlockParts types.PartSetHeader // - ProposalBlockBitArray *BitArray // True bit -> has part - ProposalPOLParts types.PartSetHeader // - ProposalPOLBitArray *BitArray // True bit -> has part - Prevotes *BitArray // All votes peer has for this round - Precommits *BitArray // All precommits peer has for this round - Commits *BitArray // All commits peer has for this height - LastCommits *BitArray // All commits peer has for last height - HasAllCatchupCommits bool // Used for catch-up + Height int // Height peer is at + Round int // Round peer is at + Step RoundStepType // 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 + ProposalBlockPartsHeader types.PartSetHeader // + ProposalBlockParts *BitArray // + ProposalPOLRound int // -1 if none + ProposalPOL *BitArray // nil until ProposalPOLMessage received. + Prevotes *BitArray // All votes peer has for this round + Precommits *BitArray // All precommits peer has for this round + LastCommitRound int // Round of commit for last height. + LastCommit *BitArray // All commit precommits of commit for last height. + CatchupCommitRound int // Round that we believe commit round is. + CatchupCommit *BitArray // All commit precommits peer has for this height } //----------------------------------------------------------------------------- @@ -611,14 +589,14 @@ var ( ) type PeerState struct { - Key string + Peer *p2p.Peer mtx sync.Mutex PeerRoundState } func NewPeerState(peer *p2p.Peer) *PeerState { - return &PeerState{Key: peer.Key} + return &PeerState{Peer: peer} } // Returns an atomic snapshot of the PeerRoundState. @@ -643,13 +621,13 @@ func (ps *PeerState) SetHasProposal(proposal *Proposal) { } ps.Proposal = true - ps.ProposalBlockParts = proposal.BlockParts - ps.ProposalBlockBitArray = NewBitArray(uint(proposal.BlockParts.Total)) - ps.ProposalPOLParts = proposal.POLParts - ps.ProposalPOLBitArray = NewBitArray(uint(proposal.POLParts.Total)) + ps.ProposalBlockPartsHeader = proposal.BlockPartsHeader + ps.ProposalBlockParts = NewBitArray(proposal.BlockPartsHeader.Total) + ps.ProposalPOLRound = proposal.POLRound + ps.ProposalPOL = nil // Nil until ProposalPOLMessage received. } -func (ps *PeerState) SetHasProposalBlockPart(height uint, round uint, index uint) { +func (ps *PeerState) SetHasProposalBlockPart(height int, round int, index int) { ps.mtx.Lock() defer ps.mtx.Unlock() @@ -657,24 +635,116 @@ func (ps *PeerState) SetHasProposalBlockPart(height uint, round uint, index uint return } - ps.ProposalBlockBitArray.SetIndex(uint(index), true) + ps.ProposalBlockParts.SetIndex(index, true) } -func (ps *PeerState) SetHasProposalPOLPart(height uint, round uint, index uint) { +// Convenience function to send vote to peer. +// Returns true if vote was sent. +func (ps *PeerState) PickSendVote(votes types.VoteSetReader) (ok bool) { + if index, vote, ok := ps.PickVoteToSend(votes); ok { + msg := &VoteMessage{index, vote} + ps.Peer.Send(VoteChannel, msg) + return true + } + return false +} + +// votes: Must be the correct Size() for the Height(). +func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (index int, vote *types.Vote, ok bool) { ps.mtx.Lock() defer ps.mtx.Unlock() - if ps.Height != height || ps.Round != round { - return + if votes.Size() == 0 { + return 0, nil, false } - ps.ProposalPOLBitArray.SetIndex(uint(index), true) + height, round, type_, size := votes.Height(), votes.Round(), votes.Type(), votes.Size() + + // Lazily set data using 'votes'. + if votes.IsCommit() { + ps.ensureCatchupCommitRound(height, round, size) + } + ps.ensureVoteBitArrays(height, size) + + psVotes := ps.getVoteBitArray(height, round, type_) + if psVotes == nil { + return 0, nil, false // Not something worth sending + } + if index, ok := votes.BitArray().Sub(psVotes).PickRandom(); ok { + ps.setHasVote(height, round, type_, index) + return index, votes.GetByIndex(index), true + } + return 0, nil, false } -func (ps *PeerState) EnsureVoteBitArrays(height uint, numValidators uint) { +func (ps *PeerState) getVoteBitArray(height, round int, type_ byte) *BitArray { + if ps.Height == height { + if ps.Round == round { + switch type_ { + case types.VoteTypePrevote: + return ps.Prevotes + case types.VoteTypePrecommit: + return ps.Precommits + default: + panic(Fmt("Unexpected vote type %X", type_)) + } + } + if ps.CatchupCommitRound == round { + switch type_ { + case types.VoteTypePrevote: + return nil + case types.VoteTypePrecommit: + return ps.CatchupCommit + default: + panic(Fmt("Unexpected vote type %X", type_)) + } + } + return nil + } + if ps.Height == height+1 { + if ps.LastCommitRound == round { + switch type_ { + case types.VoteTypePrevote: + return nil + case types.VoteTypePrecommit: + return ps.LastCommit + default: + panic(Fmt("Unexpected vote type %X", type_)) + } + } + return nil + } + return nil +} + +// NOTE: 'round' is what we know to be the commit round for height. +func (ps *PeerState) ensureCatchupCommitRound(height, round int, numValidators int) { + if ps.Height != height { + return + } + if ps.CatchupCommitRound != -1 && ps.CatchupCommitRound != round { + panic(Fmt("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round)) + } + if ps.CatchupCommitRound == round { + return // Nothing to do! + } + ps.CatchupCommitRound = round + if round == ps.Round { + ps.CatchupCommit = ps.Precommits + } else { + ps.CatchupCommit = NewBitArray(numValidators) + } +} + +// NOTE: It's important to make sure that numValidators actually matches +// what the node sees as the number of validators for height. +func (ps *PeerState) EnsureVoteBitArrays(height int, numValidators int) { ps.mtx.Lock() defer ps.mtx.Unlock() + ps.ensureVoteBitArrays(height, numValidators) +} +func (ps *PeerState) ensureVoteBitArrays(height int, numValidators int) { if ps.Height == height { if ps.Prevotes == nil { ps.Prevotes = NewBitArray(numValidators) @@ -682,61 +752,68 @@ func (ps *PeerState) EnsureVoteBitArrays(height uint, numValidators uint) { if ps.Precommits == nil { ps.Precommits = NewBitArray(numValidators) } - if ps.Commits == nil { - ps.Commits = NewBitArray(numValidators) + if ps.CatchupCommit == nil { + ps.CatchupCommit = NewBitArray(numValidators) + } + if ps.ProposalPOL == nil { + ps.ProposalPOL = NewBitArray(numValidators) } } else if ps.Height == height+1 { - if ps.LastCommits == nil { - ps.LastCommits = NewBitArray(numValidators) + if ps.LastCommit == nil { + ps.LastCommit = NewBitArray(numValidators) } } } -func (ps *PeerState) SetHasVote(vote *types.Vote, index uint) { +func (ps *PeerState) SetHasVote(vote *types.Vote, index int) { ps.mtx.Lock() defer ps.mtx.Unlock() ps.setHasVote(vote.Height, vote.Round, vote.Type, index) } -func (ps *PeerState) setHasVote(height uint, round uint, type_ byte, index uint) { - if ps.Height == height+1 && type_ == types.VoteTypeCommit { - // Special case for LastCommits. - ps.LastCommits.SetIndex(index, true) - log.Debug("SetHasVote", "lastCommits", ps.LastCommits, "index", index) - return - } else if ps.Height != height { - // Does not apply. - return +func (ps *PeerState) setHasVote(height int, round int, type_ byte, index int) { + log := log.New("peer", ps.Peer.Key, "peerRound", ps.Round, "height", height, "round", round) + if type_ != types.VoteTypePrevote && type_ != types.VoteTypePrecommit { + panic("Invalid vote type") // SANITY } - switch type_ { - case types.VoteTypePrevote: - ps.Prevotes.SetIndex(index, true) - log.Debug("SetHasVote", "peer", ps.Key, "prevotes", ps.Prevotes, "index", index) - case types.VoteTypePrecommit: - ps.Precommits.SetIndex(index, true) - log.Debug("SetHasVote", "peer", ps.Key, "precommits", ps.Precommits, "index", index) - case types.VoteTypeCommit: - if round < ps.Round { - ps.Prevotes.SetIndex(index, true) - ps.Precommits.SetIndex(index, true) - } - ps.Commits.SetIndex(index, true) - log.Debug("SetHasVote", "peer", ps.Key, "commits", ps.Commits, "index", index) - default: - panic("Invalid vote type") - } -} - -// When catching up, this helps keep track of whether -// we should send more commit votes from the block (validation) store -func (ps *PeerState) SetHasAllCatchupCommits(height uint) { - ps.mtx.Lock() - defer ps.mtx.Unlock() - if ps.Height == height { - ps.HasAllCatchupCommits = true + if ps.Round == round { + switch type_ { + case types.VoteTypePrevote: + ps.Prevotes.SetIndex(index, true) + log.Debug("SetHasVote(round-match)", "prevotes", ps.Prevotes, "index", index) + case types.VoteTypePrecommit: + ps.Precommits.SetIndex(index, true) + log.Debug("SetHasVote(round-match)", "precommits", ps.Precommits, "index", index) + } + } else if ps.CatchupCommitRound == round { + switch type_ { + case types.VoteTypePrevote: + case types.VoteTypePrecommit: + ps.CatchupCommit.SetIndex(index, true) + log.Debug("SetHasVote(CatchupCommit)", "precommits", ps.Precommits, "index", index) + } + } else if ps.ProposalPOLRound == round { + switch type_ { + case types.VoteTypePrevote: + ps.ProposalPOL.SetIndex(index, true) + log.Debug("SetHasVote(ProposalPOL)", "prevotes", ps.Prevotes, "index", index) + case types.VoteTypePrecommit: + } + } + } else if ps.Height == height+1 { + if ps.LastCommitRound == round { + switch type_ { + case types.VoteTypePrevote: + case types.VoteTypePrecommit: + ps.LastCommit.SetIndex(index, true) + log.Debug("setHasVote(LastCommit)", "lastCommit", ps.LastCommit, "index", index) + } + } + } else { + // Does not apply. } } @@ -744,10 +821,17 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage, rs *Roun ps.mtx.Lock() defer ps.mtx.Unlock() + // Ignore duplicate messages. + if ps.Height == msg.Height && ps.Round == msg.Round && ps.Step == msg.Step { + return + } + // Just remember these values. psHeight := ps.Height psRound := ps.Round //psStep := ps.Step + psCatchupCommitRound := ps.CatchupCommitRound + psCatchupCommit := ps.CatchupCommit startTime := time.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second) ps.Height = msg.Height @@ -756,24 +840,33 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage, rs *Roun ps.StartTime = startTime if psHeight != msg.Height || psRound != msg.Round { ps.Proposal = false - ps.ProposalBlockParts = types.PartSetHeader{} - ps.ProposalBlockBitArray = nil - ps.ProposalPOLParts = types.PartSetHeader{} - ps.ProposalPOLBitArray = nil + ps.ProposalBlockPartsHeader = types.PartSetHeader{} + ps.ProposalBlockParts = nil + ps.ProposalPOLRound = -1 + ps.ProposalPOL = nil // We'll update the BitArray capacity later. ps.Prevotes = nil ps.Precommits = nil } + if psHeight == msg.Height && psRound != msg.Round && msg.Round == psCatchupCommitRound { + // Peer caught up to CatchupCommitRound. + // Preserve psCatchupCommit! + // NOTE: We prefer to use prs.Precommits if + // pr.Round matches pr.CatchupCommitRound. + ps.Precommits = psCatchupCommit + } if psHeight != msg.Height { - // Shift Commits to LastCommits - if psHeight+1 == msg.Height { - ps.LastCommits = ps.Commits + // Shift Precommits to LastCommit. + if psHeight+1 == msg.Height && psRound == msg.LastCommitRound { + ps.LastCommitRound = msg.LastCommitRound + ps.LastCommit = ps.Precommits } else { - ps.LastCommits = nil + ps.LastCommitRound = msg.LastCommitRound + ps.LastCommit = nil } // We'll update the BitArray capacity later. - ps.Commits = nil - ps.HasAllCatchupCommits = false + ps.CatchupCommitRound = -1 + ps.CatchupCommit = nil } } @@ -785,25 +878,37 @@ func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage) { return } + ps.ProposalBlockPartsHeader = msg.BlockPartsHeader ps.ProposalBlockParts = msg.BlockParts - ps.ProposalBlockBitArray = msg.BlockBitArray } func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) { ps.mtx.Lock() defer ps.mtx.Unlock() - // Special case for LastCommits - if ps.Height == msg.Height+1 && msg.Type == types.VoteTypeCommit { - ps.LastCommits.SetIndex(msg.Index, true) - return - } else if ps.Height != msg.Height { + if ps.Height != msg.Height { return } ps.setHasVote(msg.Height, msg.Round, msg.Type, msg.Index) } +func (ps *PeerState) ApplyProposalPOLMessage(msg *ProposalPOLMessage) { + ps.mtx.Lock() + defer ps.mtx.Unlock() + + if ps.Height != msg.Height { + return + } + if ps.ProposalPOLRound != msg.ProposalPOLRound { + return + } + + // TODO: Merge onto existing ps.ProposalPOL? + // We might have sent some prevotes in the meantime. + ps.ProposalPOL = msg.ProposalPOL +} + //----------------------------------------------------------------------------- // Messages @@ -811,9 +916,10 @@ const ( msgTypeNewRoundStep = byte(0x01) msgTypeCommitStep = byte(0x02) msgTypeProposal = byte(0x11) - msgTypePart = byte(0x12) // both block & POL - msgTypeVote = byte(0x13) - msgTypeHasVote = byte(0x14) + msgTypeProposalPOL = byte(0x12) + msgTypeBlockPart = byte(0x13) // both block & POL + msgTypeVote = byte(0x14) + msgTypeHasVote = byte(0x15) ) type ConsensusMessage interface{} @@ -823,7 +929,8 @@ var _ = binary.RegisterInterface( binary.ConcreteType{&NewRoundStepMessage{}, msgTypeNewRoundStep}, binary.ConcreteType{&CommitStepMessage{}, msgTypeCommitStep}, binary.ConcreteType{&ProposalMessage{}, msgTypeProposal}, - binary.ConcreteType{&PartMessage{}, msgTypePart}, + binary.ConcreteType{&ProposalPOLMessage{}, msgTypeProposalPOL}, + binary.ConcreteType{&BlockPartMessage{}, msgTypeBlockPart}, binary.ConcreteType{&VoteMessage{}, msgTypeVote}, binary.ConcreteType{&HasVoteMessage{}, msgTypeHasVote}, ) @@ -839,27 +946,30 @@ func DecodeMessage(bz []byte) (msgType byte, msg ConsensusMessage, err error) { //------------------------------------- +// For every height/round/step transition type NewRoundStepMessage struct { - Height uint - Round uint + Height int + Round int Step RoundStepType - SecondsSinceStartTime uint + SecondsSinceStartTime int + LastCommitRound int } func (m *NewRoundStepMessage) String() string { - return fmt.Sprintf("[NewRoundStep H:%v R:%v S:%v]", m.Height, m.Round, m.Step) + return fmt.Sprintf("[NewRoundStep H:%v R:%v S:%v LCR:%v]", + m.Height, m.Round, m.Step, m.LastCommitRound) } //------------------------------------- type CommitStepMessage struct { - Height uint - BlockParts types.PartSetHeader - BlockBitArray *BitArray + Height int + BlockPartsHeader types.PartSetHeader + BlockParts *BitArray } func (m *CommitStepMessage) String() string { - return fmt.Sprintf("[CommitStep H:%v BP:%v BA:%v]", m.Height, m.BlockParts, m.BlockBitArray) + return fmt.Sprintf("[CommitStep H:%v BP:%v BA:%v]", m.Height, m.BlockPartsHeader, m.BlockParts) } //------------------------------------- @@ -874,26 +984,32 @@ func (m *ProposalMessage) String() string { //------------------------------------- -const ( - partTypeProposalBlock = byte(0x01) - partTypeProposalPOL = byte(0x02) -) +type ProposalPOLMessage struct { + Height int + ProposalPOLRound int + ProposalPOL *BitArray +} -type PartMessage struct { - Height uint - Round uint - Type byte +func (m *ProposalPOLMessage) String() string { + return fmt.Sprintf("[ProposalPOL H:%v POLR:%v POL:%v]", m.Height, m.ProposalPOLRound, m.ProposalPOL) +} + +//------------------------------------- + +type BlockPartMessage struct { + Height int + Round int Part *types.Part } -func (m *PartMessage) String() string { - return fmt.Sprintf("[Part H:%v R:%v T:%X P:%v]", m.Height, m.Round, m.Type, m.Part) +func (m *BlockPartMessage) String() string { + return fmt.Sprintf("[BlockPart H:%v R:%v P:%v]", m.Height, m.Round, m.Part) } //------------------------------------- type VoteMessage struct { - ValidatorIndex uint + ValidatorIndex int Vote *types.Vote } @@ -904,10 +1020,10 @@ func (m *VoteMessage) String() string { //------------------------------------- type HasVoteMessage struct { - Height uint - Round uint + Height int + Round int Type byte - Index uint + Index int } func (m *HasVoteMessage) String() string { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state.go index eb98d74501d304560785c324b12da3b769d59c76..0174d54ddf47669f95a9b19b94628dfed2b42ed6 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state.go @@ -1,56 +1,150 @@ /* -Consensus State Machine Overview: - -* Propose, Prevote, Precommit represent state machine stages. (aka RoundStep, or step). - Each take a predetermined amount of time depending on the round number. -* The Commit step can be entered by two means: - 1. After the Precommit step, +2/3 Precommits were found - 2. At any time, +2/3 Commits were found -* Once in the Commit stage, two conditions must both be satisfied - before proceeding to the next height NewHeight. -* The Propose step of the next height does not begin until - at least Delta duration *after* +2/3 Commits were found. - The step stays at NewHeight until this timeout occurs before - proceeding to Propose. -* The NewHeight is a transition period after the height is incremented, - where the node still receives late commits before potentially proposing. - The height should be incremented because a block had been - "committed by the network", and clients should see that - reflected as a new height. +* Terms: + + NewHeight, NewRound, Propose, Prevote, Precommit represent state machine steps. (aka RoundStep). + + To "prevote/precommit" something means to broadcast a prevote/precommit vote for something. + + (H,R) means a particular height H and round R. A vote "at (H,R)" is a vote signed with (H,R). + +* Proposals: + + A proposal is signed and published by the designated proposer at each round. + A proposal at (H,R) is composed of a proposed block of height H, and optionally a POL round number. + The POL round number R' (where R' < R) is set to the latest POL round known to the proposer. + If the proposer is locked on a block, it must include a POL round for the proposal. + +* POL and Justification of votes: + + A set of +2/3 of prevotes for a particular block or <nil> at (H,R) is called a POL (proof-of-lock). + A POL for <nil> might instead be called a proof-of-unlock, but it's better to have a single terminology for both. + + Each precommit which changes the lock at round R must be justified by a POL + where +2/3 prevoted for some block or <nil> at some round, equal to or less than R, + but greater than the last round at which the lock was changed. + + POL = Proof-of-Lock = +2/3 prevotes for block B (or +2/3 prevotes for <nil>) at (H,R) + + lastLockChangeRound < POLRound <= newLockChangeRound + + Without the POLRound <= newLockChangeRound condition, an unlock would be possible from a + future condition that hasn't happened yet, so it destroys deterministic accountability. + + The point of the above inequality is to ensure that changes in the lock (locking/unlocking/lock-changing) + are always justified by something that happened "in the past" by round numbers, so if there is a problem, + we can deterministically figure out "when it was caused" and by who. + + If there is a blockchain halt or fork, the blame will fall on +1/3 of Byzantine voting power = + who cannot push the blame into earlier rounds. (See lemma 4). + +* Block commits: + + The set of +2/3 of precommits at the same round for the same block is called a commit. + + A block contains the last block's commit which is comprised of +2/3 precommit votes at (H-1,R). + While all the precommits in the commit are from the same height & round (ordered by validator index), + some precommits may be absent (e.g. if the validator's precommit vote didn't reach the proposer in time), + or some precommits may be for different blockhashes for the last block hash (which is fine). + +* Consensus State Machine Overview: + + During NewHeight/NewRound/Propose/Prevote/Precommit: + * Nodes gossip the proposal block proposed by the designated proposer at round. + * Nodes gossip prevotes/precommits at rounds [0...currentRound+1] (currentRound+1 to allow round-skipping) + * Nodes gossip prevotes for the proposal's POL (proof-of-lock) round if proposed. + * Nodes gossip to late nodes (lagging in height) with precommits of the commit round (aka catchup) + + Upon each state transition, the height/round/step is broadcast to neighboring peers. + +* NewRound(height:H,round:R): + * Set up new round. --> goto Propose(H,R) + * NOTE: Not much happens in this step. It exists for clarity. + +* Propose(height:H,round:R): + * Upon entering Propose: + * The designated proposer proposes a block at (H,R). + * The Propose step ends: + * After `timeoutPropose` after entering Propose. --> goto Prevote(H,R) + * After receiving proposal block and all POL prevotes. --> goto Prevote(H,R) + * After any +2/3 prevotes received at (H,R+1). --> goto Prevote(H,R+1) + * After any +2/3 precommits received at (H,R+1). --> goto Precommit(H,R+1) + * After +2/3 precommits received for a particular block. --> goto Commit(H) + +* Prevote(height:H,round:R): + * Upon entering Prevote, each validator broadcasts its prevote vote. + * If the validator is locked on a block, it prevotes that. + * Else, if the proposed block from Propose(H,R) is good, it prevotes that. + * Else, if the proposal is invalid or wasn't received on time, it prevotes <nil>. + * The Prevote step ends: + * After +2/3 prevotes for a particular block or <nil>. --> goto Precommit(H,R) + * After `timeoutPrevote` after receiving any +2/3 prevotes. --> goto Precommit(H,R) + * After any +2/3 prevotes received at (H,R+1). --> goto Prevote(H,R+1) + * After any +2/3 precommits received at (H,R+1). --> goto Precommit(H,R+1) + * After +2/3 precommits received for a particular block. --> goto Commit(H) + +* Precommit(height:H,round:R): + * Upon entering Precommit, each validator broadcasts its precommit vote. + * If the validator had seen +2/3 of prevotes for a particular block from Prevote(H,R), + it locks (changes lock to) that block and precommits that block. + * Else, if the validator had seen +2/3 of prevotes for <nil>, it unlocks and precommits <nil>. + * Else, if +2/3 of prevotes for a particular block or <nil> is not received on time, + it precommits what it's locked on, or <nil>. + * The Precommit step ends: + * After +2/3 precommits for a particular block. --> goto Commit(H) + * After +2/3 precommits for <nil>. --> goto NewRound(H,R+1) + * After `timeoutPrecommit` after receiving any +2/3 precommits. --> goto NewRound(H,R+1) + * After any +2/3 prevotes received at (H,R+1). --> goto Prevote(H,R+1) + * After any +2/3 precommits received at (H,R+1). --> goto Precommit(H,R+1) + +* Commit(height:H): + * Set CommitTime = now + * Wait until block is received. --> goto NewHeight(H+1) + +* NewHeight(height:H): + * Move Precommits to LastCommit and increment height. + * Set StartTime = CommitTime+timeoutCommit + * Wait until `StartTime` to receive straggler commits. --> goto NewRound(H,0) + +* Proof of Safety: + If a good validator commits at round R, it's because it saw +2/3 of precommits at round R. + This implies that (assuming tolerance bounds) +1/3 of honest nodes are still locked at round R+1. + These locked validators will remain locked until they see +2/3 prevote for something + else, but this won't happen because +1/3 are locked and honest. + +* Proof of Liveness: + Lemma 1: If +1/3 good nodes are locked on two different blocks, the proposers' POLRound will + eventually cause nodes locked from the earlier round to unlock. + -> `timeoutProposalR` increments with round R, while the block.size && POL prevote size + are fixed, so eventually we'll be able to "fully gossip" the block & POL. + TODO: cap the block.size at something reasonable. + Lemma 2: If a good node is at round R, neighboring good nodes will soon catch up to round R. + Lemma 3: If a node at (H,R) receives +2/3 prevotes for a block (or +2/3 for <nil>) at (H,R+1), + it will enter NewRound(H,R+1). + Lemma 4: Terminal conditions imply the existence of deterministic accountability, for + a synchronous (fixed-duration) protocol extension (judgement). + TODO: define terminal conditions (fork and non-decision). + TODO: define new assumptions for the synchronous judgement period. + +-------------------------------------+ - | | - v |(Wait til CommitTime + Delta) + v |(Wait til `CommmitTime+timeoutCommit`) +-----------+ +-----+-----+ +----------> | Propose +--------------+ | NewHeight | | +-----------+ | +-----------+ | | ^ - | | | - | | | - |(Else) v | + |(Else, after timeoutPrecommit) v | +-----+-----+ +-----------+ | | Precommit | <------------------------+ Prevote | | +-----+-----+ +-----------+ | - |(If +2/3 Precommits found) | - | | - | + (When +2/3 Commits found) | - | | | - v v | - +------------------------------------------------------------------------------+ - | Commit | | - | | | - | +----------------+ * Save Block | | - | |Get Block Parts |---> * Stage Block +--+ + | - | +----------------+ * Broadcast Commit | * Setup New Height | - | | * Move Commits set to | - | +--> LastCommits to continue | - | | collecting commits | - | +-----------------+ | * Broadcast New State | - | |Get +2/3 Commits |--> * Set CommitTime +--+ | - | +-----------------+ | - | | - +------------------------------------------------------------------------------+ + |(When +2/3 Precommits for block found) | + v | + +--------------------------------------------------------------------+ + | Commit | + | | + | * Set CommitTime = now; | + | * Wait for block, then stage/save/commit block; | + +--------------------------------------------------------------------+ */ @@ -60,7 +154,6 @@ import ( "bytes" "errors" "fmt" - "math" "sync" "sync/atomic" "time" @@ -76,19 +169,18 @@ import ( "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types" ) -const ( - roundDeadlinePrevote = float64(1.0 / 3.0) // When the prevote is due. - roundDeadlinePrecommit = float64(2.0 / 3.0) // When the precommit vote is due. -) - var ( - RoundDuration0 = 10 * time.Second // The first round is 60 seconds long. - RoundDurationDelta = 3 * time.Second // Each successive round lasts 15 seconds longer. - newHeightDelta = RoundDuration0 / 3 // The time to wait between commitTime and startTime of next consensus rounds. + timeoutPropose = 3000 * time.Millisecond // Maximum duration of RoundStepPropose + timeoutPrevote0 = 1000 * time.Millisecond // After any +2/3 prevotes received, wait this long for stragglers. + timeoutPrevoteDelta = 0500 * time.Millisecond // timeoutPrevoteN is timeoutPrevote0 + timeoutPrevoteDelta*N + timeoutPrecommit0 = 1000 * time.Millisecond // After any +2/3 precommits received, wait this long for stragglers. + timeoutPrecommitDelta = 0500 * time.Millisecond // timeoutPrecommitN is timeoutPrecommit0 + timeoutPrecommitDelta*N + timeoutCommit = 2000 * time.Millisecond // After +2/3 commits received for committed block, wait this long for stragglers in the next height's RoundStepNewHeight. ) var ( ErrInvalidProposalSignature = errors.New("Error invalid proposal signature") + ErrInvalidProposalPOLRound = errors.New("Error invalid proposal POL round") ) //----------------------------------------------------------------------------- @@ -97,12 +189,15 @@ var ( type RoundStepType uint8 // These must be numeric, ordered. const ( - RoundStepNewHeight = RoundStepType(0x01) // Round0 for new height started, wait til CommitTime + Delta - RoundStepNewRound = RoundStepType(0x02) // Pseudostep, immediately goes to RoundStepPropose - RoundStepPropose = RoundStepType(0x03) // Did propose, gossip proposal - RoundStepPrevote = RoundStepType(0x04) // Did prevote, gossip prevotes - RoundStepPrecommit = RoundStepType(0x05) // Did precommit, gossip precommits - RoundStepCommit = RoundStepType(0x06) // Entered commit state machine + RoundStepNewHeight = RoundStepType(0x01) // Wait til CommitTime + timeoutCommit + RoundStepNewRound = RoundStepType(0x02) // Setup new round and go to RoundStepPropose + RoundStepPropose = RoundStepType(0x03) // Did propose, gossip proposal + RoundStepPrevote = RoundStepType(0x04) // Did prevote, gossip prevotes + RoundStepPrevoteWait = RoundStepType(0x05) // Did receive any +2/3 prevotes, start timeout + RoundStepPrecommit = RoundStepType(0x06) // Did precommit, gossip precommits + RoundStepPrecommitWait = RoundStepType(0x07) // Did receive any +2/3 precommits, start timeout + RoundStepCommit = RoundStepType(0x08) // Entered commit state machine + // NOTE: RoundStepNewHeight acts as RoundStepCommitWait. ) func (rs RoundStepType) String() string { @@ -115,82 +210,38 @@ func (rs RoundStepType) String() string { return "RoundStepPropose" case RoundStepPrevote: return "RoundStepPrevote" + case RoundStepPrevoteWait: + return "RoundStepPrevoteWait" case RoundStepPrecommit: return "RoundStepPrecommit" + case RoundStepPrecommitWait: + return "RoundStepPrecommitWait" case RoundStepCommit: return "RoundStepCommit" default: - panic(Fmt("Unknown RoundStep %X", rs)) + return "RoundStepUnknown" // Cannot panic. } } -//----------------------------------------------------------------------------- -// RoundAction enum type - -type RoundActionType string - -const ( - RoundActionPropose = RoundActionType("PR") // Propose and goto RoundStepPropose - RoundActionPrevote = RoundActionType("PV") // Prevote and goto RoundStepPrevote - RoundActionPrecommit = RoundActionType("PC") // Precommit and goto RoundStepPrecommit - RoundActionTryCommit = RoundActionType("TC") // Goto RoundStepCommit, or RoundStepPropose for next round. - RoundActionCommit = RoundActionType("CM") // Goto RoundStepCommit upon +2/3 commits - RoundActionTryFinalize = RoundActionType("TF") // Maybe goto RoundStepPropose for next round. -) - -func (rat RoundActionType) String() string { - switch rat { - case RoundActionPropose: - return "RoundActionPropose" - case RoundActionPrevote: - return "RoundActionPrevote" - case RoundActionPrecommit: - return "RoundActionPrecommit" - case RoundActionTryCommit: - return "RoundActionTryCommit" - case RoundActionCommit: - return "RoundActionCommit" - case RoundActionTryFinalize: - return "RoundActionTryFinalize" - default: - panic(Fmt("Unknown RoundAction %X", rat)) - } -} - -//----------------------------------------------------------------------------- - -type RoundAction struct { - Height uint // The block height for which consensus is reaching for. - Round uint // The round number at given height. - Action RoundActionType // Action to perform. -} - -func (ra RoundAction) String() string { - return Fmt("RoundAction{H:%v R:%v A:%v}", ra.Height, ra.Round, ra.Action) -} - //----------------------------------------------------------------------------- // Immutable when returned from ConsensusState.GetRoundState() type RoundState struct { - Height uint // Height we are working on - Round uint + Height int // Height we are working on + Round int Step RoundStepType StartTime time.Time - CommitTime time.Time // Time when +2/3 commits were found + CommitTime time.Time // Subjective time when +2/3 precommits for Block at Round were found Validators *sm.ValidatorSet Proposal *Proposal ProposalBlock *types.Block ProposalBlockParts *types.PartSet - ProposalPOL *POL - ProposalPOLParts *types.PartSet + LockedRound int LockedBlock *types.Block LockedBlockParts *types.PartSet - LockedPOL *POL // Rarely needed, so no LockedPOLParts. - Prevotes *VoteSet - Precommits *VoteSet - Commits *VoteSet - LastCommits *VoteSet + Votes *HeightVoteSet + LastCommit *VoteSet // Last precommits at Height-1 + LastValidators *sm.ValidatorSet } func (rs *RoundState) String() string { @@ -205,13 +256,11 @@ func (rs *RoundState) StringIndented(indent string) string { %s Validators: %v %s Proposal: %v %s ProposalBlock: %v %v -%s ProposalPOL: %v %v +%s LockedRound: %v %s LockedBlock: %v %v -%s LockedPOL: %v -%s Prevotes: %v -%s Precommits: %v -%s Commits: %v -%s LastCommits: %v +%s Votes: %v +%s LastCommit: %v +%s LastValidators: %v %s}`, indent, rs.Height, rs.Round, rs.Step, indent, rs.StartTime, @@ -219,13 +268,11 @@ func (rs *RoundState) StringIndented(indent string) string { indent, rs.Validators.StringIndented(indent+" "), indent, rs.Proposal, indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(), - indent, rs.ProposalPOLParts.StringShort(), rs.ProposalPOL.StringShort(), + indent, rs.LockedRound, indent, rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort(), - indent, rs.LockedPOL.StringShort(), - indent, rs.Prevotes.StringIndented(indent+" "), - indent, rs.Precommits.StringIndented(indent+" "), - indent, rs.Commits.StringIndented(indent+" "), - indent, rs.LastCommits.StringShort(), + indent, rs.Votes.StringIndented(indent+" "), + indent, rs.LastCommit.StringShort(), + indent, rs.LastValidators.StringIndented(indent+" "), indent) } @@ -245,15 +292,13 @@ type ConsensusState struct { blockStore *bc.BlockStore mempoolReactor *mempl.MempoolReactor privValidator *sm.PrivValidator - runActionCh chan RoundAction newStepCh chan *RoundState mtx sync.Mutex RoundState - state *sm.State // State until height-1. - stagedBlock *types.Block // Cache last staged block. - stagedState *sm.State // Cache result of staged block. - lastCommitVoteHeight uint // Last called commitVoteBlock() or saveCommitVoteBlock() on. + state *sm.State // State until height-1. + stagedBlock *types.Block // Cache last staged block. + stagedState *sm.State // Cache result of staged block. evsw events.Fireable evc *events.EventCache // set in stageBlock and passed into state @@ -264,13 +309,39 @@ func NewConsensusState(state *sm.State, blockStore *bc.BlockStore, mempoolReacto quit: make(chan struct{}), blockStore: blockStore, mempoolReactor: mempoolReactor, - runActionCh: make(chan RoundAction, 1), - newStepCh: make(chan *RoundState, 1), + newStepCh: make(chan *RoundState, 10), } cs.updateToState(state, true) + // Don't call scheduleRound0 yet. + // We do that upon Start(). + cs.maybeRebond() + cs.reconstructLastCommit(state) return cs } +// Reconstruct LastCommit from SeenValidation, which we saved along with the block, +// (which happens even before saving the state) +func (cs *ConsensusState) reconstructLastCommit(state *sm.State) { + if state.LastBlockHeight == 0 { + return + } + lastPrecommits := NewVoteSet(state.LastBlockHeight, 0, types.VoteTypePrecommit, state.LastBondedValidators) + seenValidation := cs.blockStore.LoadSeenValidation(state.LastBlockHeight) + for idx, precommit := range seenValidation.Precommits { + if precommit == nil { + continue + } + added, _, err := lastPrecommits.AddByIndex(idx, precommit) + if !added || err != nil { + panic(Fmt("Failed to reconstruct LastCommit: %v", err)) + } + } + if !lastPrecommits.HasTwoThirdsMajority() { + panic("Failed to reconstruct LastCommit: Does not have +2/3 maj") + } + cs.LastCommit = lastPrecommits +} + func (cs *ConsensusState) GetState() *sm.State { cs.mtx.Lock() defer cs.mtx.Unlock() @@ -295,201 +366,63 @@ func (cs *ConsensusState) NewStepCh() chan *RoundState { func (cs *ConsensusState) Start() { if atomic.CompareAndSwapUint32(&cs.started, 0, 1) { log.Info("Starting ConsensusState") - go cs.stepTransitionRoutine() + cs.scheduleRound0(cs.Height) } } -func (cs *ConsensusState) Stop() { - if atomic.CompareAndSwapUint32(&cs.stopped, 0, 1) { - log.Info("Stopping ConsensusState") - close(cs.quit) - } -} - -func (cs *ConsensusState) IsStopped() bool { - return atomic.LoadUint32(&cs.stopped) == 1 -} - -func (cs *ConsensusState) queueAction(ra RoundAction) { +// EnterNewRound(height, 0) at cs.StartTime. +func (cs *ConsensusState) scheduleRound0(height int) { + //log.Debug("scheduleRound0", "now", time.Now(), "startTime", cs.StartTime) + sleepDuration := cs.StartTime.Sub(time.Now()) go func() { - cs.runActionCh <- ra + if 0 < sleepDuration { + time.Sleep(sleepDuration) + } + cs.EnterNewRound(height, 0) }() } -// Source of all round state transitions (and votes). -func (cs *ConsensusState) stepTransitionRoutine() { - - // For clarity, all state transitions that happen after some timeout are here. - // Schedule the next action by pushing a RoundAction{} to cs.runActionCh. - scheduleNextAction := func() { - // NOTE: rs must be fetched in the same callstack as the caller. - rs := cs.getRoundState() - go func() { - // NOTE: We can push directly to runActionCh because - // we're running in a separate goroutine, which avoids deadlocks. - round, roundStartTime, RoundDuration, _, elapsedRatio := calcRoundInfo(rs.StartTime) - log.Debug("Scheduling next action", "height", rs.Height, "round", round, "step", rs.Step, "roundStartTime", roundStartTime, "elapsedRatio", elapsedRatio) - switch rs.Step { - case RoundStepNewHeight: - // We should run RoundActionPropose when rs.StartTime passes. - if elapsedRatio < 0 { - // startTime is in the future. - time.Sleep(time.Duration((-1.0 * elapsedRatio) * float64(RoundDuration))) - } - cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPropose} - case RoundStepNewRound: - // Pseudostep: Immediately goto propose. - cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPropose} - case RoundStepPropose: - // Wake up when it's time to vote. - time.Sleep(time.Duration((roundDeadlinePrevote - elapsedRatio) * float64(RoundDuration))) - cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPrevote} - case RoundStepPrevote: - // Wake up when it's time to precommit. - time.Sleep(time.Duration((roundDeadlinePrecommit - elapsedRatio) * float64(RoundDuration))) - cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionPrecommit} - case RoundStepPrecommit: - // Wake up when the round is over. - time.Sleep(time.Duration((1.0 - elapsedRatio) * float64(RoundDuration))) - cs.runActionCh <- RoundAction{rs.Height, rs.Round, RoundActionTryCommit} - case RoundStepCommit: - // There's nothing to scheudle, we're waiting for - // ProposalBlockParts.IsComplete() && - // Commits.HasTwoThirdsMajority() - panic("The next action from RoundStepCommit is not scheduled by time") - default: - panic("Should not happen") - } - }() - } - - if cs.getRoundState().Step < RoundStepCommit { - scheduleNextAction() - } else { - // Race condition with receipt of commits, maybe. - // We shouldn't have to schedule anything. - } - - // NOTE: All ConsensusState.RunAction*() calls come from here. - // Since only one routine calls them, it is safe to assume that - // the RoundState Height/Round/Step won't change concurrently. - // However, other fields like Proposal could change concurrent - // due to gossip routines. -ACTION_LOOP: - for { - var roundAction RoundAction - select { - case roundAction = <-cs.runActionCh: - case <-cs.quit: - return - } - - height, round, action := roundAction.Height, roundAction.Round, roundAction.Action - rs := cs.GetRoundState() - - // Continue if action is not relevant - if height != rs.Height { - log.Debug("Discarding round action: Height mismatch", "height", rs.Height, "roundAction", roundAction) - continue - } - // If action <= RoundActionPrecommit, the round must match too. - if action <= RoundActionPrecommit && round != rs.Round { - log.Debug("Discarding round action: Round mismatch", "round", rs.Round, "roundAction", roundAction) - continue - } - - log.Info("Running round action", "height", rs.Height, "round", rs.Round, "step", rs.Step, "roundAction", roundAction, "startTime", rs.StartTime) - - // Run action - switch action { - case RoundActionPropose: - if rs.Step != RoundStepNewHeight && rs.Step != RoundStepNewRound { - continue ACTION_LOOP - } - cs.RunActionPropose(rs.Height, rs.Round) - scheduleNextAction() - continue ACTION_LOOP - - case RoundActionPrevote: - if rs.Step >= RoundStepPrevote { - continue ACTION_LOOP - } - cs.RunActionPrevote(rs.Height, rs.Round) - scheduleNextAction() - continue ACTION_LOOP - - case RoundActionPrecommit: - if rs.Step >= RoundStepPrecommit { - continue ACTION_LOOP - } - cs.RunActionPrecommit(rs.Height, rs.Round) - scheduleNextAction() - continue ACTION_LOOP - - case RoundActionTryCommit: - if rs.Step >= RoundStepCommit { - continue ACTION_LOOP - } - if rs.Precommits.HasTwoThirdsMajority() { - // Enter RoundStepCommit and commit. - cs.RunActionCommit(rs.Height) - continue ACTION_LOOP - } else { - // Could not commit, move onto next round. - cs.SetupNewRound(rs.Height, rs.Round+1) - // cs.Step is now at RoundStepNewRound - scheduleNextAction() - continue ACTION_LOOP - } - - case RoundActionCommit: - if rs.Step >= RoundStepCommit { - continue ACTION_LOOP - } - // Enter RoundStepCommit and commit. - cs.RunActionCommit(rs.Height) - continue ACTION_LOOP - - case RoundActionTryFinalize: - if cs.TryFinalizeCommit(rs.Height) { - // Now at new height - // cs.Step is at RoundStepNewHeight or RoundStepNewRound. - // fire some events! - go func() { - newBlock := cs.blockStore.LoadBlock(cs.state.LastBlockHeight) - cs.evsw.FireEvent(types.EventStringNewBlock(), newBlock) - cs.evc.Flush() - }() - scheduleNextAction() - continue ACTION_LOOP - } else { - // do not schedule next action. - continue ACTION_LOOP - } - - default: - panic("Unknown action") - } - - // For clarity, ensure that all switch cases call "continue" - panic("Should not happen.") +func (cs *ConsensusState) Stop() { + if atomic.CompareAndSwapUint32(&cs.stopped, 0, 1) { + log.Info("Stopping ConsensusState") + close(cs.quit) } } // Updates ConsensusState and increments height to match that of state. -// If calculated round is greater than 0 (based on BlockTime or calculated StartTime) -// then also sets up the appropriate round, and cs.Step becomes RoundStepNewRound. -// Otherwise the round is 0 and cs.Step becomes RoundStepNewHeight. +// The round becomes 0 and cs.Step becomes RoundStepNewHeight. func (cs *ConsensusState) updateToState(state *sm.State, contiguous bool) { - // Sanity check state. - if contiguous && cs.Height > 0 && cs.Height != state.LastBlockHeight { + // SANITY CHECK + if contiguous && 0 < cs.Height && cs.Height != state.LastBlockHeight { panic(Fmt("updateToState() expected state height of %v but found %v", cs.Height, state.LastBlockHeight)) } + if cs.state != nil && cs.state.LastBlockHeight+1 != cs.Height { + // This might happen when someone else is mutating cs.state. + // Someone forgot to pass in state.Copy() somewhere?! + panic(Fmt("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v", + cs.state.LastBlockHeight+1, cs.Height)) + } + // END SANITY CHECK + + // If state isn't further out than cs.state, just ignore. + // This happens when SwitchToConsensus() is called in the reactor. + // We don't want to reset e.g. the Votes. + if cs.state != nil && (state.LastBlockHeight <= cs.state.LastBlockHeight) { + log.Info("Ignoring updateToState()", "newHeight", state.LastBlockHeight+1, "oldHeight", cs.state.LastBlockHeight+1) + return + } // Reset fields based on state. validators := state.BondedValidators height := state.LastBlockHeight + 1 // next desired block height + lastPrecommits := (*VoteSet)(nil) + if contiguous && cs.Votes != nil { + if !cs.Votes.Precommits(cs.Round).HasTwoThirdsMajority() { + panic("updateToState(state, true) called but last Precommit round didn't have +2/3") + } + lastPrecommits = cs.Votes.Precommits(cs.Round) + } // RoundState fields cs.Height = height @@ -497,86 +430,58 @@ func (cs *ConsensusState) updateToState(state *sm.State, contiguous bool) { cs.Step = RoundStepNewHeight if cs.CommitTime.IsZero() { // "Now" makes it easier to sync up dev nodes. - // We add newHeightDelta to allow transactions + // We add timeoutCommit to allow transactions // to be gathered for the first block. // And alternative solution that relies on clocks: - // cs.StartTime = state.LastBlockTime.Add(newHeightDelta) - cs.StartTime = time.Now().Add(newHeightDelta) + // cs.StartTime = state.LastBlockTime.Add(timeoutCommit) + cs.StartTime = time.Now().Add(timeoutCommit) } else { - cs.StartTime = cs.CommitTime.Add(newHeightDelta) + cs.StartTime = cs.CommitTime.Add(timeoutCommit) } cs.CommitTime = time.Time{} cs.Validators = validators cs.Proposal = nil cs.ProposalBlock = nil cs.ProposalBlockParts = nil - cs.ProposalPOL = nil - cs.ProposalPOLParts = nil + cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil - cs.LockedPOL = nil - cs.Prevotes = NewVoteSet(height, 0, types.VoteTypePrevote, validators) - cs.Precommits = NewVoteSet(height, 0, types.VoteTypePrecommit, validators) - cs.LastCommits = cs.Commits - cs.Commits = NewVoteSet(height, 0, types.VoteTypeCommit, validators) + cs.Votes = NewHeightVoteSet(height, validators) + cs.LastCommit = lastPrecommits + cs.LastValidators = state.LastBondedValidators cs.state = state cs.stagedBlock = nil cs.stagedState = nil - // Update the round if we need to. - round := calcRound(cs.StartTime) - if round > 0 { - cs.setupNewRound(round) - } + // Finally, broadcast RoundState + cs.newStepCh <- cs.getRoundState() +} - // If we've timed out, then send rebond tx. - if cs.privValidator != nil && cs.state.UnbondingValidators.HasAddress(cs.privValidator.Address) { - rebondTx := &types.RebondTx{ - Address: cs.privValidator.Address, - Height: cs.Height, - } - err := cs.privValidator.SignRebondTx(cs.state.ChainID, rebondTx) - if err == nil { - err := cs.mempoolReactor.BroadcastTx(rebondTx) - if err != nil { - log.Error("Failed to broadcast RebondTx", - "height", cs.Height, "round", cs.Round, "tx", rebondTx, "error", err) - } else { - log.Info("Signed and broadcast RebondTx", - "height", cs.Height, "round", cs.Round, "tx", rebondTx) - } +// If we're unbonded, broadcast RebondTx. +func (cs *ConsensusState) maybeRebond() { + if cs.privValidator == nil || !cs.state.UnbondingValidators.HasAddress(cs.privValidator.Address) { + return + } + rebondTx := &types.RebondTx{ + Address: cs.privValidator.Address, + Height: cs.Height, + } + err := cs.privValidator.SignRebondTx(cs.state.ChainID, rebondTx) + if err == nil { + err := cs.mempoolReactor.BroadcastTx(rebondTx) + if err != nil { + log.Error("Failed to broadcast RebondTx", + "height", cs.Height, "round", cs.Round, "tx", rebondTx, "error", err) } else { - log.Warn("Error signing RebondTx", "height", cs.Height, "round", cs.Round, "tx", rebondTx, "error", err) + log.Info("Signed and broadcast RebondTx", + "height", cs.Height, "round", cs.Round, "tx", rebondTx) } + } else { + log.Warn("Error signing RebondTx", "height", cs.Height, "round", cs.Round, "tx", rebondTx, "error", err) } } -// After the call cs.Step becomes RoundStepNewRound. -func (cs *ConsensusState) setupNewRound(round uint) { - // Sanity check - if round == 0 { - panic("setupNewRound() should never be called for round 0") - } - - // Increment all the way to round. - validators := cs.Validators.Copy() - validators.IncrementAccum(round - cs.Round) - - cs.Round = round - cs.Step = RoundStepNewRound - cs.Validators = validators - cs.Proposal = nil - cs.ProposalBlock = nil - cs.ProposalBlockParts = nil - cs.ProposalPOL = nil - cs.ProposalPOLParts = nil - cs.Prevotes = NewVoteSet(cs.Height, round, types.VoteTypePrevote, validators) - cs.Prevotes.AddFromCommits(cs.Commits) - cs.Precommits = NewVoteSet(cs.Height, round, types.VoteTypePrecommit, validators) - cs.Precommits.AddFromCommits(cs.Commits) -} - func (cs *ConsensusState) SetPrivValidator(priv *sm.PrivValidator) { cs.mtx.Lock() defer cs.mtx.Unlock() @@ -585,147 +490,215 @@ func (cs *ConsensusState) SetPrivValidator(priv *sm.PrivValidator) { //----------------------------------------------------------------------------- -// Set up the round to desired round and set step to RoundStepNewRound -func (cs *ConsensusState) SetupNewRound(height uint, desiredRound uint) bool { +// Enter: +2/3 precommits for nil at (height,round-1) +// Enter: `timeoutPrecommits` after any +2/3 precommits from (height,round-1) +// Enter: `startTime = commitTime+timeoutCommit` from NewHeight(height) +// NOTE: cs.StartTime was already set for height. +func (cs *ConsensusState) EnterNewRound(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() - if cs.Height != height { - return false + if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != RoundStepNewHeight) { + log.Debug(Fmt("EnterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + return } - if desiredRound <= cs.Round { - return false + if now := time.Now(); cs.StartTime.After(now) { + log.Warn("Need to set a buffer and log.Warn() here for sanity.", "startTime", cs.StartTime, "now", now) } - cs.setupNewRound(desiredRound) - // c.Step is now RoundStepNewRound - cs.newStepCh <- cs.getRoundState() - return true + log.Info(Fmt("EnterNewRound(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + + // Increment validators if necessary + validators := cs.Validators + if cs.Round < round { + validators = validators.Copy() + validators.IncrementAccum(round - cs.Round) + } + + // Setup new round + cs.Round = round + cs.Step = RoundStepNewRound + cs.Validators = validators + if round == 0 { + // We've already reset these upon new height, + // and meanwhile we might have received a proposal + // for round 0. + } else { + cs.Proposal = nil + cs.ProposalBlock = nil + cs.ProposalBlockParts = nil + } + cs.Votes.SetRound(round + 1) // also track next round (round+1) to allow round-skipping + + // Immediately go to EnterPropose. + go cs.EnterPropose(height, round) } -func (cs *ConsensusState) RunActionPropose(height uint, round uint) { +// Enter: from NewRound(height,round). +func (cs *ConsensusState) EnterPropose(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() - if cs.Height != height || cs.Round != round { + if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPropose <= cs.Step) { + log.Debug(Fmt("EnterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } + log.Debug(Fmt("EnterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + defer func() { + // Done EnterPropose: + cs.Round = round cs.Step = RoundStepPropose cs.newStepCh <- cs.getRoundState() + + // If we already have the proposal + POL, then goto Prevote + if cs.isProposalComplete() { + go cs.EnterPrevote(height, cs.Round) + } + }() + + // This step times out after `timeoutPropose` + go func() { + time.Sleep(timeoutPropose) + cs.EnterPrevote(height, round) }() - // Nothing to do if it's not our turn. + // Nothing more to do if we're not a validator if cs.privValidator == nil { return } + if !bytes.Equal(cs.Validators.Proposer().Address, cs.privValidator.Address) { - log.Debug("Not our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.privValidator) - return + log.Debug("EnterPropose: Not our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.privValidator) } else { - log.Debug("Our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.privValidator) + log.Debug("EnterPropose: Our turn to propose", "proposer", cs.Validators.Proposer().Address, "privValidator", cs.privValidator) + cs.decideProposal(height, round) } +} +// Decides on the next proposal and sets them onto cs.Proposal* +func (cs *ConsensusState) decideProposal(height int, round int) { var block *types.Block var blockParts *types.PartSet - var pol *POL - var polParts *types.PartSet - // Decide on block and POL + // Decide on block if cs.LockedBlock != nil { // If we're locked onto a block, just choose that. - block = cs.LockedBlock - blockParts = cs.LockedBlockParts - pol = cs.LockedPOL + block, blockParts = cs.LockedBlock, cs.LockedBlockParts } else { - // Otherwise we should create a new proposal. - var validation *types.Validation - if cs.Height == 1 { - // We're creating a proposal for the first block. - // The validation is empty. - validation = &types.Validation{} - } else if cs.LastCommits.HasTwoThirdsMajority() { - // Make the validation from LastCommits - validation = cs.LastCommits.MakeValidation() - } else { - // Upon reboot, we may have to use SeenValidation - validation = cs.blockStore.LoadSeenValidation(height - 1) - if validation == nil { - // We just don't have any validation for the previous block - log.Debug("Cannot propose anything: No validation for the previous block.") - return - } - } - txs := cs.mempoolReactor.Mempool.GetProposalTxs() - block = &types.Block{ - Header: &types.Header{ - ChainID: cs.state.ChainID, - Height: cs.Height, - Time: time.Now(), - Fees: 0, // TODO fees - NumTxs: uint(len(txs)), - LastBlockHash: cs.state.LastBlockHash, - LastBlockParts: cs.state.LastBlockParts, - StateHash: nil, // Will set afterwards. - }, - Validation: validation, - Data: &types.Data{ - Txs: txs, - }, - } - - // Set the types.Header.StateHash. - err := cs.state.SetBlockStateHash(block) - if err != nil { - log.Error("Error setting state hash", "error", err) - return - } - - blockParts = block.MakePartSet() - pol = cs.LockedPOL // If exists, is a PoUnlock. - } - - if pol != nil { - polParts = pol.MakePartSet() + // Create a new proposal block from state/txs from the mempool. + block, blockParts = cs.createProposalBlock() } // Make proposal - proposal := NewProposal(cs.Height, cs.Round, blockParts.Header(), polParts.Header()) + proposal := NewProposal(height, round, blockParts.Header(), cs.Votes.POLRound()) err := cs.privValidator.SignProposal(cs.state.ChainID, proposal) if err == nil { - log.Info("Signed and set proposal", "height", cs.Height, "round", cs.Round, "proposal", proposal) + log.Info("Signed and set proposal", "height", height, "round", round, "proposal", proposal) log.Debug(Fmt("Signed and set proposal block: %v", block)) // Set fields cs.Proposal = proposal cs.ProposalBlock = block cs.ProposalBlockParts = blockParts - cs.ProposalPOL = pol - cs.ProposalPOLParts = polParts } else { - log.Warn("Error signing proposal", "height", cs.Height, "round", cs.Round, "error", err) + log.Warn("EnterPropose: Error signing proposal", "height", height, "round", round, "error", err) } + } -// Prevote for LockedBlock if we're locked, or ProposealBlock if valid. +// Returns true if the proposal block is complete && +// (if POLRound was proposed, we have +2/3 prevotes from there). +func (cs *ConsensusState) isProposalComplete() bool { + if cs.Proposal == nil || cs.ProposalBlock == nil { + return false + } + if cs.Proposal.POLRound < 0 { + return true + } else { + return cs.Votes.Prevotes(cs.Proposal.POLRound).HasTwoThirdsMajority() + } +} + +// Create the next block to propose and return it. +// NOTE: make it side-effect free for clarity. +func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts *types.PartSet) { + var validation *types.Validation + if cs.Height == 1 { + // We're creating a proposal for the first block. + // The validation is empty, but not nil. + validation = &types.Validation{} + } else if cs.LastCommit.HasTwoThirdsMajority() { + // Make the validation from LastCommit + validation = cs.LastCommit.MakeValidation() + } else { + // This shouldn't happen. + log.Error("EnterPropose: Cannot propose anything: No validation for the previous block.") + return + } + txs := cs.mempoolReactor.Mempool.GetProposalTxs() + block = &types.Block{ + Header: &types.Header{ + ChainID: cs.state.ChainID, + Height: cs.Height, + Time: time.Now(), + Fees: 0, // TODO fees + NumTxs: len(txs), + LastBlockHash: cs.state.LastBlockHash, + LastBlockParts: cs.state.LastBlockParts, + StateHash: nil, // Will set afterwards. + }, + LastValidation: validation, + Data: &types.Data{ + Txs: txs, + }, + } + + // Set the block.Header.StateHash. + err := cs.state.ComputeBlockStateHash(block) + if err != nil { + log.Error("EnterPropose: Error setting state hash", "error", err) + return + } + + blockParts = block.MakePartSet() + return block, blockParts +} + +// Enter: `timeoutPropose` after entering Propose. +// Enter: proposal block and POL is ready. +// Enter: any +2/3 prevotes for future round. +// Prevote for LockedBlock if we're locked, or ProposalBlock if valid. // Otherwise vote nil. -func (cs *ConsensusState) RunActionPrevote(height uint, round uint) { +func (cs *ConsensusState) EnterPrevote(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() - if cs.Height != height || cs.Round != round { - panic(Fmt("RunActionPrevote(%v/%v), expected %v/%v", height, round, cs.Height, cs.Round)) + if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrevote <= cs.Step) { + log.Debug(Fmt("EnterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + return } - defer func() { - cs.Step = RoundStepPrevote - cs.newStepCh <- cs.getRoundState() - }() + log.Debug(Fmt("EnterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + + // Sign and broadcast vote as necessary + cs.doPrevote(height, round) + // Done EnterPrevote: + cs.Round = round + cs.Step = RoundStepPrevote + cs.newStepCh <- cs.getRoundState() + /* This isn't necessary because addVote() does it for us. + if cs.Votes.Prevotes(round).HasTwoThirdsAny() { + go cs.EnterPrevoteWait(height, round) + }*/ +} + +func (cs *ConsensusState) doPrevote(height int, round int) { // If a block is locked, prevote that. if cs.LockedBlock != nil { - log.Debug("Block was locked") + log.Debug("EnterPrevote: Block was locked") cs.signAddVote(types.VoteTypePrevote, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header()) return } // If ProposalBlock is nil, prevote nil. if cs.ProposalBlock == nil { - log.Warn("ProposalBlock is nil") + log.Warn("EnterPrevote: ProposalBlock is nil") cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{}) return } @@ -734,7 +707,7 @@ func (cs *ConsensusState) RunActionPrevote(height uint, round uint) { err := cs.stageBlock(cs.ProposalBlock, cs.ProposalBlockParts) if err != nil { // ProposalBlock is invalid, prevote nil. - log.Warn("ProposalBlock is invalid", "error", err) + log.Warn("EnterPrevote: ProposalBlock is invalid", "error", err) cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{}) return } @@ -744,107 +717,194 @@ func (cs *ConsensusState) RunActionPrevote(height uint, round uint) { return } -// 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 uint, round uint) { +// Enter: any +2/3 prevotes at next round. +func (cs *ConsensusState) EnterPrevoteWait(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() - if cs.Height != height || cs.Round != round { - panic(Fmt("RunActionPrecommit(%v/%v), expected %v/%v", height, round, cs.Height, cs.Round)) + if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrevoteWait <= cs.Step) { + log.Debug(Fmt("EnterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + return } + if !cs.Votes.Prevotes(round).HasTwoThirdsAny() { + panic(Fmt("EnterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round)) + } + log.Debug(Fmt("EnterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + + // Done EnterPrevoteWait: + cs.Round = round + cs.Step = RoundStepPrevoteWait + cs.newStepCh <- cs.getRoundState() + + // After `timeoutPrevote0+timeoutPrevoteDelta*round`, EnterPrecommit() + go func() { + time.Sleep(timeoutPrevote0 + timeoutPrevote0*time.Duration(round)) + cs.EnterPrecommit(height, round) + }() +} + +// Enter: +2/3 precomits for block or nil. +// Enter: `timeoutPrevote` after any +2/3 prevotes. +// Enter: any +2/3 precommits for next round. +// Lock & precommit the ProposalBlock if we have enough prevotes for it, +// else, unlock an existing lock and precommit nil if +2/3 of prevotes were nil, +// else, precommit locked block or nil otherwise. +func (cs *ConsensusState) EnterPrecommit(height int, round int) { + cs.mtx.Lock() + defer cs.mtx.Unlock() + if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrecommit <= cs.Step) { + log.Debug(Fmt("EnterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + return + } + log.Debug(Fmt("EnterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) + defer func() { + // Done EnterPrecommit: + cs.Round = round cs.Step = RoundStepPrecommit cs.newStepCh <- cs.getRoundState() + /* This isn't necessary because addVote() does it for us. + if cs.Votes.Precommits(round).HasTwoThirdsAny() { + go cs.EnterPrecommitWait(height, round) + }*/ }() - hash, partsHeader, ok := cs.Prevotes.TwoThirdsMajority() + hash, partsHeader, ok := cs.Votes.Prevotes(round).TwoThirdsMajority() + + // If we don't have two thirds of prevotes, just precommit locked block or nil if !ok { - // If we don't have two thirds of prevotes, - // don't do anything at all. - log.Info("Insufficient prevotes for precommit") + if cs.LockedBlock != nil { + log.Debug("EnterPrecommit: No +2/3 prevotes during EnterPrecommit. Precommitting lock.") + cs.signAddVote(types.VoteTypePrecommit, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header()) + } else { + log.Debug("EnterPrecommit: No +2/3 prevotes during EnterPrecommit. Precommitting nil.") + cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) + } return } - // Remember this POL. (hash may be nil) - cs.LockedPOL = cs.Prevotes.MakePOL() - - // If +2/3 prevoted nil. Just unlock. + // +2/3 prevoted nil. Unlock and precommit nil. if len(hash) == 0 { if cs.LockedBlock == nil { - log.Info("+2/3 prevoted for nil.") + log.Debug("EnterPrecommit: +2/3 prevoted for nil.") } else { - log.Info("+2/3 prevoted for nil. Unlocking") + log.Debug("EnterPrecommit: +2/3 prevoted for nil. Unlocking") + cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil } + cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) return } + // At this point, +2/3 prevoted for a particular block. + // If +2/3 prevoted for already locked block, precommit it. if cs.LockedBlock.HashesTo(hash) { - log.Info("+2/3 prevoted locked block.") + log.Debug("EnterPrecommit: +2/3 prevoted locked block.") cs.signAddVote(types.VoteTypePrecommit, hash, partsHeader) return } - // If +2/3 prevoted for cs.ProposalBlock, lock it and precommit it. - if !cs.ProposalBlock.HashesTo(hash) { - log.Warn("Proposal does not hash to +2/3 prevotes") - // We don't have the block that validators prevoted. - // Unlock if we're locked. - cs.LockedBlock = nil - cs.LockedBlockParts = nil + // If +2/3 prevoted for proposal block, stage and precommit it + if cs.ProposalBlock.HashesTo(hash) { + log.Debug("EnterPrecommit: +2/3 prevoted proposal block.") + // Validate the block. + if err := cs.stageBlock(cs.ProposalBlock, cs.ProposalBlockParts); err != nil { + panic(Fmt("EnterPrecommit: +2/3 prevoted for an invalid block: %v", err)) + } + cs.LockedRound = round + cs.LockedBlock = cs.ProposalBlock + cs.LockedBlockParts = cs.ProposalBlockParts + cs.signAddVote(types.VoteTypePrecommit, hash, partsHeader) return } - // Validate the block. - if err := cs.stageBlock(cs.ProposalBlock, cs.ProposalBlockParts); err != nil { - // Prevent zombies. - log.Warn("+2/3 prevoted for an invalid block", "error", err) + // Otherwise, we need to fetch the +2/3 prevoted block. + // Unlock and precommit nil. + // The +2/3 prevotes for this round is the POL for our unlock. + if cs.Votes.POLRound() < round { + panic(Fmt("This POLRound shold be %v but got %", round, cs.Votes.POLRound())) + } + cs.LockedRound = 0 + cs.LockedBlock = nil + cs.LockedBlockParts = nil + if !cs.ProposalBlockParts.HasHeader(partsHeader) { + cs.ProposalBlock = nil + cs.ProposalBlockParts = types.NewPartSetFromHeader(partsHeader) + } + cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) + return +} + +// Enter: any +2/3 precommits for next round. +func (cs *ConsensusState) EnterPrecommitWait(height int, round int) { + cs.mtx.Lock() + defer cs.mtx.Unlock() + if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrecommitWait <= cs.Step) { + log.Debug(Fmt("EnterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } + if !cs.Votes.Precommits(round).HasTwoThirdsAny() { + panic(Fmt("EnterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round)) + } + log.Debug(Fmt("EnterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) - cs.LockedBlock = cs.ProposalBlock - cs.LockedBlockParts = cs.ProposalBlockParts - cs.signAddVote(types.VoteTypePrecommit, hash, partsHeader) - return + // Done EnterPrecommitWait: + cs.Round = round + cs.Step = RoundStepPrecommitWait + cs.newStepCh <- cs.getRoundState() + + // After `timeoutPrecommit0+timeoutPrecommitDelta*round`, EnterNewRound() + go func() { + time.Sleep(timeoutPrecommit0 + timeoutPrecommitDelta*time.Duration(round)) + // If we have +2/3 of precommits for a particular block (or nil), + // we already entered commit (or the next round). + // So just try to transition to the next round, + // which is what we'd do otherwise. + cs.EnterNewRound(height, round+1) + }() } -// Enter commit step. See the diagram for details. -// 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) { +// Enter: +2/3 precommits for block +func (cs *ConsensusState) EnterCommit(height int) { cs.mtx.Lock() defer cs.mtx.Unlock() - if cs.Height != height { - panic(Fmt("RunActionCommit(%v), expected %v", height, cs.Height)) + if cs.Height != height || RoundStepCommit <= cs.Step { + log.Debug(Fmt("EnterCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step)) + return } + log.Debug(Fmt("EnterCommit(%v). Current: %v/%v/%v", height, cs.Height, cs.Round, cs.Step)) + defer func() { + // Done Entercommit: + // keep ca.Round the same, it points to the right Precommits set. cs.Step = RoundStepCommit cs.newStepCh <- cs.getRoundState() + + // Maybe finalize immediately. + cs.tryFinalizeCommit(height) }() - // Sanity check. - // There are two ways to enter: - // 1. +2/3 precommits at the end of RoundStepPrecommit - // 2. +2/3 commits at any time - hash, partsHeader, ok := cs.Precommits.TwoThirdsMajority() + // SANITY CHECK + hash, partsHeader, ok := cs.Votes.Precommits(cs.Round).TwoThirdsMajority() if !ok { - hash, partsHeader, ok = cs.Commits.TwoThirdsMajority() - if !ok { - panic("RunActionCommit() expects +2/3 precommits or commits") - } + panic("RunActionCommit() expects +2/3 precommits") } + // END SANITY CHECK - // Clear the Locked* fields and use cs.Proposed* + // The Locked* fields no longer matter. + // Move them over to ProposalBlock if they match the commit hash, + // otherwise they can now be cleared. if cs.LockedBlock.HashesTo(hash) { cs.ProposalBlock = cs.LockedBlock cs.ProposalBlockParts = cs.LockedBlockParts + cs.LockedRound = 0 + cs.LockedBlock = nil + cs.LockedBlockParts = nil + } else { + cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil - cs.LockedPOL = nil } // If we don't have the block being committed, set up to get it. @@ -854,67 +914,73 @@ func (cs *ConsensusState) RunActionCommit(height uint) { // Set up ProposalBlockParts and keep waiting. cs.ProposalBlock = nil cs.ProposalBlockParts = types.NewPartSetFromHeader(partsHeader) - } else { // We just need to keep waiting. } - } else { - // We have the block, so sign a Commit-vote. - cs.commitVoteBlock(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}) +// If we have the block AND +2/3 commits for it, finalize. +func (cs *ConsensusState) tryFinalizeCommit(height int) { + // SANITY CHECK + if cs.Height != height { + panic(Fmt("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height)) } + // END SANITY CHECK + hash, _, ok := cs.Votes.Precommits(cs.Round).TwoThirdsMajority() + if !ok || len(hash) == 0 { + return // There was no +2/3 majority, or +2/3 was for <nil>. + } + if !cs.ProposalBlock.HashesTo(hash) { + return // We don't have the commit block. + } + go cs.FinalizeCommit(height) } -// Returns true if Finalize happened, which increments height && sets -// the step to RoundStepNewHeight (or RoundStepNewRound, but probably not). -func (cs *ConsensusState) TryFinalizeCommit(height uint) bool { +// Increment height and goto RoundStepNewHeight +func (cs *ConsensusState) FinalizeCommit(height int) { cs.mtx.Lock() defer cs.mtx.Unlock() - if cs.Height != height { - panic(Fmt("TryFinalizeCommit(%v), expected %v", height, cs.Height)) + if cs.Height != height || cs.Step != RoundStepCommit { + log.Debug(Fmt("FinalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step)) + return } - if cs.Step == RoundStepCommit && - cs.Commits.HasTwoThirdsMajority() && - cs.ProposalBlockParts.IsComplete() { + hash, header, ok := cs.Votes.Precommits(cs.Round).TwoThirdsMajority() - // Sanity check - if cs.ProposalBlock == nil { - panic(Fmt("Expected ProposalBlock to exist")) - } - hash, header, _ := cs.Commits.TwoThirdsMajority() - if !cs.ProposalBlock.HashesTo(hash) { - // XXX See: https://github.com/tendermint/tendermint/issues/44 - panic(Fmt("Expected ProposalBlock to hash to commit hash. Expected %X, got %X", hash, cs.ProposalBlock.Hash())) - } - if !cs.ProposalBlockParts.HasHeader(header) { - panic(Fmt("Expected ProposalBlockParts header to be commit header")) - } - - err := cs.stageBlock(cs.ProposalBlock, cs.ProposalBlockParts) - if err == nil { - log.Debug(Fmt("Finalizing commit of block: %v", cs.ProposalBlock)) - // We have the block, so save/stage/sign-commit-vote. - cs.saveCommitVoteBlock(cs.ProposalBlock, cs.ProposalBlockParts, cs.Commits) - // Increment height. - cs.updateToState(cs.stagedState, true) - // cs.Step is now RoundStepNewHeight or RoundStepNewRound - cs.newStepCh <- cs.getRoundState() - return true - } else { - // Prevent zombies. - // TODO: Does this ever happen? - panic(Fmt("+2/3 committed an invalid block: %v", err)) - } + // SANITY CHECK + if !ok { + panic(Fmt("Cannot FinalizeCommit, commit does not have two thirds majority")) } - return false + if !cs.ProposalBlockParts.HasHeader(header) { + panic(Fmt("Expected ProposalBlockParts header to be commit header")) + } + if !cs.ProposalBlock.HashesTo(hash) { + panic(Fmt("Cannot FinalizeCommit, ProposalBlock does not hash to commit hash")) + } + if err := cs.stageBlock(cs.ProposalBlock, cs.ProposalBlockParts); err != nil { + panic(Fmt("+2/3 committed an invalid block: %v", err)) + } + // END SANITY CHECK + + log.Debug(Fmt("Finalizing commit of block: %v", cs.ProposalBlock)) + // We have the block, so stage/save/commit-vote. + cs.saveBlock(cs.ProposalBlock, cs.ProposalBlockParts, cs.Votes.Precommits(cs.Round)) + // Increment height. + cs.updateToState(cs.stagedState, true) + // cs.StartTime is already set. + // Schedule Round0 to start soon. + go cs.scheduleRound0(height + 1) + // If we're unbonded, broadcast RebondTx. + cs.maybeRebond() + + // By here, + // * cs.Height has been increment to height+1 + // * cs.Step is now RoundStepNewHeight + // * cs.StartTime is set to when we will start round0. + return } //----------------------------------------------------------------------------- @@ -934,24 +1000,28 @@ func (cs *ConsensusState) SetProposal(proposal *Proposal) error { } // We don't care about the proposal if we're already in RoundStepCommit. - if cs.Step == RoundStepCommit { + if RoundStepCommit <= cs.Step { return nil } + // Verify POLRound, which must be -1 or between 0 and proposal.Round exclusive. + if proposal.POLRound != -1 && + (proposal.POLRound < 0 || proposal.Round <= proposal.POLRound) { + return ErrInvalidProposalPOLRound + } + // Verify signature if !cs.Validators.Proposer().PubKey.VerifyBytes(account.SignBytes(cs.state.ChainID, proposal), proposal.Signature) { return ErrInvalidProposalSignature } cs.Proposal = proposal - cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockParts) - cs.ProposalPOLParts = types.NewPartSetFromHeader(proposal.POLParts) + cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockPartsHeader) return nil } // NOTE: block is not necessarily valid. -// NOTE: This function may increment the height. -func (cs *ConsensusState) AddProposalBlockPart(height uint, round uint, part *types.Part) (added bool, err error) { +func (cs *ConsensusState) AddProposalBlockPart(height int, part *types.Part) (added bool, err error) { cs.mtx.Lock() defer cs.mtx.Unlock() @@ -970,105 +1040,117 @@ func (cs *ConsensusState) AddProposalBlockPart(height uint, round uint, part *ty return added, err } if added && cs.ProposalBlockParts.IsComplete() { + // Added and completed! var n int64 var err error cs.ProposalBlock = binary.ReadBinary(&types.Block{}, cs.ProposalBlockParts.GetReader(), &n, &err).(*types.Block) log.Debug("Received complete proposal", "hash", cs.ProposalBlock.Hash()) - // If we're already in the commit step, try to finalize round. - if cs.Step == RoundStepCommit { - cs.queueAction(RoundAction{cs.Height, cs.Round, RoundActionTryFinalize}) + if cs.Step == RoundStepPropose && cs.isProposalComplete() { + // Move onto the next step + go cs.EnterPrevote(height, cs.Round) + } else if cs.Step == RoundStepCommit { + // If we're waiting on the proposal block... + cs.tryFinalizeCommit(height) } - // XXX If POL is valid, consider unlocking. - return true, err - } - return true, nil -} - -// NOTE: POL is not necessarily valid. -func (cs *ConsensusState) AddProposalPOLPart(height uint, round uint, part *types.Part) (added bool, err error) { - cs.mtx.Lock() - defer cs.mtx.Unlock() - - if cs.Height != height || cs.Round != round { - return false, nil - } - - // We're not expecting a POL part. - if cs.ProposalPOLParts == nil { - return false, nil // TODO: bad peer? Return error? - } - - added, err = cs.ProposalPOLParts.AddPart(part) - if err != nil { - return added, err - } - if added && cs.ProposalPOLParts.IsComplete() { - var n int64 - var err error - cs.ProposalPOL = binary.ReadBinary(&POL{}, cs.ProposalPOLParts.GetReader(), &n, &err).(*POL) return true, err } - return true, nil + return added, nil } -func (cs *ConsensusState) AddVote(address []byte, vote *types.Vote) (added bool, index uint, err error) { +func (cs *ConsensusState) AddVote(address []byte, vote *types.Vote, peerKey string) (added bool, index int, err error) { cs.mtx.Lock() defer cs.mtx.Unlock() - return cs.addVote(address, vote) + return cs.addVote(address, vote, peerKey) } //----------------------------------------------------------------------------- -func (cs *ConsensusState) addVote(address []byte, vote *types.Vote) (added bool, index uint, err error) { - switch vote.Type { - case types.VoteTypePrevote: - // Prevotes checks for height+round match. - added, index, err = cs.Prevotes.Add(address, vote) +func (cs *ConsensusState) addVote(address []byte, vote *types.Vote, peerKey string) (added bool, index int, err error) { + log.Debug("addVote", "voteHeight", vote.Height, "voteType", vote.Type, "csHeight", cs.Height) + + // A precommit for the previous height? + if vote.Height+1 == cs.Height && vote.Type == types.VoteTypePrecommit { + added, index, err = cs.LastCommit.AddByAddress(address, vote) if added { - log.Debug(Fmt("Added prevote: %v", cs.Prevotes.StringShort())) + log.Debug(Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort())) } return - case types.VoteTypePrecommit: - // Precommits checks for height+round match. - added, index, err = cs.Precommits.Add(address, vote) + } + + // A prevote/precommit for this height? + if vote.Height == cs.Height { + height := cs.Height + added, index, err = cs.Votes.AddByAddress(address, vote, peerKey) if added { - log.Debug(Fmt("Added precommit: %v", cs.Precommits.StringShort())) - } - return - case types.VoteTypeCommit: - if vote.Height == cs.Height { - // No need to check if vote.Round < cs.Round ... - // Prevotes && Precommits already checks that. - 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(Fmt("Set CommitTime to %v", cs.CommitTime)) - if cs.Step < RoundStepCommit { - cs.queueAction(RoundAction{cs.Height, cs.Round, RoundActionCommit}) - } else { - cs.queueAction(RoundAction{cs.Height, cs.Round, RoundActionTryFinalize}) + switch vote.Type { + case types.VoteTypePrevote: + prevotes := cs.Votes.Prevotes(vote.Round) + log.Debug(Fmt("Added to prevotes: %v", prevotes.StringShort())) + // First, unlock if prevotes is a valid POL. + // >> lockRound < POLRound <= unlockOrChangeLockRound (see spec) + // NOTE: If (lockRound < POLRound) but !(POLRound <= unlockOrChangeLockRound), + // we'll still EnterNewRound(H,vote.R) and EnterPrecommit(H,vote.R) to process it + // there. + if (cs.LockedBlock != nil) && (cs.LockedRound < vote.Round) && (vote.Round <= cs.Round) { + hash, _, ok := prevotes.TwoThirdsMajority() + if ok && !cs.LockedBlock.HashesTo(hash) { + log.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round) + cs.LockedRound = 0 + cs.LockedBlock = nil + cs.LockedBlockParts = nil + } } + if cs.Round <= vote.Round && prevotes.HasTwoThirdsAny() { + // Round-skip over to PrevoteWait or goto Precommit. + go func() { + if cs.Round < vote.Round { + cs.EnterNewRound(height, vote.Round) + } + if prevotes.HasTwoThirdsMajority() { + cs.EnterPrecommit(height, vote.Round) + } else { + cs.EnterPrevote(height, vote.Round) + cs.EnterPrevoteWait(height, vote.Round) + } + }() + } else if cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round { + // If the proposal is now complete, enter prevote of cs.Round. + if cs.isProposalComplete() { + go cs.EnterPrevote(height, cs.Round) + } + } + case types.VoteTypePrecommit: + precommits := cs.Votes.Precommits(vote.Round) + log.Debug(Fmt("Added to precommit: %v", precommits.StringShort())) + if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { + go func() { + hash, _, ok := precommits.TwoThirdsMajority() + if ok && len(hash) == 0 { + cs.EnterNewRound(height, vote.Round+1) + return + } else if cs.Round < vote.Round { + cs.EnterNewRound(height, vote.Round) + } + if ok { + cs.EnterCommit(height) + } else { + cs.EnterPrecommit(height, vote.Round) + cs.EnterPrecommitWait(height, vote.Round) + } + }() + } + default: + panic(Fmt("Unexpected vote type %X", vote.Type)) // Should not happen. } - if added { - log.Debug(Fmt("Added commit: %v\nprecommits: %v\nprevotes: %v", - cs.Commits.StringShort(), - cs.Precommits.StringShort(), - cs.Prevotes.StringShort())) - } - return } - if vote.Height+1 == cs.Height { - added, index, err = cs.LastCommits.Add(address, vote) - log.Debug(Fmt("Added lastCommits: %v", cs.LastCommits.StringShort())) - return - } - return false, 0, nil - default: - panic("Unknown vote type") + // Either duplicate, or error upon cs.Votes.AddByAddress() + return } + + // Height mismatch, bad peer? + log.Debug("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height) + return } func (cs *ConsensusState) stageBlock(block *types.Block, blockParts *types.PartSet) error { @@ -1113,8 +1195,8 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part } err := cs.privValidator.SignVote(cs.state.ChainID, vote) if err == nil { - log.Info("Signed and added vote", "height", cs.Height, "round", cs.Round, "vote", vote) - cs.addVote(cs.privValidator.Address, vote) + _, _, err := cs.addVote(cs.privValidator.Address, vote, "") + log.Info("Signed and added vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err) return vote } else { log.Warn("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "error", err) @@ -1122,34 +1204,12 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part } } -// sign a Commit-Vote -func (cs *ConsensusState) commitVoteBlock(block *types.Block, blockParts *types.PartSet) { - - // The proposal must be valid. - if err := cs.stageBlock(block, blockParts); err != nil { - // Prevent zombies. - log.Warn("commitVoteBlock() an invalid block", "error", err) - return - } - - // Commit-vote. - if cs.lastCommitVoteHeight < block.Height { - cs.signAddVote(types.VoteTypeCommit, block.Hash(), blockParts.Header()) - cs.lastCommitVoteHeight = block.Height - } else { - log.Error("Duplicate commitVoteBlock() attempt", "lastCommitVoteHeight", cs.lastCommitVoteHeight, "types.Height", block.Height) - } -} - -// Save Block, save the +2/3 Commits we've seen, -// and sign a Commit-Vote if we haven't already -func (cs *ConsensusState) saveCommitVoteBlock(block *types.Block, blockParts *types.PartSet, commits *VoteSet) { +// Save Block, save the +2/3 Commits we've seen +func (cs *ConsensusState) saveBlock(block *types.Block, blockParts *types.PartSet, commits *VoteSet) { // The proposal must be valid. if err := cs.stageBlock(block, blockParts); err != nil { - // Prevent zombies. - log.Warn("saveCommitVoteBlock() an invalid block", "error", err) - return + panic(Fmt("saveBlock() an invalid block: %v", err)) } // Save to blockStore. @@ -1164,66 +1224,15 @@ func (cs *ConsensusState) saveCommitVoteBlock(block *types.Block, blockParts *ty // Update mempool. cs.mempoolReactor.Mempool.ResetForBlockAndState(block, cs.stagedState) - // Commit-vote if we haven't already. - if cs.lastCommitVoteHeight < block.Height { - cs.signAddVote(types.VoteTypeCommit, block.Hash(), blockParts.Header()) - cs.lastCommitVoteHeight = block.Height - } + // Fire off event + go func(block *types.Block) { + cs.evsw.FireEvent(types.EventStringNewBlock(), block) + cs.evc.Flush() + }(block) + } // implements events.Eventable func (cs *ConsensusState) SetFireable(evsw events.Fireable) { cs.evsw = evsw } - -//----------------------------------------------------------------------------- - -// total duration of given round -func calcRoundDuration(round uint) time.Duration { - return RoundDuration0 + RoundDurationDelta*time.Duration(round) -} - -// startTime is when round zero started. -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) uint { - now := time.Now() - if now.Before(startTime) { - return 0 - } - // Start + D_0 * R + D_delta * (R^2 - R)/2 <= Now; find largest integer R. - // D_delta * R^2 + (2D_0 - D_delta) * R + 2(Start - Now) <= 0. - // AR^2 + BR + C <= 0; A = D_delta, B = (2_D0 - D_delta), C = 2(Start - Now). - // R = Floor((-B + Sqrt(B^2 - 4AC))/2A) - A := float64(RoundDurationDelta) - B := 2.0*float64(RoundDuration0) - float64(RoundDurationDelta) - C := 2.0 * float64(startTime.Sub(now)) - R := math.Floor((-B + math.Sqrt(B*B-4.0*A*C)) / (2 * A)) - if math.IsNaN(R) { - panic("Could not calc round, should not happen") - } - if R > math.MaxInt32 { - panic(Fmt("Could not calc round, round overflow: %v", R)) - } - if R < 0 { - return 0 - } - return uint(R) -} - -// convenience -// NOTE: elapsedRatio can be negative if startTime is in the future. -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) - RoundDuration = calcRoundDuration(round) - roundElapsed = time.Now().Sub(roundStartTime) - elapsedRatio = float64(roundElapsed) / float64(RoundDuration) - return -} diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state_test.go index b85ecad9a953e57811e4969a040c959585963109..f5d99db74758e2bb16332dc8fc18f9733a79f769 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/state_test.go @@ -1,73 +1,26 @@ package consensus import ( - "bytes" "testing" _ "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/config/tendermint_test" - "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types" ) -func TestSetupRound(t *testing.T) { - cs, privValidators := randConsensusState() - val0 := privValidators[0] - - // Add a vote, precommit, and commit by val0. - voteTypes := []byte{types.VoteTypePrevote, types.VoteTypePrecommit, types.VoteTypeCommit} - for _, voteType := range voteTypes { - vote := &types.Vote{Height: 1, Round: 0, Type: voteType} // nil vote - err := val0.SignVote(cs.state.ChainID, vote) - if err != nil { - t.Error("Error signing vote: %v", err) - } - cs.AddVote(val0.Address, vote) - } - - // Ensure that vote appears in RoundState. - rs0 := cs.GetRoundState() - if vote := rs0.Prevotes.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypePrevote { - t.Errorf("Expected to find prevote but got %v", vote) - } - if vote := rs0.Precommits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypePrecommit { - t.Errorf("Expected to find precommit but got %v", vote) - } - if vote := rs0.Commits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit { - t.Errorf("Expected to find commit but got %v", vote) - } - - // Setup round 1 (next round) - cs.SetupNewRound(1, 1) - <-cs.NewStepCh() - - // Now the commit should be copied over to prevotes and precommits. - rs1 := cs.GetRoundState() - if vote := rs1.Prevotes.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit { - t.Errorf("Expected to find commit but got %v", vote) - } - if vote := rs1.Precommits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit { - t.Errorf("Expected to find commit but got %v", vote) - } - if vote := rs1.Commits.GetByAddress(val0.Address); vote == nil || vote.Type != types.VoteTypeCommit { - t.Errorf("Expected to find commit but got %v", vote) - } - -} - -func TestRunActionProposeNoPrivValidator(t *testing.T) { +func TestEnterProposeNoPrivValidator(t *testing.T) { cs, _ := randConsensusState() - cs.RunActionPropose(1, 0) + cs.EnterPropose(1, 0) rs := cs.GetRoundState() if rs.Proposal != nil { t.Error("Expected to make no proposal, since no privValidator") } } -func TestRunActionPropose(t *testing.T) { +func TestEnterPropose(t *testing.T) { cs, privValidators := randConsensusState() val0 := privValidators[0] cs.SetPrivValidator(val0) - cs.RunActionPropose(1, 0) + cs.EnterPropose(1, 0) rs := cs.GetRoundState() // Check that Proposal, ProposalBlock, ProposalBlockParts are set. @@ -82,128 +35,4 @@ func TestRunActionPropose(t *testing.T) { } } -func checkRoundState(t *testing.T, rs *RoundState, - height uint, round uint, step RoundStepType) { - if rs.Height != height { - t.Errorf("rs.Height should be %v, got %v", height, rs.Height) - } - if rs.Round != round { - t.Errorf("rs.Round should be %v, got %v", round, rs.Round) - } - if rs.Step != step { - t.Errorf("rs.Step should be %v, got %v", step, rs.Step) - } -} - -func TestRunActionPrecommitCommitFinalize(t *testing.T) { - cs, privValidators := randConsensusState() - val0 := privValidators[0] - cs.SetPrivValidator(val0) - - cs.RunActionPrecommit(1, 0) - <-cs.NewStepCh() // TODO: test this value too. - if cs.Precommits.GetByAddress(val0.Address) != nil { - t.Errorf("RunActionPrecommit should return nil without a proposal") - } - - cs.RunActionPropose(1, 0) - <-cs.NewStepCh() // TODO: test this value too. - - cs.RunActionPrecommit(1, 0) - <-cs.NewStepCh() // TODO: test this value too. - if cs.Precommits.GetByAddress(val0.Address) != nil { - t.Errorf("RunActionPrecommit should return nil, not enough prevotes") - } - - // Add at least +2/3 prevotes. - for i := 0; i < 7; i++ { - vote := &types.Vote{ - Height: 1, - Round: 0, - Type: types.VoteTypePrevote, - BlockHash: cs.ProposalBlock.Hash(), - BlockParts: cs.ProposalBlockParts.Header(), - } - err := privValidators[i].SignVote(cs.state.ChainID, vote) - if err != nil { - t.Error("Error signing vote: %v", err) - } - cs.AddVote(privValidators[i].Address, vote) - } - - // Test RunActionPrecommit success: - cs.RunActionPrecommit(1, 0) - <-cs.NewStepCh() // TODO: test this value too. - if cs.Precommits.GetByAddress(val0.Address) == nil { - t.Errorf("RunActionPrecommit should have succeeded") - } - checkRoundState(t, cs.GetRoundState(), 1, 0, RoundStepPrecommit) - - // Add at least +2/3 precommits. - for i := 0; i < 7; i++ { - if bytes.Equal(privValidators[i].Address, val0.Address) { - if cs.Precommits.GetByAddress(val0.Address) == nil { - t.Errorf("Proposer should already have signed a precommit vote") - } - continue - } - vote := &types.Vote{ - Height: 1, - Round: 0, - Type: types.VoteTypePrecommit, - BlockHash: cs.ProposalBlock.Hash(), - BlockParts: cs.ProposalBlockParts.Header(), - } - err := privValidators[i].SignVote(cs.state.ChainID, vote) - if err != nil { - t.Error("Error signing vote: %v", err) - } - added, _, err := cs.AddVote(privValidators[i].Address, vote) - if !added || err != nil { - t.Errorf("Error adding precommit: %v", err) - } - } - - // Test RunActionCommit success: - cs.RunActionCommit(1) - <-cs.NewStepCh() // TODO: test this value too. - if cs.Commits.GetByAddress(val0.Address) == nil { - t.Errorf("RunActionCommit should have succeeded") - } - checkRoundState(t, cs.GetRoundState(), 1, 0, RoundStepCommit) - - // cs.CommitTime should still be zero - if !cs.CommitTime.IsZero() { - t.Errorf("Expected CommitTime to yet be zero") - } - - // Add at least +2/3 commits. - for i := 0; i < 7; i++ { - if bytes.Equal(privValidators[i].Address, val0.Address) { - if cs.Commits.GetByAddress(val0.Address) == nil { - t.Errorf("Proposer should already have signed a commit vote") - } - continue - } - vote := &types.Vote{ - Height: 1, - Round: uint(i), // Doesn't matter what round - Type: types.VoteTypeCommit, - BlockHash: cs.ProposalBlock.Hash(), - BlockParts: cs.ProposalBlockParts.Header(), - } - err := privValidators[i].SignVote(cs.state.ChainID, vote) - if err != nil { - t.Error("Error signing vote: %v", err) - } - added, _, err := cs.AddVote(privValidators[i].Address, vote) - if !added || err != nil { - t.Errorf("Error adding commit: %v", err) - } - } - - // Test TryFinalizeCommit: - cs.TryFinalizeCommit(1) - <-cs.NewStepCh() // TODO: test this value too. - checkRoundState(t, cs.GetRoundState(), 2, 0, RoundStepNewHeight) -} +// TODO write better consensus state tests diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/test.go index 4ddf544b6b5819ca2c45a2fb8989b463f103e0d3..684c7b7737510e7615e840939a1011fb66b350e2 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/test.go @@ -18,7 +18,7 @@ func randConsensusState() (*ConsensusState, []*sm.PrivValidator) { return cs, privValidators } -func randVoteSet(height uint, round uint, type_ byte, numValidators int, votingPower uint64) (*VoteSet, *sm.ValidatorSet, []*sm.PrivValidator) { +func randVoteSet(height int, round int, type_ byte, numValidators int, votingPower int64) (*VoteSet, *sm.ValidatorSet, []*sm.PrivValidator) { vals := make([]*sm.Validator, numValidators) privValidators := make([]*sm.PrivValidator, numValidators) for i := 0; i < numValidators; i++ { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/types/proposal.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/types/proposal.go index 2d5ab7e6d5cb2b6141cde0fae9879ed2b44d00ff..54e0413810e214091072d5ad35eb43f06e9438ff 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/types/proposal.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/types/proposal.go @@ -17,32 +17,31 @@ var ( ) type Proposal struct { - Height uint `json:"height"` - Round uint `json:"round"` - BlockParts types.PartSetHeader `json:"block_parts"` - POLParts types.PartSetHeader `json:"pol_parts"` - Signature account.SignatureEd25519 `json:"signature"` + Height int `json:"height"` + Round int `json:"round"` + BlockPartsHeader types.PartSetHeader `json:"block_parts_header"` + POLRound int `json:"pol_round"` // -1 if null. + Signature account.SignatureEd25519 `json:"signature"` } -func NewProposal(height uint, round uint, blockParts, polParts types.PartSetHeader) *Proposal { +func NewProposal(height int, round int, blockPartsHeader types.PartSetHeader, polRound int) *Proposal { return &Proposal{ - Height: height, - Round: round, - BlockParts: blockParts, - POLParts: polParts, + Height: height, + Round: round, + BlockPartsHeader: blockPartsHeader, + POLRound: polRound, } } func (p *Proposal) String() string { return fmt.Sprintf("Proposal{%v/%v %v %v %v}", p.Height, p.Round, - p.BlockParts, p.POLParts, p.Signature) + p.BlockPartsHeader, p.POLRound, p.Signature) } func (p *Proposal) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { binary.WriteTo([]byte(Fmt(`{"chain_id":"%s"`, chainID)), w, n, err) - binary.WriteTo([]byte(`,"proposal":{"block_parts":`), w, n, err) - p.BlockParts.WriteSignBytes(w, n, err) - binary.WriteTo([]byte(Fmt(`,"height":%v,"pol_parts":`, p.Height)), w, n, err) - p.POLParts.WriteSignBytes(w, n, err) + binary.WriteTo([]byte(`,"proposal":{"block_parts_header":`), w, n, err) + p.BlockPartsHeader.WriteSignBytes(w, n, err) + binary.WriteTo([]byte(Fmt(`,"height":%v,"pol_round":%v`, p.Height, p.POLRound)), w, n, err) binary.WriteTo([]byte(Fmt(`,"round":%v}}`, p.Round)), w, n, err) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/types/proposal_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/types/proposal_test.go index 314349659f0e2a612da7ad2ac54efc0e1980af88..a9f435c84bbb5a2d9e46f06eb5609459fd8fdc69 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/types/proposal_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/types/proposal_test.go @@ -11,15 +11,16 @@ import ( func TestProposalSignable(t *testing.T) { proposal := &Proposal{ - Height: 12345, - Round: 23456, - BlockParts: types.PartSetHeader{111, []byte("blockparts")}, - POLParts: types.PartSetHeader{222, []byte("polparts")}, - Signature: nil, + Height: 12345, + Round: 23456, + BlockPartsHeader: types.PartSetHeader{111, []byte("blockparts")}, + POLRound: -1, + Signature: nil, } signBytes := account.SignBytes(config.GetString("chain_id"), proposal) signStr := string(signBytes) - expected := Fmt(`{"chain_id":"%s","proposal":{"block_parts":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_parts":{"hash":"706F6C7061727473","total":222},"round":23456}}`, + + expected := Fmt(`{"chain_id":"%s","proposal":{"block_parts_header":{"hash":"626C6F636B7061727473","total":111},"height":12345,"pol_round":-1,"round":23456}}`, config.GetString("chain_id")) if signStr != expected { t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/vote_set.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/vote_set.go index 5fc76115895c988c1512b5dbc6c87f6ce778615c..075817bb625a43d0094d527eb863905851e5525d 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/vote_set.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/vote_set.go @@ -19,29 +19,26 @@ import ( // A commit of prior rounds can be added added in lieu of votes/precommits. // NOTE: Assumes that the sum total of voting power does not exceed MaxUInt64. type VoteSet struct { - height uint - round uint + height int + round int type_ byte mtx sync.Mutex valSet *sm.ValidatorSet - votes []*types.Vote // validator index -> vote - votesBitArray *BitArray // validator index -> has vote? - votesByBlock map[string]uint64 // string(blockHash)+string(blockParts) -> vote sum. - totalVotes uint64 + votes []*types.Vote // validator index -> vote + votesBitArray *BitArray // validator index -> has vote? + votesByBlock map[string]int64 // string(blockHash)+string(blockParts) -> vote sum. + totalVotes int64 maj23Hash []byte maj23Parts types.PartSetHeader maj23Exists bool } // Constructs a new VoteSet struct used to accumulate votes for given height/round. -func NewVoteSet(height uint, round uint, type_ byte, valSet *sm.ValidatorSet) *VoteSet { +func NewVoteSet(height int, round int, type_ byte, valSet *sm.ValidatorSet) *VoteSet { if height == 0 { panic("Cannot make VoteSet for height == 0, doesn't make sense.") } - if type_ == types.VoteTypeCommit && round != 0 { - panic("Expected round 0 for commit vote set") - } return &VoteSet{ height: height, round: round, @@ -49,12 +46,12 @@ func NewVoteSet(height uint, round uint, type_ byte, valSet *sm.ValidatorSet) *V valSet: valSet, votes: make([]*types.Vote, valSet.Size()), votesBitArray: NewBitArray(valSet.Size()), - votesByBlock: make(map[string]uint64), + votesByBlock: make(map[string]int64), totalVotes: 0, } } -func (voteSet *VoteSet) Height() uint { +func (voteSet *VoteSet) Height() int { if voteSet == nil { return 0 } else { @@ -62,7 +59,23 @@ func (voteSet *VoteSet) Height() uint { } } -func (voteSet *VoteSet) Size() uint { +func (voteSet *VoteSet) Round() int { + if voteSet == nil { + return 0 + } else { + return voteSet.round + } +} + +func (voteSet *VoteSet) Type() byte { + if voteSet == nil { + return 0x00 + } else { + return voteSet.type_ + } +} + +func (voteSet *VoteSet) Size() int { if voteSet == nil { return 0 } else { @@ -70,21 +83,24 @@ func (voteSet *VoteSet) Size() uint { } } -// True if added, false if not. -// Returns ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature] +// Returns added=true, index if vote was added +// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature] +// Duplicate votes return added=false, err=nil. // NOTE: vote should not be mutated after adding. -// Returns the validator index of the vote unless error is set. -func (voteSet *VoteSet) Add(address []byte, vote *types.Vote) (bool, uint, error) { +func (voteSet *VoteSet) AddByIndex(valIndex int, vote *types.Vote) (added bool, index int, err error) { voteSet.mtx.Lock() defer voteSet.mtx.Unlock() - // Make sure the step matches. (or that vote is commit && round < voteSet.round) - if vote.Height != voteSet.height || - (vote.Type != types.VoteTypeCommit && vote.Round != voteSet.round) || - (vote.Type != types.VoteTypeCommit && vote.Type != voteSet.type_) || - (vote.Type == types.VoteTypeCommit && voteSet.type_ != types.VoteTypeCommit && vote.Round >= voteSet.round) { - return false, 0, types.ErrVoteUnexpectedStep - } + return voteSet.addByIndex(valIndex, vote) +} + +// Returns added=true, index if vote was added +// Otherwise returns err=ErrVote[UnexpectedStep|InvalidAccount|InvalidSignature|InvalidBlockHash|ConflictingSignature] +// Duplicate votes return added=false, err=nil. +// NOTE: vote should not be mutated after adding. +func (voteSet *VoteSet) AddByAddress(address []byte, vote *types.Vote) (added bool, index int, err error) { + voteSet.mtx.Lock() + defer voteSet.mtx.Unlock() // Ensure that signer is a validator. valIndex, val := voteSet.valSet.GetByAddress(address) @@ -92,16 +108,34 @@ func (voteSet *VoteSet) Add(address []byte, vote *types.Vote) (bool, uint, error return false, 0, types.ErrVoteInvalidAccount } + return voteSet.addVote(val, valIndex, vote) +} + +func (voteSet *VoteSet) addByIndex(valIndex int, vote *types.Vote) (bool, int, error) { + // Ensure that signer is a validator. + _, val := voteSet.valSet.GetByIndex(valIndex) + if val == nil { + return false, 0, types.ErrVoteInvalidAccount + } + + return voteSet.addVote(val, valIndex, vote) +} + +func (voteSet *VoteSet) addVote(val *sm.Validator, valIndex int, vote *types.Vote) (bool, int, error) { + + // Make sure the step matches. (or that vote is commit && round < voteSet.round) + if (vote.Height != voteSet.height) || + (vote.Round != voteSet.round) || + (vote.Type != voteSet.type_) { + return false, 0, types.ErrVoteUnexpectedStep + } + // Check signature. if !val.PubKey.VerifyBytes(account.SignBytes(config.GetString("chain_id"), vote), vote.Signature) { // Bad signature. return false, 0, types.ErrVoteInvalidSignature } - return voteSet.addVote(valIndex, vote) -} - -func (voteSet *VoteSet) addVote(valIndex uint, vote *types.Vote) (bool, uint, error) { // If vote already exists, return false. if existingVote := voteSet.votes[valIndex]; existingVote != nil { if bytes.Equal(existingVote.BlockHash, vote.BlockHash) { @@ -115,10 +149,6 @@ func (voteSet *VoteSet) addVote(valIndex uint, vote *types.Vote) (bool, uint, er } // Add vote. - _, val := voteSet.valSet.GetByIndex(valIndex) - if val == nil { - panic(fmt.Sprintf("Missing validator for index %v", valIndex)) - } voteSet.votes[valIndex] = vote voteSet.votesBitArray.SetIndex(valIndex, true) blockKey := string(vote.BlockHash) + string(binary.BinaryBytes(vote.BlockParts)) @@ -137,18 +167,6 @@ func (voteSet *VoteSet) addVote(valIndex uint, vote *types.Vote) (bool, uint, er return true, valIndex, nil } -// Assumes that commits VoteSet is valid. -func (voteSet *VoteSet) AddFromCommits(commits *VoteSet) { - for valIndex, commit := range commits.votes { - if commit == nil { - continue - } - if commit.Round < voteSet.round { - voteSet.addVote(uint(valIndex), commit) - } - } -} - func (voteSet *VoteSet) BitArray() *BitArray { if voteSet == nil { return nil @@ -158,7 +176,7 @@ func (voteSet *VoteSet) BitArray() *BitArray { return voteSet.votesBitArray.Copy() } -func (voteSet *VoteSet) GetByIndex(valIndex uint) *types.Vote { +func (voteSet *VoteSet) GetByIndex(valIndex int) *types.Vote { voteSet.mtx.Lock() defer voteSet.mtx.Unlock() return voteSet.votes[valIndex] @@ -183,82 +201,40 @@ func (voteSet *VoteSet) HasTwoThirdsMajority() bool { return voteSet.maj23Exists } -// Returns either a blockhash (or nil) that received +2/3 majority. -// If there exists no such majority, returns (nil, false). -func (voteSet *VoteSet) TwoThirdsMajority() (hash []byte, parts types.PartSetHeader, ok bool) { +func (voteSet *VoteSet) IsCommit() bool { + if voteSet == nil { + return false + } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() - if voteSet.maj23Exists { - return voteSet.maj23Hash, voteSet.maj23Parts, true - } else { - return nil, types.PartSetHeader{}, false - } + return len(voteSet.maj23Hash) > 0 } -func (voteSet *VoteSet) MakePOL() *POL { - if voteSet.type_ != types.VoteTypePrevote { - panic("Cannot MakePOL() unless VoteSet.Type is types.VoteTypePrevote") +func (voteSet *VoteSet) HasTwoThirdsAny() bool { + if voteSet == nil { + return false } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() - if !voteSet.maj23Exists { - return nil - } - pol := &POL{ - Height: voteSet.height, - Round: voteSet.round, - BlockHash: voteSet.maj23Hash, - BlockParts: voteSet.maj23Parts, - Votes: make([]POLVoteSignature, voteSet.valSet.Size()), - } - for valIndex, vote := range voteSet.votes { - if vote == nil { - continue - } - if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) { - continue - } - if !vote.BlockParts.Equals(voteSet.maj23Parts) { - continue - } - pol.Votes[valIndex] = POLVoteSignature{ - Round: vote.Round, - Signature: vote.Signature, - } - } - return pol + return voteSet.totalVotes > voteSet.valSet.TotalVotingPower()*2/3 } -func (voteSet *VoteSet) MakeValidation() *types.Validation { - if voteSet.type_ != types.VoteTypeCommit { - panic("Cannot MakeValidation() unless VoteSet.Type is types.VoteTypeCommit") - } +// Returns either a blockhash (or nil) that received +2/3 majority. +// If there exists no such majority, returns (nil, false). +func (voteSet *VoteSet) TwoThirdsMajority() (hash []byte, parts types.PartSetHeader, ok bool) { voteSet.mtx.Lock() defer voteSet.mtx.Unlock() - if len(voteSet.maj23Hash) == 0 { - panic("Cannot MakeValidation() unless a blockhash has +2/3") - } - commits := make([]types.Commit, voteSet.valSet.Size()) - voteSet.valSet.Iterate(func(valIndex uint, val *sm.Validator) bool { - vote := voteSet.votes[valIndex] - if vote == nil { - return false - } - if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) { - return false - } - if !vote.BlockParts.Equals(voteSet.maj23Parts) { - return false - } - commits[valIndex] = types.Commit{val.Address, vote.Round, vote.Signature} - return false - }) - return &types.Validation{ - Commits: commits, + if voteSet.maj23Exists { + return voteSet.maj23Hash, voteSet.maj23Parts, true + } else { + return nil, types.PartSetHeader{}, false } } func (voteSet *VoteSet) String() string { + if voteSet == nil { + return "nil-VoteSet" + } return voteSet.StringIndented("") } @@ -291,3 +267,35 @@ func (voteSet *VoteSet) StringShort() string { return fmt.Sprintf(`VoteSet{H:%v R:%v T:%v +2/3:%v %v}`, voteSet.height, voteSet.round, voteSet.type_, voteSet.maj23Exists, voteSet.votesBitArray) } + +//-------------------------------------------------------------------------------- +// Validation + +func (voteSet *VoteSet) MakeValidation() *types.Validation { + if voteSet.type_ != types.VoteTypePrecommit { + panic("Cannot MakeValidation() unless VoteSet.Type is types.VoteTypePrecommit") + } + voteSet.mtx.Lock() + defer voteSet.mtx.Unlock() + if len(voteSet.maj23Hash) == 0 { + panic("Cannot MakeValidation() unless a blockhash has +2/3") + } + precommits := make([]*types.Vote, voteSet.valSet.Size()) + voteSet.valSet.Iterate(func(valIndex int, val *sm.Validator) bool { + vote := voteSet.votes[valIndex] + if vote == nil { + return false + } + if !bytes.Equal(vote.BlockHash, voteSet.maj23Hash) { + return false + } + if !vote.BlockParts.Equals(voteSet.maj23Parts) { + return false + } + precommits[valIndex] = vote + return false + }) + return &types.Validation{ + Precommits: precommits, + } +} diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/vote_set_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/vote_set_test.go index 07915f9e01bc7e8ee694812edd0b1ad74c813388..896e386928a9366b7df0d66483f79b96b844d9d4 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/vote_set_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/consensus/vote_set_test.go @@ -15,14 +15,14 @@ import ( // NOTE: see consensus/test.go for common test methods. // Convenience: Return new vote with different height -func withHeight(vote *types.Vote, height uint) *types.Vote { +func withHeight(vote *types.Vote, height int) *types.Vote { vote = vote.Copy() vote.Height = height return vote } // Convenience: Return new vote with different round -func withRound(vote *types.Vote, round uint) *types.Vote { +func withRound(vote *types.Vote, round int) *types.Vote { vote = vote.Copy() vote.Round = round return vote @@ -51,12 +51,12 @@ func withBlockParts(vote *types.Vote, blockParts types.PartSetHeader) *types.Vot func signAddVote(privVal *sm.PrivValidator, vote *types.Vote, voteSet *VoteSet) (bool, error) { privVal.SignVoteUnsafe(config.GetString("chain_id"), vote) - added, _, err := voteSet.Add(privVal.Address, vote) + added, _, err := voteSet.AddByAddress(privVal.Address, vote) return added, err } func TestAddVote(t *testing.T) { - height, round := uint(1), uint(0) + height, round := 1, 0 voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) val0 := privValidators[0] @@ -89,7 +89,7 @@ func TestAddVote(t *testing.T) { } func Test2_3Majority(t *testing.T) { - height, round := uint(1), uint(0) + height, round := 1, 0 voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) vote := &types.Vote{Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: nil} @@ -123,11 +123,11 @@ func Test2_3Majority(t *testing.T) { } func Test2_3MajorityRedux(t *testing.T) { - height, round := uint(1), uint(0) + height, round := 1, 0 voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 100, 1) blockHash := CRandBytes(32) - blockPartsTotal := uint(123) + blockPartsTotal := 123 blockParts := types.PartSetHeader{blockPartsTotal, CRandBytes(32)} vote := &types.Vote{Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: blockHash, BlockParts: blockParts} @@ -190,104 +190,47 @@ func Test2_3MajorityRedux(t *testing.T) { } func TestBadVotes(t *testing.T) { - height, round := uint(1), uint(0) + height, round := 1, 0 voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) // val0 votes for nil. vote := &types.Vote{Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: nil} added, err := signAddVote(privValidators[0], vote, voteSet) if !added || err != nil { - t.Errorf("Expected Add() to succeed") + t.Errorf("Expected VoteSet.Add to succeed") } // val0 votes again for some block. added, err = signAddVote(privValidators[0], withBlockHash(vote, RandBytes(32)), voteSet) if added || err == nil { - t.Errorf("Expected Add() to fail, dupeout.") + t.Errorf("Expected VoteSet.Add to fail, dupeout.") } // val1 votes on another height added, err = signAddVote(privValidators[1], withHeight(vote, height+1), voteSet) if added { - t.Errorf("Expected Add() to fail, wrong height") + t.Errorf("Expected VoteSet.Add to fail, wrong height") } // val2 votes on another round added, err = signAddVote(privValidators[2], withRound(vote, round+1), voteSet) if added { - t.Errorf("Expected Add() to fail, wrong round") + t.Errorf("Expected VoteSet.Add to fail, wrong round") } // val3 votes of another type. added, err = signAddVote(privValidators[3], withType(vote, types.VoteTypePrecommit), voteSet) if added { - t.Errorf("Expected Add() to fail, wrong type") + t.Errorf("Expected VoteSet.Add to fail, wrong type") } } -func TestAddCommitsToPrevoteVotes(t *testing.T) { - height, round := uint(2), uint(5) - voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypePrevote, 10, 1) - - // val0, val1, val2, val3, val4, val5 vote for nil. - vote := &types.Vote{Height: height, Round: round, Type: types.VoteTypePrevote, BlockHash: nil} - for i := 0; i < 6; i++ { - signAddVote(privValidators[i], vote, voteSet) - } - hash, header, ok := voteSet.TwoThirdsMajority() - if hash != nil || !header.IsZero() || ok { - t.Errorf("There should be no 2/3 majority") - } - - // Attempt to add a commit from val6 at a previous height - vote = &types.Vote{Height: height - 1, Round: round, Type: types.VoteTypeCommit, BlockHash: nil} - added, _ := signAddVote(privValidators[6], vote, voteSet) - if added { - t.Errorf("Expected Add() to fail, wrong height.") - } - - // Attempt to add a commit from val6 at a later round - vote = &types.Vote{Height: height, Round: round + 1, Type: types.VoteTypeCommit, BlockHash: nil} - added, _ = signAddVote(privValidators[6], vote, voteSet) - if added { - t.Errorf("Expected Add() to fail, cannot add future round vote.") - } - - // Attempt to add a commit from val6 for currrent height/round. - vote = &types.Vote{Height: height, Round: round, Type: types.VoteTypeCommit, BlockHash: nil} - added, err := signAddVote(privValidators[6], vote, voteSet) - if added || err == nil { - t.Errorf("Expected Add() to fail, only prior round commits can be added.") - } - - // Add commit from val6 at a previous round - vote = &types.Vote{Height: height, Round: round - 1, Type: types.VoteTypeCommit, BlockHash: nil} - added, err = signAddVote(privValidators[6], vote, voteSet) - if !added || err != nil { - t.Errorf("Expected Add() to succeed, commit for prior rounds are relevant.") - } - - // Also add commit from val7 for previous round. - vote = &types.Vote{Height: height, Round: round - 2, Type: types.VoteTypeCommit, BlockHash: nil} - added, err = signAddVote(privValidators[7], vote, voteSet) - if !added || err != nil { - t.Errorf("Expected Add() to succeed. err: %v", err) - } - - // We should have 2/3 majority - hash, header, ok = voteSet.TwoThirdsMajority() - if hash != nil || !header.IsZero() || !ok { - t.Errorf("There should be 2/3 majority for nil") - } - -} - func TestMakeValidation(t *testing.T) { - height, round := uint(1), uint(0) - voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypeCommit, 10, 1) + height, round := 1, 0 + voteSet, _, privValidators := randVoteSet(height, round, types.VoteTypePrecommit, 10, 1) blockHash, blockParts := CRandBytes(32), types.PartSetHeader{123, CRandBytes(32)} - vote := &types.Vote{Height: height, Round: round, Type: types.VoteTypeCommit, + vote := &types.Vote{Height: height, Round: round, Type: types.VoteTypePrecommit, BlockHash: blockHash, BlockParts: blockParts} // 6 out of 10 voted for some block. @@ -313,11 +256,11 @@ func TestMakeValidation(t *testing.T) { validation := voteSet.MakeValidation() // Validation should have 10 elements - if len(validation.Commits) != 10 { - t.Errorf("Validation Commits should have the same number of commits as validators") + if len(validation.Precommits) != 10 { + t.Errorf("Validation Precommits should have the same number of precommits as validators") } - // Ensure that Validation commits are ordered. + // Ensure that Validation precommits are ordered. if err := validation.ValidateBasic(); err != nil { t.Errorf("Error in Validation.ValidateBasic(): %v", err) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool.go index 32c9edce75c19474f7bf25a48e5fcec8032f3e45..2fa6999c858b7274ba856b1da8ffc5050852f965 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/mempool/mempool.go @@ -11,7 +11,6 @@ package mempool import ( "sync" - "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/binary" sm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types" ) @@ -73,19 +72,18 @@ func (mem *Mempool) ResetForBlockAndState(block *types.Block, state *sm.State) { // First, create a lookup map of txns in new block. blockTxsMap := make(map[string]struct{}) for _, tx := range block.Data.Txs { - txHash := binary.BinarySha256(tx) - blockTxsMap[string(txHash)] = struct{}{} + blockTxsMap[string(types.TxID(state.ChainID, tx))] = struct{}{} } // Next, filter all txs from mem.txs that are in blockTxsMap txs := []types.Tx{} for _, tx := range mem.txs { - txHash := binary.BinarySha256(tx) - if _, ok := blockTxsMap[string(txHash)]; ok { - log.Debug("Filter out, already committed", "tx", tx, "txHash", txHash) + txID := types.TxID(state.ChainID, tx) + if _, ok := blockTxsMap[string(txID)]; ok { + log.Debug("Filter out, already committed", "tx", tx, "txID", txID) continue } else { - log.Debug("Filter in, still new", "tx", tx, "txHash", txHash) + log.Debug("Filter in, still new", "tx", tx, "txID", txID) txs = append(txs, tx) } } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_node.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_node.go index 3d6f79320f95ad1b6672a6cd55969c149bc95102..1e71bb1c03ed118eff0dcbf865faa380e99b9dce 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_node.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_node.go @@ -2,7 +2,7 @@ package merkle import ( "bytes" - "crypto/sha256" + "github.com/eris-ltd/eris-db/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160" "io" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/binary" @@ -13,8 +13,8 @@ import ( type IAVLNode struct { key interface{} value interface{} - height uint8 - size uint + height int8 + size int hash []byte leftHash []byte leftNode *IAVLNode @@ -38,8 +38,8 @@ func ReadIAVLNode(t *IAVLTree, r io.Reader, n *int64, err *error) *IAVLNode { node := &IAVLNode{} // node header - node.height = binary.ReadUint8(r, n, err) - node.size = binary.ReadUvarint(r, n, err) + node.height = binary.ReadInt8(r, n, err) + node.size = binary.ReadVarint(r, n, err) node.key = decodeByteSlice(t.keyCodec, r, n, err) if node.height == 0 { @@ -88,7 +88,7 @@ func (node *IAVLNode) has(t *IAVLTree, key interface{}) (has bool) { } } -func (node *IAVLNode) get(t *IAVLTree, key interface{}) (index uint, value interface{}) { +func (node *IAVLNode) get(t *IAVLTree, key interface{}) (index int, value interface{}) { if node.height == 0 { if t.keyCodec.Compare(node.key, key) == 0 { return 0, node.value @@ -107,7 +107,7 @@ func (node *IAVLNode) get(t *IAVLTree, key interface{}) (index uint, value inter } } -func (node *IAVLNode) getByIndex(t *IAVLTree, index uint) (key interface{}, value interface{}) { +func (node *IAVLNode) getByIndex(t *IAVLTree, index int) (key interface{}, value interface{}) { if node.height == 0 { if index == 0 { return node.key, node.value @@ -127,12 +127,12 @@ func (node *IAVLNode) getByIndex(t *IAVLTree, index uint) (key interface{}, valu } // NOTE: sets hashes recursively -func (node *IAVLNode) hashWithCount(t *IAVLTree) ([]byte, uint) { +func (node *IAVLNode) hashWithCount(t *IAVLTree) ([]byte, int) { if node.hash != nil { return node.hash, 0 } - hasher := sha256.New() + hasher := ripemd160.New() buf := new(bytes.Buffer) _, hashCount, err := node.writeHashBytes(t, buf) if err != nil { @@ -147,10 +147,10 @@ func (node *IAVLNode) hashWithCount(t *IAVLTree) ([]byte, uint) { } // NOTE: sets hashes recursively -func (node *IAVLNode) writeHashBytes(t *IAVLTree, w io.Writer) (n int64, hashCount uint, err error) { +func (node *IAVLNode) writeHashBytes(t *IAVLTree, w io.Writer) (n int64, hashCount int, err error) { // height & size - binary.WriteUint8(node.height, w, &n, &err) - binary.WriteUvarint(node.size, w, &n, &err) + binary.WriteInt8(node.height, w, &n, &err) + binary.WriteVarint(node.size, w, &n, &err) // key is not written for inner nodes, unlike writePersistBytes if node.height == 0 { @@ -210,8 +210,8 @@ func (node *IAVLNode) save(t *IAVLTree) []byte { // NOTE: sets hashes recursively func (node *IAVLNode) writePersistBytes(t *IAVLTree, w io.Writer) (n int64, err error) { // node header - binary.WriteUint8(node.height, w, &n, &err) - binary.WriteUvarint(node.size, w, &n, &err) + binary.WriteInt8(node.height, w, &n, &err) + binary.WriteVarint(node.size, w, &n, &err) // key (unlike writeHashBytes, key is written for inner nodes) encodeByteSlice(node.key, t.keyCodec, w, &n, &err) @@ -365,7 +365,7 @@ func (node *IAVLNode) rotateLeft(t *IAVLTree) *IAVLNode { // NOTE: mutates height and size func (node *IAVLNode) calcHeightAndSize(t *IAVLTree) { - node.height = maxUint8(node.getLeftNode(t).height, node.getRightNode(t).height) + 1 + node.height = maxInt8(node.getLeftNode(t).height, node.getRightNode(t).height) + 1 node.size = node.getLeftNode(t).size + node.getRightNode(t).size } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_proof.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_proof.go index 7c0b2acacc89032d82bc38ca322a90398442183c..a2f63197d1655874fbea3e8a234092556c26eafe 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_proof.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_proof.go @@ -2,43 +2,53 @@ package merkle import ( "bytes" - "crypto/sha256" + + "github.com/eris-ltd/eris-db/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/binary" . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" ) type IAVLProof struct { - Root []byte - Branches []IAVLProofBranch - Leaf IAVLProofLeaf + LeafNode IAVLProofLeafNode + InnerNodes []IAVLProofInnerNode + RootHash []byte } -func (proof *IAVLProof) Verify() bool { - hash := proof.Leaf.Hash() +func (proof *IAVLProof) Verify(keyBytes, valueBytes, rootHash []byte) bool { + if !bytes.Equal(keyBytes, proof.LeafNode.KeyBytes) { + return false + } + if !bytes.Equal(valueBytes, proof.LeafNode.ValueBytes) { + return false + } + if !bytes.Equal(rootHash, proof.RootHash) { + return false + } + hash := proof.LeafNode.Hash() // fmt.Printf("leaf hash: %X\n", hash) - for i := len(proof.Branches) - 1; 0 <= i; i-- { - hash = proof.Branches[i].Hash(hash) + for _, branch := range proof.InnerNodes { + hash = branch.Hash(hash) // fmt.Printf("branch hash: %X\n", hash) } - // fmt.Printf("root: %X, computed: %X\n", proof.Root, hash) - return bytes.Equal(proof.Root, hash) + // fmt.Printf("root: %X, computed: %X\n", proof.RootHash, hash) + return bytes.Equal(proof.RootHash, hash) } -type IAVLProofBranch struct { - Height uint8 - Size uint +type IAVLProofInnerNode struct { + Height int8 + Size int Left []byte Right []byte } -func (branch IAVLProofBranch) Hash(childHash []byte) []byte { - hasher := sha256.New() +func (branch IAVLProofInnerNode) Hash(childHash []byte) []byte { + hasher := ripemd160.New() buf := new(bytes.Buffer) n, err := int64(0), error(nil) - binary.WriteUint8(branch.Height, buf, &n, &err) - binary.WriteUvarint(branch.Size, buf, &n, &err) - if branch.Left == nil { + binary.WriteInt8(branch.Height, buf, &n, &err) + binary.WriteVarint(branch.Size, buf, &n, &err) + if len(branch.Left) == 0 { binary.WriteByteSlice(childHash, buf, &n, &err) binary.WriteByteSlice(branch.Right, buf, &n, &err) } else { @@ -46,30 +56,30 @@ func (branch IAVLProofBranch) Hash(childHash []byte) []byte { binary.WriteByteSlice(childHash, buf, &n, &err) } if err != nil { - panic(Fmt("Failed to hash IAVLProofBranch: %v", err)) + panic(Fmt("Failed to hash IAVLProofInnerNode: %v", err)) } - // fmt.Printf("Branch hash bytes: %X\n", buf.Bytes()) + // fmt.Printf("InnerNode hash bytes: %X\n", buf.Bytes()) hasher.Write(buf.Bytes()) return hasher.Sum(nil) } -type IAVLProofLeaf struct { +type IAVLProofLeafNode struct { KeyBytes []byte ValueBytes []byte } -func (leaf IAVLProofLeaf) Hash() []byte { - hasher := sha256.New() +func (leaf IAVLProofLeafNode) Hash() []byte { + hasher := ripemd160.New() buf := new(bytes.Buffer) n, err := int64(0), error(nil) - binary.WriteUint8(0, buf, &n, &err) - binary.WriteUvarint(1, buf, &n, &err) + binary.WriteInt8(0, buf, &n, &err) + binary.WriteVarint(1, buf, &n, &err) binary.WriteByteSlice(leaf.KeyBytes, buf, &n, &err) binary.WriteByteSlice(leaf.ValueBytes, buf, &n, &err) if err != nil { - panic(Fmt("Failed to hash IAVLProofLeaf: %v", err)) + panic(Fmt("Failed to hash IAVLProofLeafNode: %v", err)) } - // fmt.Printf("Leaf hash bytes: %X\n", buf.Bytes()) + // fmt.Printf("LeafNode hash bytes: %X\n", buf.Bytes()) hasher.Write(buf.Bytes()) return hasher.Sum(nil) } @@ -87,38 +97,42 @@ func (node *IAVLNode) constructProof(t *IAVLTree, key interface{}, proof *IAVLPr if err != nil { panic(Fmt("Failed to encode node.value: %v", err)) } - leaf := IAVLProofLeaf{ + leaf := IAVLProofLeafNode{ KeyBytes: keyBuf.Bytes(), ValueBytes: valueBuf.Bytes(), } - proof.Leaf = leaf + proof.LeafNode = leaf return true } else { return false } } else { if t.keyCodec.Compare(key, node.key) < 0 { - branch := IAVLProofBranch{ + exists := node.getLeftNode(t).constructProof(t, key, proof) + if !exists { + return false + } + branch := IAVLProofInnerNode{ Height: node.height, Size: node.size, Left: nil, Right: node.getRightNode(t).hash, } - if node.getRightNode(t).hash == nil { - // fmt.Println(node.getRightNode(t)) - panic("WTF") - } - proof.Branches = append(proof.Branches, branch) - return node.getLeftNode(t).constructProof(t, key, proof) + proof.InnerNodes = append(proof.InnerNodes, branch) + return true } else { - branch := IAVLProofBranch{ + exists := node.getRightNode(t).constructProof(t, key, proof) + if !exists { + return false + } + branch := IAVLProofInnerNode{ Height: node.height, Size: node.size, Left: node.getLeftNode(t).hash, Right: nil, } - proof.Branches = append(proof.Branches, branch) - return node.getRightNode(t).constructProof(t, key, proof) + proof.InnerNodes = append(proof.InnerNodes, branch) + return true } } } @@ -130,7 +144,7 @@ func (t *IAVLTree) ConstructProof(key interface{}) *IAVLProof { } t.root.hashWithCount(t) // Ensure that all hashes are calculated. proof := &IAVLProof{ - Root: t.root.hash, + RootHash: t.root.hash, } t.root.constructProof(t, key, proof) return proof diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_test.go index d3b294c155b9b6cbd709643ac470bac00c3b010a..4861257029f0fbb6dd8aa0d41bba7298a9017e78 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_test.go @@ -60,7 +60,7 @@ func P(n *IAVLNode) string { func TestUnit(t *testing.T) { - expectHash := func(tree *IAVLTree, hashCount uint) { + expectHash := func(tree *IAVLTree, hashCount int) { // ensure number of new hash calculations is as expected. hash, count := tree.HashWithCount() if count != hashCount { @@ -78,7 +78,7 @@ func TestUnit(t *testing.T) { } } - expectSet := func(tree *IAVLTree, i int, repr string, hashCount uint) { + expectSet := func(tree *IAVLTree, i int, repr string, hashCount int) { origNode := tree.root updated := tree.Set(i, "") // ensure node was added & structure is as expected. @@ -91,7 +91,7 @@ func TestUnit(t *testing.T) { tree.root = origNode } - expectRemove := func(tree *IAVLTree, i int, repr string, hashCount uint) { + expectRemove := func(tree *IAVLTree, i int, repr string, hashCount int) { origNode := tree.root value, removed := tree.Remove(i) // ensure node was added & structure is as expected. @@ -168,7 +168,7 @@ func TestIntegration(t *testing.T) { if !updated { t.Error("should have been updated") } - if tree.Size() != uint(i+1) { + if tree.Size() != i+1 { t.Error("size was wrong", tree.Size(), i+1) } } @@ -203,7 +203,7 @@ func TestIntegration(t *testing.T) { t.Error("wrong value") } } - if tree.Size() != uint(len(records)-(i+1)) { + if tree.Size() != len(records)-(i+1) { t.Error("size was wrong", tree.Size(), (len(records) - (i + 1))) } } @@ -238,9 +238,9 @@ func TestPersistence(t *testing.T) { } } -func testProof(t *testing.T, proof *IAVLProof) { +func testProof(t *testing.T, proof *IAVLProof, keyBytes, valueBytes, rootHash []byte) { // Proof must verify. - if !proof.Verify() { + if !proof.Verify(keyBytes, valueBytes, rootHash) { t.Errorf("Invalid proof. Verification failed.") return } @@ -252,25 +252,37 @@ func testProof(t *testing.T, proof *IAVLProof) { t.Errorf("Failed to read IAVLProof from bytes: %v", err) return } - if !proof2.Verify() { + if !proof2.Verify(keyBytes, valueBytes, rootHash) { + // t.Log(Fmt("%X\n%X\n", proofBytes, binary.BinaryBytes(proof2))) t.Errorf("Invalid proof after write/read. Verification failed.") return } // Random mutations must not verify - for i := 0; i < 3; i++ { + for i := 0; i < 5; i++ { badProofBytes := MutateByteSlice(proofBytes) n, err := int64(0), error(nil) badProof := binary.ReadBinary(&IAVLProof{}, bytes.NewBuffer(badProofBytes), &n, &err).(*IAVLProof) if err != nil { continue // This is fine. } - if badProof.Verify() { + if badProof.Verify(keyBytes, valueBytes, rootHash) { t.Errorf("Proof was still valid after a random mutation:\n%X\n%X", proofBytes, badProofBytes) } } } -func TestConstructProof(t *testing.T) { +func TestIAVLProof(t *testing.T) { + + // Convenient wrapper around binary.BasicCodec. + toBytes := func(o interface{}) []byte { + buf, n, err := new(bytes.Buffer), int64(0), error(nil) + binary.BasicCodec.Encode(o, buf, &n, &err) + if err != nil { + panic(Fmt("Failed to encode thing: %v", err)) + } + return buf.Bytes() + } + // Construct some random tree db := db.NewMemDB() var tree *IAVLTree = NewIAVLTree(binary.BasicCodec, binary.BasicCodec, 100, db) @@ -291,13 +303,10 @@ func TestConstructProof(t *testing.T) { // Now for each item, construct a proof and verify tree.Iterate(func(key interface{}, value interface{}) bool { proof := tree.ConstructProof(key) - if !bytes.Equal(proof.Root, tree.Hash()) { - t.Errorf("Invalid proof. Expected root %X, got %X", tree.Hash(), proof.Root) - } - if !proof.Verify() { - t.Errorf("Invalid proof. Verification failed.") + if !bytes.Equal(proof.RootHash, tree.Hash()) { + t.Errorf("Invalid proof. Expected root %X, got %X", tree.Hash(), proof.RootHash) } - testProof(t, proof) + testProof(t, proof, toBytes(key), toBytes(value), tree.Hash()) return false }) @@ -310,7 +319,7 @@ func BenchmarkImmutableAvlTree(b *testing.B) { // 23000ns/op, 43000ops/s // for i := 0; i < 10000000; i++ { for i := 0; i < 1000000; i++ { - t.Set(RandUint64(), "") + t.Set(RandInt64(), "") } fmt.Println("ok, starting") @@ -319,7 +328,7 @@ func BenchmarkImmutableAvlTree(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - ri := RandUint64() + ri := RandInt64() t.Set(ri, "") t.Remove(ri) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_tree.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_tree.go index 87ae71564a8c8f9683b50c5514a4c75985a528c9..84e548a6df46010eabd8189f5c5accae87f550c2 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_tree.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/iavl_tree.go @@ -68,14 +68,14 @@ func (t *IAVLTree) Copy() Tree { } } -func (t *IAVLTree) Size() uint { +func (t *IAVLTree) Size() int { if t.root == nil { return 0 } return t.root.size } -func (t *IAVLTree) Height() uint8 { +func (t *IAVLTree) Height() int8 { if t.root == nil { return 0 } @@ -106,7 +106,7 @@ func (t *IAVLTree) Hash() []byte { return hash } -func (t *IAVLTree) HashWithCount() ([]byte, uint) { +func (t *IAVLTree) HashWithCount() ([]byte, int) { if t.root == nil { return nil, 0 } @@ -130,14 +130,14 @@ func (t *IAVLTree) Load(hash []byte) { } } -func (t *IAVLTree) Get(key interface{}) (index uint, value interface{}) { +func (t *IAVLTree) Get(key interface{}) (index int, value interface{}) { if t.root == nil { return 0, nil } return t.root.get(t, key) } -func (t *IAVLTree) GetByIndex(index uint) (key interface{}, value interface{}) { +func (t *IAVLTree) GetByIndex(index int) (key interface{}, value interface{}) { if t.root == nil { return nil, nil } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/simple_tree.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/simple_tree.go index 79def4561ce46c044c367ce17794cd71b723352d..a37cd7015a163c54e1ecbf4b49649045365ca4b9 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/simple_tree.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/simple_tree.go @@ -7,18 +7,18 @@ the tree the same size, but the left may be one greater. Use this for short deterministic trees, such as the validator list. For larger datasets, use IAVLTree. - * - / \ - / \ - / \ - / \ - * * - / \ / \ - / \ / \ - / \ / \ - * * * h6 - / \ / \ / \ - h0 h1 h2 h3 h4 h5 + * + / \ + / \ + / \ + / \ + * * + / \ / \ + / \ / \ + / \ / \ + * * * h6 + / \ / \ / \ + h0 h1 h2 h3 h4 h5 */ @@ -26,15 +26,17 @@ package merkle import ( "bytes" - "crypto/sha256" + "fmt" + + "github.com/eris-ltd/eris-db/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/binary" ) -func HashFromTwoHashes(left []byte, right []byte) []byte { +func SimpleHashFromTwoHashes(left []byte, right []byte) []byte { var n int64 var err error - var hasher = sha256.New() + var hasher = ripemd160.New() binary.WriteByteSlice(left, hasher, &n, &err) binary.WriteByteSlice(right, hasher, &n, &err) if err != nil { @@ -43,7 +45,7 @@ func HashFromTwoHashes(left []byte, right []byte) []byte { return hasher.Sum(nil) } -func HashFromHashes(hashes [][]byte) []byte { +func SimpleHashFromHashes(hashes [][]byte) []byte { // Recursive impl. switch len(hashes) { case 0: @@ -51,24 +53,24 @@ func HashFromHashes(hashes [][]byte) []byte { case 1: return hashes[0] default: - left := HashFromHashes(hashes[:(len(hashes)+1)/2]) - right := HashFromHashes(hashes[(len(hashes)+1)/2:]) - return HashFromTwoHashes(left, right) + left := SimpleHashFromHashes(hashes[:(len(hashes)+1)/2]) + right := SimpleHashFromHashes(hashes[(len(hashes)+1)/2:]) + return SimpleHashFromTwoHashes(left, right) } } -// Convenience for HashFromHashes. -func HashFromBinaries(items []interface{}) []byte { +// Convenience for SimpleHashFromHashes. +func SimpleHashFromBinaries(items []interface{}) []byte { hashes := [][]byte{} for _, item := range items { - hashes = append(hashes, HashFromBinary(item)) + hashes = append(hashes, SimpleHashFromBinary(item)) } - return HashFromHashes(hashes) + return SimpleHashFromHashes(hashes) } // General Convenience -func HashFromBinary(item interface{}) []byte { - hasher, n, err := sha256.New(), new(int64), new(error) +func SimpleHashFromBinary(item interface{}) []byte { + hasher, n, err := ripemd160.New(), new(int64), new(error) binary.WriteBinary(item, hasher, n, err) if *err != nil { panic(err) @@ -76,103 +78,166 @@ func HashFromBinary(item interface{}) []byte { return hasher.Sum(nil) } -// Convenience for HashFromHashes. -func HashFromHashables(items []Hashable) []byte { +// Convenience for SimpleHashFromHashes. +func SimpleHashFromHashables(items []Hashable) []byte { hashes := [][]byte{} for _, item := range items { hash := item.Hash() hashes = append(hashes, hash) } - return HashFromHashes(hashes) + return SimpleHashFromHashes(hashes) } -type HashTrail struct { - Hash []byte - Parent *HashTrail - Left *HashTrail - Right *HashTrail +//-------------------------------------------------------------------------------- + +type SimpleProof struct { + Index int `json:"index"` + Total int `json:"total"` + LeafHash []byte `json:"leaf_hash"` + InnerHashes [][]byte `json:"inner_hashes"` // Hashes from leaf's sibling to a root's child. + RootHash []byte `json:"root_hash"` } -func (ht *HashTrail) Flatten() [][]byte { - // Nonrecursive impl. - trail := [][]byte{} - for ht != nil { - if ht.Left != nil { - trail = append(trail, ht.Left.Hash) - } else if ht.Right != nil { - trail = append(trail, ht.Right.Hash) - } else { - break +// proofs[0] is the proof for items[0]. +func SimpleProofsFromHashables(items []Hashable) (proofs []*SimpleProof) { + trails, root := trailsFromHashables(items) + proofs = make([]*SimpleProof, len(items)) + for i, trail := range trails { + proofs[i] = &SimpleProof{ + Index: i, + Total: len(items), + LeafHash: trail.Hash, + InnerHashes: trail.FlattenInnerHashes(), + RootHash: root.Hash, } - ht = ht.Parent } - return trail + return } -// returned trails[0].Hash is the leaf hash. -// trails[0].Parent.Hash is the hash above that, etc. -func HashTrailsFromHashables(items []Hashable) (trails []*HashTrail, root *HashTrail) { - // Recursive impl. - switch len(items) { - case 0: - return nil, nil - case 1: - trail := &HashTrail{items[0].Hash(), nil, nil, nil} - return []*HashTrail{trail}, trail - default: - lefts, leftRoot := HashTrailsFromHashables(items[:(len(items)+1)/2]) - rights, rightRoot := HashTrailsFromHashables(items[(len(items)+1)/2:]) - rootHash := HashFromTwoHashes(leftRoot.Hash, rightRoot.Hash) - root := &HashTrail{rootHash, nil, nil, nil} - leftRoot.Parent = root - leftRoot.Right = rightRoot - rightRoot.Parent = root - rightRoot.Left = leftRoot - return append(lefts, rights...), root +// Verify that leafHash is a leaf hash of the simple-merkle-tree +// which hashes to rootHash. +func (sp *SimpleProof) Verify(leafHash []byte, rootHash []byte) bool { + if !bytes.Equal(leafHash, sp.LeafHash) { + return false } -} - -// Ensures that leafHash is part of rootHash. -func VerifyHashTrail(index uint, total uint, leafHash []byte, trail [][]byte, rootHash []byte) bool { - computedRoot := ComputeRootFromTrail(index, total, leafHash, trail) - if computedRoot == nil { + if !bytes.Equal(rootHash, sp.RootHash) { return false } - return bytes.Equal(computedRoot, rootHash) + computedHash := computeHashFromInnerHashes(sp.Index, sp.Total, sp.LeafHash, sp.InnerHashes) + if computedHash == nil { + return false + } + if !bytes.Equal(computedHash, rootHash) { + return false + } + return true +} + +func (sp *SimpleProof) String() string { + return sp.StringIndented("") } -// Use the leafHash and trail to get the root merkle hash. -// If the length of the trail slice isn't exactly correct, the result is nil. -func ComputeRootFromTrail(index uint, total uint, leafHash []byte, trail [][]byte) []byte { +func (sp *SimpleProof) StringIndented(indent string) string { + return fmt.Sprintf(`SimpleProof{ +%s Index: %v +%s Total: %v +%s LeafHash: %X +%s InnerHashes: %X +%s RootHash: %X +%s}`, + indent, sp.Index, + indent, sp.Total, + indent, sp.LeafHash, + indent, sp.InnerHashes, + indent, sp.RootHash, + indent) +} + +// Use the leafHash and innerHashes to get the root merkle hash. +// If the length of the innerHashes slice isn't exactly correct, the result is nil. +func computeHashFromInnerHashes(index int, total int, leafHash []byte, innerHashes [][]byte) []byte { // Recursive impl. if index >= total { return nil } switch total { case 0: - panic("Cannot call ComputeRootFromTrail() with 0 total") + panic("Cannot call computeHashFromInnerHashes() with 0 total") case 1: - if len(trail) != 0 { + if len(innerHashes) != 0 { return nil } return leafHash default: - if len(trail) == 0 { + if len(innerHashes) == 0 { return nil } numLeft := (total + 1) / 2 if index < numLeft { - leftRoot := ComputeRootFromTrail(index, numLeft, leafHash, trail[:len(trail)-1]) - if leftRoot == nil { + leftHash := computeHashFromInnerHashes(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1]) + if leftHash == nil { return nil } - return HashFromTwoHashes(leftRoot, trail[len(trail)-1]) + return SimpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1]) } else { - rightRoot := ComputeRootFromTrail(index-numLeft, total-numLeft, leafHash, trail[:len(trail)-1]) - if rightRoot == nil { + rightHash := computeHashFromInnerHashes(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1]) + if rightHash == nil { return nil } - return HashFromTwoHashes(trail[len(trail)-1], rightRoot) + return SimpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash) } } } + +// Helper structure to construct merkle proof. +// The node and the tree is thrown away afterwards. +// Exactly one of node.Left and node.Right is nil, unless node is the root, in which case both are nil. +// node.Parent.Hash = hash(node.Hash, node.Right.Hash) or +// hash(node.Left.Hash, node.Hash), depending on whether node is a left/right child. +type SimpleProofNode struct { + Hash []byte + Parent *SimpleProofNode + Left *SimpleProofNode // Left sibling (only one of Left,Right is set) + Right *SimpleProofNode // Right sibling (only one of Left,Right is set) +} + +// Starting from a leaf SimpleProofNode, FlattenInnerHashes() will return +// the inner hashes for the item corresponding to the leaf. +func (spn *SimpleProofNode) FlattenInnerHashes() [][]byte { + // Nonrecursive impl. + innerHashes := [][]byte{} + for spn != nil { + if spn.Left != nil { + innerHashes = append(innerHashes, spn.Left.Hash) + } else if spn.Right != nil { + innerHashes = append(innerHashes, spn.Right.Hash) + } else { + break + } + spn = spn.Parent + } + return innerHashes +} + +// trails[0].Hash is the leaf hash for items[0]. +// trails[i].Parent.Parent....Parent == root for all i. +func trailsFromHashables(items []Hashable) (trails []*SimpleProofNode, root *SimpleProofNode) { + // Recursive impl. + switch len(items) { + case 0: + return nil, nil + case 1: + trail := &SimpleProofNode{items[0].Hash(), nil, nil, nil} + return []*SimpleProofNode{trail}, trail + default: + lefts, leftRoot := trailsFromHashables(items[:(len(items)+1)/2]) + rights, rightRoot := trailsFromHashables(items[(len(items)+1)/2:]) + rootHash := SimpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash) + root := &SimpleProofNode{rootHash, nil, nil, nil} + leftRoot.Parent = root + leftRoot.Right = rightRoot + rightRoot.Parent = root + rightRoot.Left = leftRoot + return append(lefts, rights...), root + } +} diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/simple_tree_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/simple_tree_test.go index 9d9b36b790fce331e463a1569d4ce0496bfe42f8..6b47479d5af81797ca35f8bac76d76833be99065 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/simple_tree_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/simple_tree_test.go @@ -2,8 +2,8 @@ package merkle import ( . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" + . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common/test" - "bytes" "testing" ) @@ -13,62 +13,71 @@ func (tI testItem) Hash() []byte { return []byte(tI) } -func TestMerkleTrails(t *testing.T) { +func TestSimpleProof(t *testing.T) { - numItems := uint(100) + numItems := 100 items := make([]Hashable, numItems) - for i := uint(0); i < numItems; i++ { + for i := 0; i < numItems; i++ { items[i] = testItem(RandBytes(32)) } - root := HashFromHashables(items) + rootHash := SimpleHashFromHashables(items) - trails, rootTrail := HashTrailsFromHashables(items) - - // Assert that HashFromHashables and HashTrailsFromHashables are compatible. - if !bytes.Equal(root, rootTrail.Hash) { - t.Errorf("Root mismatch:\n%X vs\n%X", root, rootTrail.Hash) - } + proofs := SimpleProofsFromHashables(items) // For each item, check the trail. for i, item := range items { itemHash := item.Hash() - flatTrail := trails[i].Flatten() + proof := proofs[i] // Verify success - ok := VerifyHashTrail(uint(i), numItems, itemHash, flatTrail, root) + ok := proof.Verify(itemHash, rootHash) if !ok { t.Errorf("Verification failed for index %v.", i) } // Wrong item index should make it fail - ok = VerifyHashTrail(uint(i)+1, numItems, itemHash, flatTrail, root) - if ok { - t.Errorf("Expected verification to fail for wrong index %v.", i) + proof.Index += 1 + { + ok = proof.Verify(itemHash, rootHash) + if ok { + t.Errorf("Expected verification to fail for wrong index %v.", i) + } } + proof.Index -= 1 // Trail too long should make it fail - trail2 := append(flatTrail, RandBytes(32)) - ok = VerifyHashTrail(uint(i), numItems, itemHash, trail2, root) - if ok { - t.Errorf("Expected verification to fail for wrong trail length.") + origInnerHashes := proof.InnerHashes + proof.InnerHashes = append(proof.InnerHashes, RandBytes(32)) + { + ok = proof.Verify(itemHash, rootHash) + if ok { + t.Errorf("Expected verification to fail for wrong trail length.") + } } + proof.InnerHashes = origInnerHashes // Trail too short should make it fail - trail2 = flatTrail[:len(flatTrail)-1] - ok = VerifyHashTrail(uint(i), numItems, itemHash, trail2, root) - if ok { - t.Errorf("Expected verification to fail for wrong trail length.") + proof.InnerHashes = proof.InnerHashes[0 : len(proof.InnerHashes)-1] + { + ok = proof.Verify(itemHash, rootHash) + if ok { + t.Errorf("Expected verification to fail for wrong trail length.") + } } + proof.InnerHashes = origInnerHashes // Mutating the itemHash should make it fail. - itemHash2 := make([]byte, len(itemHash)) - copy(itemHash2, itemHash) - itemHash2[0] += byte(0x01) - ok = VerifyHashTrail(uint(i), numItems, itemHash2, flatTrail, root) + ok = proof.Verify(MutateByteSlice(itemHash), rootHash) if ok { t.Errorf("Expected verification to fail for mutated leaf hash") } + + // Mutating the rootHash should make it fail. + ok = proof.Verify(itemHash, MutateByteSlice(rootHash)) + if ok { + t.Errorf("Expected verification to fail for mutated root hash") + } } } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/types.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/types.go index 68a461310c30b91aa805b4ff849d895456a95fc5..87f716c78a76c211c33bd980d7cd9d271d124a94 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/types.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/types.go @@ -1,14 +1,14 @@ package merkle type Tree interface { - Size() (size uint) - Height() (height uint8) + Size() (size int) + Height() (height int8) Has(key interface{}) (has bool) - Get(key interface{}) (index uint, value interface{}) - GetByIndex(index uint) (key interface{}, value interface{}) + Get(key interface{}) (index int, value interface{}) + GetByIndex(index int) (key interface{}, value interface{}) Set(key interface{}, value interface{}) (updated bool) Remove(key interface{}) (value interface{}, removed bool) - HashWithCount() (hash []byte, count uint) + HashWithCount() (hash []byte, count int) Hash() (hash []byte) Save() (hash []byte) Load(hash []byte) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/util.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/util.go index edc3fbf74941e20ffee25c880e773d5b7b8bb168..89fd2741aa590e041f6443ee5becaefbb13b2e9c 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/util.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle/util.go @@ -35,7 +35,7 @@ func printIAVLNode(node *IAVLNode, indent int) { } -func maxUint8(a, b uint8) uint8 { +func maxInt8(a, b int8) int8 { if a > b { return a } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node.go index 43e7b2b32cb197cb5e1902ee360bf993529a764c..c725fefed13ec0e80c0e364fe6d2debec475fcb4 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/node/node.go @@ -7,10 +7,12 @@ import ( "net" "net/http" "os" + "path" "strconv" "strings" "time" + "code.google.com/p/go-uuid/uuid" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/binary" bc "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain" . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" @@ -99,14 +101,14 @@ func NewNode() *Node { pexReactor := p2p.NewPEXReactor(book) // Get BlockchainReactor - bcReactor := bc.NewBlockchainReactor(state, blockStore, config.GetBool("fast_sync")) + bcReactor := bc.NewBlockchainReactor(state.Copy(), blockStore, config.GetBool("fast_sync")) // Get MempoolReactor mempool := mempl.NewMempool(state.Copy()) mempoolReactor := mempl.NewMempoolReactor(mempool) // Get ConsensusReactor - consensusState := consensus.NewConsensusState(state, blockStore, mempoolReactor) + consensusState := consensus.NewConsensusState(state.Copy(), blockStore, mempoolReactor) consensusReactor := consensus.NewConsensusReactor(consensusState, blockStore, config.GetBool("fast_sync")) if privValidator != nil { consensusReactor.SetPrivValidator(privValidator) @@ -139,10 +141,9 @@ func NewNode() *Node { // Call Start() after adding the listeners. func (n *Node) Start() { - log.Info("Starting Node") + log.Info("Starting Node", "chainID", config.GetString("chain_id")) n.book.Start() - nodeInfo := makeNodeInfo(n.sw) - n.sw.SetNodeInfo(nodeInfo) + n.sw.SetNodeInfo(makeNodeInfo(n.sw)) n.sw.Start() } @@ -169,7 +170,8 @@ func (n *Node) AddListener(l p2p.Listener) { n.book.AddOurAddress(l.ExternalAddress()) } -// NOTE: Blocking +// Dial a list of seeds in random order +// Spawns a go routine for each dial func (n *Node) DialSeed() { // permute the list, dial them in random order. seeds := strings.Split(config.GetString("seeds"), ",") @@ -196,7 +198,7 @@ func (n *Node) dialSeed(addr *p2p.NetAddress) { } } -func (n *Node) StartRPC() { +func (n *Node) StartRPC() net.Listener { core.SetBlockStore(n.blockStore) core.SetConsensusState(n.consensusState) core.SetConsensusReactor(n.consensusReactor) @@ -209,7 +211,11 @@ func (n *Node) StartRPC() { mux := http.NewServeMux() rpcserver.RegisterEventsHandler(mux, n.evsw) rpcserver.RegisterRPCFuncs(mux, core.Routes) - rpcserver.StartHTTPServer(listenAddr, mux) + listener, err := rpcserver.StartHTTPServer(listenAddr, mux) + if err != nil { + panic(err) + } + return listener } func (n *Node) Switch() *p2p.Switch { @@ -237,10 +243,18 @@ func makeNodeInfo(sw *p2p.Switch) *types.NodeInfo { ChainID: config.GetString("chain_id"), Moniker: config.GetString("moniker"), Version: config.GetString("version"), + UUID: uuid.New(), + } + + // include git hash in the nodeInfo if available + if rev, err := ReadFile(path.Join(TendermintRepo, ".revision")); err == nil { + nodeInfo.Revision = string(rev) } + if !sw.IsListening() { return nodeInfo } + p2pListener := sw.Listeners()[0] p2pHost := p2pListener.ExternalAddress().IP.String() p2pPort := p2pListener.ExternalAddress().Port @@ -252,7 +266,8 @@ func makeNodeInfo(sw *p2p.Switch) *types.NodeInfo { } // We assume that the rpcListener has the same ExternalAddress. - // This is probably true because both P2P and RPC listeners use UPnP. + // This is probably true because both P2P and RPC listeners use UPnP, + // except of course if the rpc is only bound to localhost nodeInfo.Host = p2pHost nodeInfo.P2PPort = p2pPort nodeInfo.RPCPort = uint16(rpcPort) @@ -269,7 +284,7 @@ func RunNode() { n.Start() // If seedNode is provided by config, dial out. - if len(config.GetString("seeds")) > 0 { + if config.GetString("seeds") != "" { n.DialSeed() } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/addrbook.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/addrbook.go index c72276af827a2861104870c91a5cb13bbafd6510..6bf5c0b5fa811e6397fe5b1065d9b879085d5ffa 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/addrbook.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/addrbook.go @@ -715,7 +715,7 @@ func groupKey(na *NetAddress) string { type knownAddress struct { Addr *NetAddress Src *NetAddress - Attempts uint32 + Attempts int32 LastAttempt time.Time LastSuccess time.Time BucketType byte diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/connection.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/connection.go index 8e48d958919a5739f67d85b965588610e2fb3aed..55268fa6d253a2bea5a9ab38193ba1d163faa3f6 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/connection.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/connection.go @@ -151,6 +151,7 @@ func (c *MConnection) String() string { } func (c *MConnection) flush() { + log.Debug("Flush", "conn", c) err := c.bufWriter.Flush() if err != nil { log.Warn("MConnection flush failed", "error", err) @@ -402,13 +403,13 @@ FOR_LOOP: // do nothing log.Debug("Receive Pong") case packetTypeMsg: - pkt, n, err := msgPacket{}, new(int64), new(error) - binary.ReadBinary(&pkt, c.bufReader, n, err) - c.recvMonitor.Update(int(*n)) - if *err != nil { + pkt, n, err := msgPacket{}, int64(0), error(nil) + binary.ReadBinaryPtr(&pkt, c.bufReader, &n, &err) + c.recvMonitor.Update(int(n)) + if err != nil { if atomic.LoadUint32(&c.stopped) != 1 { - log.Warn("Connection failed @ recvRoutine", "connection", c, "error", *err) - c.stopForError(*err) + log.Warn("Connection failed @ recvRoutine", "connection", c, "error", err) + c.stopForError(err) } break FOR_LOOP } @@ -416,9 +417,16 @@ FOR_LOOP: if !ok || channel == nil { panic(Fmt("Unknown channel %X", pkt.ChannelId)) } - msgBytes := channel.recvMsgPacket(pkt) + msgBytes, err := channel.recvMsgPacket(pkt) + if err != nil { + if atomic.LoadUint32(&c.stopped) != 1 { + log.Warn("Connection failed @ recvRoutine", "connection", c, "error", err) + c.stopForError(err) + } + break FOR_LOOP + } if msgBytes != nil { - //log.Debug("Received bytes", "chId", pkt.ChannelId, "msgBytes", msgBytes) + log.Debug("Received bytes", "chId", pkt.ChannelId, "msgBytes", msgBytes) c.onReceive(pkt.ChannelId, msgBytes) } default: @@ -441,9 +449,9 @@ FOR_LOOP: type ChannelDescriptor struct { Id byte - Priority uint - SendQueueCapacity uint - RecvBufferCapacity uint + Priority int + SendQueueCapacity int + RecvBufferCapacity int } func (chDesc *ChannelDescriptor) FillDefaults() { @@ -462,10 +470,10 @@ type Channel struct { desc *ChannelDescriptor id byte sendQueue chan []byte - sendQueueSize uint32 // atomic. + sendQueueSize int32 // atomic. recving []byte sending []byte - priority uint + priority int recentlySent int64 // exponential moving average } @@ -494,7 +502,7 @@ func (ch *Channel) sendBytes(bytes []byte) bool { // timeout return false case ch.sendQueue <- bytes: - atomic.AddUint32(&ch.sendQueueSize, 1) + atomic.AddInt32(&ch.sendQueueSize, 1) return true } } @@ -505,7 +513,7 @@ func (ch *Channel) sendBytes(bytes []byte) bool { func (ch *Channel) trySendBytes(bytes []byte) bool { select { case ch.sendQueue <- bytes: - atomic.AddUint32(&ch.sendQueueSize, 1) + atomic.AddInt32(&ch.sendQueueSize, 1) return true default: return false @@ -514,7 +522,7 @@ func (ch *Channel) trySendBytes(bytes []byte) bool { // Goroutine-safe func (ch *Channel) loadSendQueueSize() (size int) { - return int(atomic.LoadUint32(&ch.sendQueueSize)) + return int(atomic.LoadInt32(&ch.sendQueueSize)) } // Goroutine-safe @@ -545,7 +553,7 @@ func (ch *Channel) nextMsgPacket() msgPacket { if len(ch.sending) <= maxMsgPacketSize { packet.EOF = byte(0x01) ch.sending = nil - atomic.AddUint32(&ch.sendQueueSize, ^uint32(0)) // decrement sendQueueSize + atomic.AddInt32(&ch.sendQueueSize, -1) // decrement sendQueueSize } else { packet.EOF = byte(0x00) ch.sending = ch.sending[MinInt(maxMsgPacketSize, len(ch.sending)):] @@ -557,6 +565,7 @@ func (ch *Channel) nextMsgPacket() msgPacket { // Not goroutine-safe func (ch *Channel) writeMsgPacketTo(w io.Writer) (n int64, err error) { packet := ch.nextMsgPacket() + log.Debug("Write Msg Packet", "conn", ch.conn, "packet", packet) binary.WriteByte(packetTypeMsg, w, &n, &err) binary.WriteBinary(packet, w, &n, &err) if err != nil { @@ -567,14 +576,18 @@ func (ch *Channel) writeMsgPacketTo(w io.Writer) (n int64, err error) { // Handles incoming msgPackets. Returns a msg bytes if msg is complete. // Not goroutine-safe -func (ch *Channel) recvMsgPacket(pkt msgPacket) []byte { - ch.recving = append(ch.recving, pkt.Bytes...) - if pkt.EOF == byte(0x01) { +func (ch *Channel) recvMsgPacket(packet msgPacket) ([]byte, error) { + log.Debug("Read Msg Packet", "conn", ch.conn, "packet", packet) + if binary.MaxBinaryReadSize < len(ch.recving)+len(packet.Bytes) { + return nil, binary.ErrBinaryReadSizeOverflow + } + ch.recving = append(ch.recving, packet.Bytes...) + if packet.EOF == byte(0x01) { msgBytes := ch.recving ch.recving = make([]byte, 0, defaultRecvBufferCapacity) - return msgBytes + return msgBytes, nil } - return nil + return nil, nil } // Call this periodically to update stats for throttling purposes. @@ -602,7 +615,7 @@ type msgPacket struct { } func (p msgPacket) String() string { - return fmt.Sprintf("MsgPacket{%X:%X}", p.ChannelId, p.Bytes) + return fmt.Sprintf("MsgPacket{%X:%X T:%X}", p.ChannelId, p.Bytes, p.EOF) } //----------------------------------------------------------------------------- diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/listener.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/listener.go index 62f83dec7ba907e034a39c79adb7c6a53f5ca1b7..d049b68438af36815b87a0f5a007bbe8fea2136a 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/listener.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/listener.go @@ -160,7 +160,7 @@ func getUPNPExternalAddress(externalPort, internalPort int) *NetAddress { log.Debug("Getting UPNP external address") nat, err := upnp.Discover() if err != nil { - log.Debug("Could not get UPNP extrernal address", "error", err) + log.Debug("Could not perform UPNP discover", "error", err) return nil } @@ -177,7 +177,7 @@ func getUPNPExternalAddress(externalPort, internalPort int) *NetAddress { externalPort, err = nat.AddPortMapping("tcp", externalPort, internalPort, "tendermint", 0) if err != nil { - log.Debug("Could not get UPNP external address", "error", err) + log.Debug("Could not add UPNP port mapping", "error", err) return nil } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer.go index 49bcd280854b7e090e6222003638d75c03fde7e5..80b971626514bce09ca57de6342af556bff92ab5 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer.go @@ -68,7 +68,7 @@ func newPeer(conn net.Conn, peerNodeInfo *types.NodeInfo, outbound bool, reactor mconn: mconn, running: 0, NodeInfo: peerNodeInfo, - Key: mconn.RemoteAddress.IP.String(), + Key: peerNodeInfo.UUID, Data: NewCMap(), } return p diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer_set.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer_set.go index effad6dccc5a57faeac0796ef804c827328c17ab..4b43defec88791189a9706a878d1e8d61580d919 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer_set.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer_set.go @@ -34,7 +34,7 @@ func NewPeerSet() *PeerSet { } } -// Returns false if peer with key (address) is already in set. +// Returns false if peer with key (uuid) is already in set. func (ps *PeerSet) Add(peer *Peer) bool { ps.mtx.Lock() defer ps.mtx.Unlock() diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer_set_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer_set_test.go index dfdcb174c77fa721a357026b26f2a98b3d309d9e..622055bcc516278c64b28e913702e78978f6700c 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer_set_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/peer_set_test.go @@ -1,16 +1,15 @@ package p2p import ( - "math/rand" "testing" - . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" + "code.google.com/p/go-uuid/uuid" ) // Returns an empty dummy peer func randPeer() *Peer { return &Peer{ - Key: Fmt("%v.%v.%v.%v:%v", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%10000+80), + Key: uuid.New(), } } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/pex_reactor.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/pex_reactor.go index d1329021c2367c75b9b599a3876ac45303ef1d31..47a279627bf87148650a734854e72e5c7343f53b 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/pex_reactor.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/pex_reactor.go @@ -76,8 +76,11 @@ func (pexR *PEXReactor) GetChannels() []*ChannelDescriptor { // Implements Reactor func (pexR *PEXReactor) AddPeer(peer *Peer) { + // Add the peer to the address book + netAddr := NewNetAddressString(fmt.Sprintf("%s:%d", peer.Host, peer.P2PPort)) + pexR.book.AddAddress(netAddr, netAddr) // the peer is its own source + if peer.IsOutbound() { - pexR.SendAddrs(peer, pexR.book.OurAddresses()) if pexR.book.NeedMoreAddrs() { pexR.RequestPEX(peer) } @@ -101,7 +104,7 @@ func (pexR *PEXReactor) Receive(chId byte, src *Peer, msgBytes []byte) { } log.Info("Received message", "msg", msg) - switch msg.(type) { + switch msgT := msg.(type) { case *pexRequestMessage: // src requested some peers. // TODO: prevent abuse. @@ -111,7 +114,7 @@ func (pexR *PEXReactor) Receive(chId byte, src *Peer, msgBytes []byte) { // TODO: prevent abuse. // (We don't want to get spammed with bad peers) srcAddr := src.Connection().RemoteAddress - for _, addr := range msg.(*pexAddrsMessage).Addrs { + for _, addr := range msgT.Addrs { pexR.book.AddAddress(addr, srcAddr) } default: @@ -206,6 +209,15 @@ func (pexR *PEXReactor) ensurePeers() { } }(item.(*NetAddress)) } + + // if no addresses to dial, pick a random connected peer and ask for more peers + if toDial.Size() == 0 { + if peers := pexR.sw.Peers().List(); len(peers) > 0 { + i := rand.Int() % len(peers) + log.Debug("No addresses to dial. Sending pexRequest to random peer", "peer", peers[i]) + pexR.RequestPEX(peers[i]) + } + } } // implements events.Eventable diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/switch.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/switch.go index c44974f733a1593548e26d94c5e15156a2a4b2d7..8c4bbe840bfbddab4df7ee3aebc8490eebd8f406 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/switch.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/p2p/switch.go @@ -53,10 +53,12 @@ type Switch struct { var ( ErrSwitchDuplicatePeer = errors.New("Duplicate peer") + ErrSwitchMaxPeersPerIP = errors.New("IP has too many peers") ) const ( peerDialTimeoutSeconds = 3 + maxPeersPerIP = 3 ) func NewSwitch() *Switch { @@ -119,6 +121,11 @@ func (sw *Switch) SetNodeInfo(nodeInfo *types.NodeInfo) { sw.nodeInfo = nodeInfo } +// Not goroutine safe. +func (sw *Switch) NodeInfo() *types.NodeInfo { + return sw.nodeInfo +} + func (sw *Switch) Start() { if atomic.CompareAndSwapUint32(&sw.running, 0, 1) { // Start reactors @@ -162,9 +169,14 @@ func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, er if err != nil { return nil, err } + // check version, chain id if err := sw.nodeInfo.CompatibleWith(peerNodeInfo); err != nil { return nil, err } + // avoid self + if peerNodeInfo.UUID == sw.nodeInfo.UUID { + return nil, fmt.Errorf("Ignoring connection from self") + } // the peerNodeInfo is not verified, // so we overwrite the IP with that from the conn @@ -178,10 +190,21 @@ func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, er } peer := newPeer(conn, peerNodeInfo, outbound, sw.reactorsByCh, sw.chDescs, sw.StopPeerForError) + // restrict the number of peers we're willing to connect to behind a single IP + var numPeersOnThisIP int + peers := sw.Peers().List() + for _, p := range peers { + if p.Host == peerNodeInfo.Host { + numPeersOnThisIP += 1 + } + } + if numPeersOnThisIP == maxPeersPerIP { + log.Info("Ignoring peer as we have the max allowed for that IP", "IP", peerNodeInfo.Host, "peer", peer, "max", maxPeersPerIP) + return nil, ErrSwitchMaxPeersPerIP + } + // Add the peer to .peers - if sw.peers.Add(peer) { - log.Info("Added peer", "peer", peer) - } else { + if !sw.peers.Add(peer) { log.Info("Ignoring duplicate peer", "peer", peer) return nil, ErrSwitchDuplicatePeer } @@ -189,12 +212,14 @@ func (sw *Switch) AddPeerWithConnection(conn net.Conn, outbound bool) (*Peer, er if atomic.LoadUint32(&sw.running) == 1 { sw.startInitPeer(peer) } + + log.Info("Added peer", "peer", peer) return peer, nil } func (sw *Switch) startInitPeer(peer *Peer) { - peer.start() - sw.addPeerToReactors(peer) + peer.start() // spawn send/recv routines + sw.addPeerToReactors(peer) // run AddPeer on each reactor } func (sw *Switch) DialPeerWithAddress(addr *NetAddress) (*Peer, error) { @@ -297,7 +322,7 @@ func (sw *Switch) listenerRoutine(l Listener) { } // NOTE: We don't yet have the external address of the // remote (if they have a listener at all). - // PEXReactor's pexRoutine will handle that. + // The peerHandshake will take care of that } // cleanup diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/errors.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/errors.go index 5a170a7cce5ec71ae314d72864576d492c434e41..4b1d7cb945a40bcc614546ea08587c6478c90c23 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/errors.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/errors.go @@ -14,27 +14,6 @@ func (e ErrInvalidPermission) Error() string { return fmt.Sprintf("invalid permission %d", e) } -// unknown string for permission -type ErrInvalidPermissionString string - -func (e ErrInvalidPermissionString) Error() string { - return fmt.Sprintf("invalid permission '%s'", e) -} - -// already exists (err on add) -type ErrPermissionExists string - -func (e ErrPermissionExists) Error() string { - return fmt.Sprintf("permission '%s' already exists", e) -} - -// unknown string for snative contract -type ErrInvalidSNativeString string - -func (e ErrInvalidSNativeString) Error() string { - return fmt.Sprintf("invalid snative contract '%s'", e) -} - // set=false. This error should be caught and the global // value fetched for the permission by the caller type ErrValueNotSet PermFlag diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/permissions.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/permissions.go index c530a42e547ad4ce88cabbebd4349606420774ba..0588d4f35a901bf4db6ded76ec733bbd5be44651 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/permissions.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/permissions.go @@ -3,7 +3,6 @@ package types import ( "fmt" . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" - "reflect" ) //------------------------------------------------------------------------------------------------ @@ -20,34 +19,34 @@ type PermFlag uint64 // Base permission references are like unix (the index is already bit shifted) const ( - Root PermFlag = 1 << iota // 1 - Send // 2 - Call // 4 - CreateContract // 8 - CreateAccount // 16 - Bond // 32 - Name // 64 - - DefaultBBPB = Send | Call | CreateContract | CreateAccount | Bond | Name - - // XXX: must be adjusted if base perms added/removed - NumBasePermissions uint = 7 - TopBasePermission PermFlag = 1 << (NumBasePermissions - 1) - AllBasePermissions PermFlag = TopBasePermission | (TopBasePermission - 1) - - AllSet PermFlag = AllBasePermissions | AllSNativePermissions + Root PermFlag = 1 << iota // 1 + Send // 2 + Call // 4 + CreateContract // 8 + CreateAccount // 16 + Bond // 32 + Name // 64 + NumBasePermissions uint = 7 // NOTE Adjust this too. + + TopBasePermFlag PermFlag = 1 << (NumBasePermissions - 1) + AllBasePermFlags PermFlag = TopBasePermFlag | (TopBasePermFlag - 1) + AllPermFlags PermFlag = AllBasePermFlags | AllSNativePermFlags + DefaultBasePermFlags PermFlag = Send | Call | CreateContract | CreateAccount | Bond | Name ) -// should have same ordering as above -type BasePermissionsString struct { - Root bool `json:"root,omitempty"` - Send bool `json:"send,omitempty"` - Call bool `json:"call,omitempty"` - CreateContract bool `json:"create_contract,omitempty"` - CreateAccount bool `json:"create_account,omitempty"` - Bond bool `json:"bond,omitempty"` - Name bool `json:"name,omitempty"` -} +var ( + ZeroBasePermissions = BasePermissions{0, 0} + ZeroAccountPermissions = AccountPermissions{ + Base: ZeroBasePermissions, + } + DefaultAccountPermissions = AccountPermissions{ + Base: BasePermissions{ + Perms: DefaultBasePermFlags, + SetBit: AllPermFlags, + }, + Roles: []string{}, + } +) //--------------------------------------------------------------------------------------------- @@ -60,10 +59,6 @@ type BasePermissions struct { SetBit PermFlag `json:"set"` } -func NewBasePermissions() *BasePermissions { - return &BasePermissions{0, 0} -} - // Get a permission value. ty should be a power of 2. // ErrValueNotSet is returned if the permission's set bit is off, // and should be caught by caller so the global permission can be fetched @@ -108,32 +103,15 @@ func (p *BasePermissions) IsSet(ty PermFlag) bool { return p.SetBit&ty > 0 } -func (p *BasePermissions) Copy() *BasePermissions { - if p == nil { - return nil - } - return &BasePermissions{ - Perms: p.Perms, - SetBit: p.SetBit, - } -} - -func (p *BasePermissions) String() string { +func (p BasePermissions) String() string { return fmt.Sprintf("Base: %b; Set: %b", p.Perms, p.SetBit) } //--------------------------------------------------------------------------------------------- type AccountPermissions struct { - Base *BasePermissions `json:"base"` - Roles []string `json:"roles"` -} - -func NewAccountPermissions() *AccountPermissions { - return &AccountPermissions{ - Base: NewBasePermissions(), - Roles: []string{}, - } + Base BasePermissions `json:"base"` + Roles []string `json:"roles"` } // Returns true if the role is found @@ -175,75 +153,7 @@ func (aP *AccountPermissions) RmRole(role string) bool { return false } -func (aP *AccountPermissions) Copy() *AccountPermissions { - if aP == nil { - return nil - } - r := make([]string, len(aP.Roles)) - copy(r, aP.Roles) - return &AccountPermissions{ - Base: aP.Base.Copy(), - Roles: r, - } -} - -func NewDefaultAccountPermissions() *AccountPermissions { - return &AccountPermissions{ - Base: &BasePermissions{ - Perms: DefaultBBPB, - SetBit: AllSet, - }, - Roles: []string{}, - } -} - -//--------------------------------------------------------------------------------------------- -// Utilities to make bitmasks human readable - -func NewDefaultAccountPermissionsString() BasePermissionsString { - return BasePermissionsString{ - Root: false, - Bond: true, - Send: true, - Call: true, - Name: true, - CreateAccount: true, - CreateContract: true, - } -} - -func AccountPermissionsFromStrings(perms *BasePermissionsString, roles []string) (*AccountPermissions, error) { - base := NewBasePermissions() - permRv := reflect.ValueOf(perms) - for i := uint(0); i < uint(permRv.NumField()); i++ { - v := permRv.Field(int(i)).Bool() - base.Set(1<<i, v) - } - - aP := &AccountPermissions{ - Base: base, - Roles: make([]string, len(roles)), - } - copy(aP.Roles, roles) - return aP, nil -} - -func AccountPermissionsToStrings(aP *AccountPermissions) (*BasePermissionsString, []string, error) { - perms := new(BasePermissionsString) - permsRv := reflect.ValueOf(perms).Elem() - for i := uint(0); i < NumBasePermissions; i++ { - pf := PermFlag(1 << i) - if aP.Base.IsSet(pf) { - // won't err if the bit is set - v, _ := aP.Base.Get(pf) - f := permsRv.Field(int(i)) - f.SetBool(v) - } - } - roles := make([]string, len(aP.Roles)) - copy(roles, aP.Roles) - return perms, roles, nil -} +//-------------------------------------------------------------------------------- func PermFlagToString(pf PermFlag) (perm string, err error) { switch pf { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/snatives.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/snatives.go index d4e0ee4bacbd730aee06c9d4973bb7aa9fe76294..be1d0b2498255c4e6b122469e20e31889103aabb 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/snatives.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types/snatives.go @@ -2,13 +2,13 @@ package types const ( // first 32 bits of BasePermission are for chain, second 32 are for snative - FirstSNativePerm PermFlag = 1 << 32 + FirstSNativePermFlag PermFlag = 1 << 32 ) // we need to reset iota with no const block const ( // each snative has an associated permission flag - HasBasePerm PermFlag = FirstSNativePerm << iota + HasBasePerm PermFlag = FirstSNativePermFlag << iota SetBasePerm UnsetBasePerm SetGlobalPerm @@ -16,9 +16,8 @@ const ( HasRole AddRole RmRole + NumSNativePermissions uint = 8 // NOTE adjust this too - // XXX: must be adjusted if snative's added/removed - NumSNativePermissions uint = 8 - TopSNativePermission PermFlag = FirstSNativePerm << (NumSNativePermissions - 1) - AllSNativePermissions PermFlag = (TopSNativePermission | (TopSNativePermission - 1)) &^ (FirstSNativePerm - 1) + TopSNativePermFlag PermFlag = FirstSNativePermFlag << (NumSNativePermissions - 1) + AllSNativePermFlags PermFlag = (TopSNativePermFlag | (TopSNativePermFlag - 1)) &^ (FirstSNativePermFlag - 1) ) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/accounts.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/accounts.go index d256242cfeda60efd629013479318582de0949ee..8461ef0b54d7a9eb192ccfe2763ff6ee5b54047c 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/accounts.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/accounts.go @@ -47,7 +47,7 @@ func GetStorage(address, key []byte) (*ctypes.ResponseGetStorage, error) { } func ListAccounts() (*ctypes.ResponseListAccounts, error) { - var blockHeight uint + var blockHeight int var accounts []*acm.Account state := consensusState.GetState() blockHeight = state.LastBlockHeight diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/blocks.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/blocks.go index 28d3db7ac286c5a2f7973767f1b7bc328464dfed..edce985a7a3b6811cfff44720a33670e3ef08598 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/blocks.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/blocks.go @@ -9,14 +9,14 @@ import ( //----------------------------------------------------------------------------- -func BlockchainInfo(minHeight, maxHeight uint) (*ctypes.ResponseBlockchainInfo, error) { +func BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResponseBlockchainInfo, error) { if maxHeight == 0 { maxHeight = blockStore.Height() } else { - maxHeight = MinUint(blockStore.Height(), maxHeight) + maxHeight = MinInt(blockStore.Height(), maxHeight) } if minHeight == 0 { - minHeight = uint(MaxInt(1, int(maxHeight)-20)) + minHeight = MaxInt(1, maxHeight-20) } log.Debug("BlockchainInfoHandler", "maxHeight", maxHeight, "minHeight", minHeight) @@ -31,7 +31,7 @@ func BlockchainInfo(minHeight, maxHeight uint) (*ctypes.ResponseBlockchainInfo, //----------------------------------------------------------------------------- -func GetBlock(height uint) (*ctypes.ResponseGetBlock, error) { +func GetBlock(height int) (*ctypes.ResponseGetBlock, error) { if height == 0 { return nil, fmt.Errorf("height must be greater than 0") } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/consensus.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/consensus.go index c089a8da4cd0fcfdc2d0c80405c66c6b5336d44b..69efc084d48337ff521a7a876e06028abbd5dd79 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/consensus.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/consensus.go @@ -8,17 +8,17 @@ import ( ) func ListValidators() (*ctypes.ResponseListValidators, error) { - var blockHeight uint + var blockHeight int var bondedValidators []*sm.Validator var unbondingValidators []*sm.Validator state := consensusState.GetState() blockHeight = state.LastBlockHeight - state.BondedValidators.Iterate(func(index uint, val *sm.Validator) bool { + state.BondedValidators.Iterate(func(index int, val *sm.Validator) bool { bondedValidators = append(bondedValidators, val) return false }) - state.UnbondingValidators.Iterate(func(index uint, val *sm.Validator) bool { + state.UnbondingValidators.Iterate(func(index int, val *sm.Validator) bool { unbondingValidators = append(unbondingValidators, val) return false }) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/mempool.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/mempool.go index f7fa944286e6f7c2b7ffea873064a35d630c7e1b..47f2f543ac874349ac01fdfa86cad2c3f3039578 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/mempool.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/mempool.go @@ -16,14 +16,14 @@ func BroadcastTx(tx types.Tx) (*ctypes.Receipt, error) { return nil, fmt.Errorf("Error broadcasting transaction: %v", err) } - txHash := types.TxId(mempoolReactor.Mempool.GetState().ChainID, tx) + txHash := types.TxID(mempoolReactor.Mempool.GetState().ChainID, tx) var createsContract uint8 var contractAddr []byte // check if creates new contract if callTx, ok := tx.(*types.CallTx); ok { if len(callTx.Address) == 0 { createsContract = 1 - contractAddr = state.NewContractAddress(callTx.Input.Address, uint64(callTx.Input.Sequence)) + contractAddr = state.NewContractAddress(callTx.Input.Address, callTx.Input.Sequence) } } return &ctypes.Receipt{txHash, createsContract, contractAddr}, nil diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/names.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/names.go index 0fc60ecff0b5ad948e631c196dad3f4dc4874c32..9dabe36933100aff59231e8de486cc774e8411b5 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/names.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/names.go @@ -17,7 +17,7 @@ func GetName(name string) (*types.NameRegEntry, error) { } func ListNames() (*ctypes.ResponseListNames, error) { - var blockHeight uint + var blockHeight int var names []*types.NameRegEntry state := consensusState.GetState() blockHeight = state.LastBlockHeight diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/net.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/net.go index 715e27fdc18aeec6e73cab96f8f9e9a2c1850e0a..6de69f39f6d13422cab18e2d1cdc6fdb07b8b429 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/net.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/net.go @@ -31,9 +31,7 @@ func Status() (*ctypes.ResponseStatus, error) { } return &ctypes.ResponseStatus{ - Moniker: config.GetString("moniker"), - ChainID: config.GetString("chain_id"), - Version: config.GetString("version"), + NodeInfo: p2pSwitch.NodeInfo(), GenesisHash: genesisHash, PubKey: privValidator.PubKey, LatestBlockHash: latestBlockHash, diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/txs.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/txs.go index 4088542695ae4650ceb33ca993df5ee2f5b8464b..83aee445aa4bae846d3b9f710ae0a2ac3790e952 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/txs.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/txs.go @@ -15,7 +15,7 @@ func toVMAccount(acc *account.Account) *vm.Account { Address: LeftPadWord256(acc.Address), Balance: acc.Balance, Code: acc.Code, // This is crazy. - Nonce: uint64(acc.Sequence), + Nonce: int64(acc.Sequence), StorageRoot: LeftPadWord256(acc.StorageRoot), Other: acc.PubKey, } @@ -36,14 +36,14 @@ func Call(address, data []byte) (*ctypes.ResponseCall, error) { caller := &vm.Account{Address: Zero256} txCache := state.NewTxCache(cache) params := vm.Params{ - BlockHeight: uint64(st.LastBlockHeight), + BlockHeight: int64(st.LastBlockHeight), BlockHash: LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: 10000000, } vmach := vm.NewVM(txCache, params, caller.Address, nil) - gas := uint64(1000000000) + gas := int64(1000000000) ret, err := vmach.Call(caller, callee, callee.Code, data, 0, &gas) if err != nil { return nil, err @@ -61,14 +61,14 @@ func CallCode(code, data []byte) (*ctypes.ResponseCall, error) { caller := &vm.Account{Address: Zero256} txCache := state.NewTxCache(cache) params := vm.Params{ - BlockHeight: uint64(st.LastBlockHeight), + BlockHeight: int64(st.LastBlockHeight), BlockHash: LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: 10000000, } vmach := vm.NewVM(txCache, params, caller.Address, nil) - gas := uint64(1000000000) + gas := int64(1000000000) ret, err := vmach.Call(caller, callee, code, data, 0, &gas) if err != nil { return nil, err diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/types/responses.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/types/responses.go index bc227e41b2e64bb8da208d419234e28c9241b095..bd1e88b8da71f44eaee4bac7f66898c24ff65917 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/types/responses.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/core/types/responses.go @@ -13,12 +13,12 @@ type ResponseGetStorage struct { type ResponseCall struct { Return []byte `json:"return"` - GasUsed uint64 `json:"gas_used"` + GasUsed int64 `json:"gas_used"` // TODO ... } type ResponseListAccounts struct { - BlockHeight uint `json:"block_height"` + BlockHeight int `json:"block_height"` Accounts []*account.Account `json:"accounts"` } @@ -33,7 +33,7 @@ type ResponseDumpStorage struct { } type ResponseBlockchainInfo struct { - LastHeight uint `json:"last_height"` + LastHeight int `json:"last_height"` BlockMetas []*types.BlockMeta `json:"block_metas"` } @@ -49,14 +49,12 @@ type Receipt struct { } type ResponseStatus struct { - Moniker string `json:"moniker"` - ChainID string `json:"chain_id"` - Version string `json:"version"` - GenesisHash []byte `json:"genesis_hash"` - PubKey account.PubKey `json:"pub_key"` - LatestBlockHash []byte `json:"latest_block_hash"` - LatestBlockHeight uint `json:"latest_block_height"` - LatestBlockTime int64 `json:"latest_block_time"` // nano + NodeInfo *types.NodeInfo `json:"node_info"` + GenesisHash []byte `json:"genesis_hash"` + PubKey account.PubKey `json:"pub_key"` + LatestBlockHash []byte `json:"latest_block_hash"` + LatestBlockHeight int `json:"latest_block_height"` + LatestBlockTime int64 `json:"latest_block_time"` // nano } type ResponseNetInfo struct { @@ -71,7 +69,7 @@ type Peer struct { } type ResponseListValidators struct { - BlockHeight uint `json:"block_height"` + BlockHeight int `json:"block_height"` BondedValidators []*sm.Validator `json:"bonded_validators"` UnbondingValidators []*sm.Validator `json:"unbonding_validators"` } @@ -82,6 +80,6 @@ type ResponseDumpConsensusState struct { } type ResponseListNames struct { - BlockHeight uint `json:"block_height"` + BlockHeight int `json:"block_height"` Names []*types.NameRegEntry `json:"names"` } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/server/handlers.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/server/handlers.go index 4205bbeafeafeb8a155162d044eb3a8bce6699d6..c394aa0e88b0bcb532dcd161f70f73acb1b04bea 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/server/handlers.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/rpc/server/handlers.go @@ -224,7 +224,7 @@ type WSConnection struct { wsConn *websocket.Conn writeChan chan WSResponse quitChan chan struct{} - failedSends uint + failedSends int started uint32 stopped uint32 @@ -346,7 +346,7 @@ func (con *WSConnection) write() { log.Error("Failed to marshal WSResponse to JSON", "error", err) } else { if err := con.wsConn.WriteMessage(websocket.TextMessage, buf.Bytes()); err != nil { - log.Error("Failed to write response on websocket", "error", err) + log.Warn("Failed to write response on websocket", "error", err) con.Stop() return } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/block_cache.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/block_cache.go index d01d311d9abd360effbe9c7d550de1ef8a1315f3..a388fd7d7ba98fb9692bc7235beb61cb77ed286e 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/block_cache.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/block_cache.go @@ -63,8 +63,8 @@ func (cache *BlockCache) GetAccount(addr []byte) *ac.Account { func (cache *BlockCache) UpdateAccount(acc *ac.Account) { addr := acc.Address - // SANITY CHECK _, storage, removed, _ := cache.accounts[string(addr)].unpack() + // SANITY CHECK if removed { panic("UpdateAccount on a removed account") } @@ -95,9 +95,11 @@ func (cache *BlockCache) GetStorage(addr Word256, key Word256) (value Word256) { // Get or load storage acc, storage, removed, dirty := cache.accounts[string(addr.Postfix(20))].unpack() + // SANITY CHECK if removed { panic("GetStorage() on removed account") } + // SANITY CHECK END if acc != nil && storage == nil { storage = makeStorage(cache.db, acc.StorageRoot) cache.accounts[string(addr.Postfix(20))] = accountInfo{acc, storage, false, dirty} @@ -117,10 +119,12 @@ func (cache *BlockCache) GetStorage(addr Word256, key Word256) (value Word256) { // NOTE: Set value to zero to removed from the trie. func (cache *BlockCache) SetStorage(addr Word256, key Word256, value Word256) { + // SANITY CHECK _, _, removed, _ := cache.accounts[string(addr.Postfix(20))].unpack() if removed { panic("SetStorage() on a removed account") } + // SANITY CHECK END cache.storages[Tuple256{addr, key}] = storageInfo{value, true} } @@ -143,12 +147,6 @@ func (cache *BlockCache) GetNameRegEntry(name string) *types.NameRegEntry { func (cache *BlockCache) UpdateNameRegEntry(entry *types.NameRegEntry) { name := entry.Name - // SANITY CHECK - _, removed, _ := cache.names[name].unpack() - if removed { - panic("UpdateNameRegEntry on a removed name") - } - // SANITY CHECK END cache.names[name] = nameInfo{entry, false, true} } @@ -224,6 +222,7 @@ func (cache *BlockCache) Sync() { if removed { removed := cache.backend.RemoveAccount(acc.Address) if !removed { + // SOMETHING HORRIBLE HAS GONE WRONG panic(Fmt("Could not remove account to be removed: %X", acc.Address)) } } else { @@ -257,6 +256,7 @@ func (cache *BlockCache) Sync() { if removed { removed := cache.backend.RemoveNameRegEntry(nameStr) if !removed { + // SOMETHING HORRIBLE HAS GONE WRONG panic(Fmt("Could not remove namereg entry to be removed: %s", nameStr)) } } else { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/execution.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/execution.go index e59d92b278234e66b32e8c7f381c089e44eb2211..a5f3ca313b510313190831fe459025c3aa331c61 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/execution.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/execution.go @@ -23,8 +23,8 @@ func ExecBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade // State.Hash should match block.StateHash stateHash := s.Hash() if !bytes.Equal(stateHash, block.StateHash) { - return fmt.Errorf("Invalid state hash. Expected %X, got %X", - stateHash, block.StateHash) + return errors.New(Fmt("Invalid state hash. Expected %X, got %X", + stateHash, block.StateHash)) } return nil } @@ -39,53 +39,30 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade return err } - // Validate block Validation. + // Validate block LastValidation. if block.Height == 1 { - if len(block.Validation.Commits) != 0 { - return errors.New("Block at height 1 (first block) should have no Validation commits") + if len(block.LastValidation.Precommits) != 0 { + return errors.New("Block at height 1 (first block) should have no LastValidation precommits") } } else { - if uint(len(block.Validation.Commits)) != s.LastBondedValidators.Size() { + if len(block.LastValidation.Precommits) != s.LastBondedValidators.Size() { return errors.New(Fmt("Invalid block validation size. Expected %v, got %v", - s.LastBondedValidators.Size(), len(block.Validation.Commits))) + s.LastBondedValidators.Size(), len(block.LastValidation.Precommits))) } - var sumVotingPower uint64 - s.LastBondedValidators.Iterate(func(index uint, val *Validator) bool { - commit := block.Validation.Commits[index] - if commit.IsZero() { - return false - } else { - vote := &types.Vote{ - Height: block.Height - 1, - Round: commit.Round, - Type: types.VoteTypeCommit, - BlockHash: block.LastBlockHash, - BlockParts: block.LastBlockParts, - } - if val.PubKey.VerifyBytes(account.SignBytes(s.ChainID, vote), commit.Signature) { - sumVotingPower += val.VotingPower - return false - } else { - log.Warn(Fmt("Invalid validation signature.\nval: %v\nvote: %v", val, vote)) - err = errors.New("Invalid validation signature") - return true - } - } - }) + err := s.LastBondedValidators.VerifyValidation( + s.ChainID, s.LastBlockHash, s.LastBlockParts, block.Height-1, block.LastValidation) if err != nil { return err } - if sumVotingPower <= s.LastBondedValidators.TotalVotingPower()*2/3 { - return errors.New("Insufficient validation voting power") - } } // Update Validator.LastCommitHeight as necessary. - for i, commit := range block.Validation.Commits { - if commit.IsZero() { + // If we panic in here, something has gone horribly wrong + for i, precommit := range block.LastValidation.Precommits { + if precommit == nil { continue } - _, val := s.LastBondedValidators.GetByIndex(uint(i)) + _, val := s.LastBondedValidators.GetByIndex(i) if val == nil { panic(Fmt("Failed to fetch validator at index %v", i)) } @@ -112,7 +89,7 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade // Create BlockCache to cache changes to state. blockCache := NewBlockCache(s) - // Commit each tx + // Execute each tx for _, tx := range block.Data.Txs { err := ExecTx(blockCache, tx, true, s.evc) if err != nil { @@ -126,7 +103,7 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade // If any unbonding periods are over, // reward account with bonded coins. toRelease := []*Validator{} - s.UnbondingValidators.Iterate(func(index uint, val *Validator) bool { + s.UnbondingValidators.Iterate(func(index int, val *Validator) bool { if val.UnbondHeight+unbondingPeriodBlocks < block.Height { toRelease = append(toRelease, val) } @@ -139,8 +116,8 @@ func execBlock(s *State, block *types.Block, blockPartsHeader types.PartSetHeade // If any validators haven't signed in a while, // unbond them, they have timed out. toTimeout := []*Validator{} - s.BondedValidators.Iterate(func(index uint, val *Validator) bool { - lastActivityHeight := MaxUint(val.BondHeight, val.LastCommitHeight) + s.BondedValidators.Iterate(func(index int, val *Validator) bool { + lastActivityHeight := MaxInt(val.BondHeight, val.LastCommitHeight) if lastActivityHeight+validatorTimeoutBlocks < block.Height { log.Info("Validator timeout", "validator", val, "height", block.Height) toTimeout = append(toTimeout, val) @@ -210,7 +187,7 @@ func getOrMakeOutputs(state AccountGetter, accounts map[string]*account.Account, PubKey: nil, Sequence: 0, Balance: 0, - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } } accounts[string(out.Address)] = acc @@ -233,12 +210,14 @@ func checkInputPubKey(acc *account.Account, in *types.TxInput) error { return nil } -func validateInputs(accounts map[string]*account.Account, signBytes []byte, ins []*types.TxInput) (total uint64, err error) { +func validateInputs(accounts map[string]*account.Account, signBytes []byte, ins []*types.TxInput) (total int64, err error) { for _, in := range ins { acc := accounts[string(in.Address)] + // SANITY CHECK if acc == nil { panic("validateInputs() expects account in accounts") } + // SANITY CHECK END err = validateInput(acc, signBytes, in) if err != nil { return @@ -261,8 +240,8 @@ func validateInput(acc *account.Account, signBytes []byte, in *types.TxInput) (e // Check sequences if acc.Sequence+1 != in.Sequence { return types.ErrTxInvalidSequence{ - Got: uint64(in.Sequence), - Expected: uint64(acc.Sequence + 1), + Got: in.Sequence, + Expected: acc.Sequence + 1, } } // Check amount @@ -272,7 +251,7 @@ func validateInput(acc *account.Account, signBytes []byte, in *types.TxInput) (e return nil } -func validateOutputs(outs []*types.TxOutput) (total uint64, err error) { +func validateOutputs(outs []*types.TxOutput) (total int64, err error) { for _, out := range outs { // Check TxOutput basic if err := out.ValidateBasic(); err != nil { @@ -287,12 +266,14 @@ func validateOutputs(outs []*types.TxOutput) (total uint64, err error) { func adjustByInputs(accounts map[string]*account.Account, ins []*types.TxInput) { for _, in := range ins { acc := accounts[string(in.Address)] + // SANITY CHECK if acc == nil { panic("adjustByInputs() expects account in accounts") } if acc.Balance < in.Amount { panic("adjustByInputs() expects sufficient funds") } + // SANITY CHECK END acc.Balance -= in.Amount acc.Sequence += 1 } @@ -301,18 +282,21 @@ func adjustByInputs(accounts map[string]*account.Account, ins []*types.TxInput) func adjustByOutputs(accounts map[string]*account.Account, outs []*types.TxOutput) { for _, out := range outs { acc := accounts[string(out.Address)] + // SANITY CHECK if acc == nil { panic("adjustByOutputs() expects account in accounts") } + // SANITY CHECK END acc.Balance += out.Amount } } // If the tx is invalid, an error will be returned. // Unlike ExecBlock(), state will not be altered. -func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Fireable) error { +func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Fireable) (err error) { + // TODO: do something with fees - fees := uint64(0) + fees := int64(0) _s := blockCache.State() // hack to access validators and block height // Exec tx @@ -428,14 +412,14 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea if runCall { var ( - gas uint64 = tx.GasLimit + gas int64 = tx.GasLimit err error = nil caller *vm.Account = toVMAccount(inAcc) callee *vm.Account = nil code []byte = nil txCache = NewTxCache(blockCache) params = vm.Params{ - BlockHeight: uint64(_s.LastBlockHeight), + BlockHeight: int64(_s.LastBlockHeight), BlockHash: LeftPadWord256(_s.LastBlockHash), BlockTime: _s.LastBlockTime.Unix(), GasLimit: 10000000, @@ -481,12 +465,9 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea txCache.UpdateAccount(caller) // because we bumped nonce txCache.UpdateAccount(callee) // so the txCache knows about the callee and the create and/or transfer takes effect - vmach := vm.NewVM(txCache, params, caller.Address, account.HashSignBytes(_s.ChainID, tx)) + vmach := vm.NewVM(txCache, params, caller.Address, types.TxID(_s.ChainID, tx)) vmach.SetFireable(evc) - vmach.EnablePermissions() // permission checks on CALL/CREATE - vmach.EnableSNatives() // allows calls to snatives (with permission checks) - // NOTE: Call() transfers the value from caller to callee iff call succeeds. ret, err := vmach.Call(caller, callee, code, tx.Data, value, &gas) exception := "" @@ -569,8 +550,8 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea // let's say cost of a name for one block is len(data) + 32 costPerBlock := types.NameCostPerBlock * types.NameCostPerByte * tx.BaseEntryCost() - expiresIn := value / uint64(costPerBlock) - lastBlockHeight := uint64(_s.LastBlockHeight) + expiresIn := int(value / costPerBlock) + lastBlockHeight := _s.LastBlockHeight log.Debug("New NameTx", "value", value, "costPerBlock", costPerBlock, "expiresIn", expiresIn, "lastBlock", lastBlockHeight) @@ -601,7 +582,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea // and changing the data if expired { if expiresIn < types.MinNameRegistrationPeriod { - return fmt.Errorf("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod) + return errors.New(Fmt("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod)) } entry.Expires = lastBlockHeight + expiresIn entry.Owner = tx.Input.Address @@ -609,11 +590,11 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea } else { // since the size of the data may have changed // we use the total amount of "credit" - oldCredit := (entry.Expires - lastBlockHeight) * types.BaseEntryCost(entry.Name, entry.Data) + oldCredit := int64(entry.Expires-lastBlockHeight) * types.BaseEntryCost(entry.Name, entry.Data) credit := oldCredit + value - expiresIn = credit / costPerBlock + expiresIn = int(credit / costPerBlock) if expiresIn < types.MinNameRegistrationPeriod { - return fmt.Errorf("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod) + return errors.New(Fmt("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod)) } entry.Expires = lastBlockHeight + expiresIn log.Debug("Updated namereg entry", "name", entry.Name, "expiresIn", expiresIn, "oldCredit", oldCredit, "value", value, "credit", credit) @@ -623,7 +604,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea } } else { if expiresIn < types.MinNameRegistrationPeriod { - return fmt.Errorf("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod) + return errors.New(Fmt("Names must be registered for at least %d blocks", types.MinNameRegistrationPeriod)) } // entry does not exist, so create it entry = &types.NameRegEntry{ @@ -723,6 +704,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea Accum: 0, }) if !added { + // SOMETHING HAS GONE HORRIBLY WRONG panic("Failed to add validator") } if evc != nil { @@ -802,21 +784,14 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea if tx.VoteA.Height != tx.VoteB.Height { return errors.New("DupeoutTx heights don't match") } - if tx.VoteA.Type == types.VoteTypeCommit && tx.VoteA.Round < tx.VoteB.Round { - // Check special case (not an error, validator must be slashed!) - // Validators should not sign another vote after committing. - } else if tx.VoteB.Type == types.VoteTypeCommit && tx.VoteB.Round < tx.VoteA.Round { - // We need to check both orderings of the votes - } else { - if tx.VoteA.Round != tx.VoteB.Round { - return errors.New("DupeoutTx rounds don't match") - } - if tx.VoteA.Type != tx.VoteB.Type { - return errors.New("DupeoutTx types don't match") - } - if bytes.Equal(tx.VoteA.BlockHash, tx.VoteB.BlockHash) { - return errors.New("DupeoutTx blockhashes shouldn't match") - } + if tx.VoteA.Round != tx.VoteB.Round { + return errors.New("DupeoutTx rounds don't match") + } + if tx.VoteA.Type != tx.VoteB.Type { + return errors.New("DupeoutTx types don't match") + } + if bytes.Equal(tx.VoteA.BlockHash, tx.VoteB.BlockHash) { + return errors.New("DupeoutTx blockhashes shouldn't match") } // Good! (Bad validator!) @@ -827,6 +802,8 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea return nil default: + // SANITY CHECK (binary decoding should catch bad tx types + // before they get here panic("Unknown Tx type") } } @@ -835,7 +812,7 @@ func ExecTx(blockCache *BlockCache, tx_ types.Tx, runCall bool, evc events.Firea // Get permission on an account or fall back to global value func HasPermission(state AccountGetter, acc *account.Account, perm ptypes.PermFlag) bool { - if perm > ptypes.AllBasePermissions { + if perm > ptypes.AllBasePermFlags { panic("Checking an unknown permission in state should never happen") } @@ -847,10 +824,14 @@ func HasPermission(state AccountGetter, acc *account.Account, perm ptypes.PermFl v, err := acc.Permissions.Base.Get(perm) if _, ok := err.(ptypes.ErrValueNotSet); ok { + log.Debug("Account does not have permission", "account", acc, "accPermissions", acc.Permissions, "perm", perm) if state == nil { panic("All known global permissions should be set!") } + log.Debug("Querying GlobalPermissionsAddress") return HasPermission(nil, state.GetAccount(ptypes.GlobalPermissionsAddress), perm) + } else { + log.Debug("Account has permission", "account", acc, "accPermissions", acc.Permissions, "perm", perm) } return v } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/genesis.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/genesis.go index 2fda4ba0766ba25efe7de282037db6db5b2f1857..c936f4f2ba566b7feca7c758e43aee3afc9c6d5b 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/genesis.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/genesis.go @@ -2,6 +2,7 @@ package state import ( "io/ioutil" + "os" "time" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/account" @@ -23,19 +24,19 @@ var GenDocKey = []byte("GenDocKey") type BasicAccount struct { Address []byte `json:"address"` - Amount uint64 `json:"amount"` + Amount int64 `json:"amount"` } type GenesisAccount struct { Address []byte `json:"address"` - Amount uint64 `json:"amount"` + Amount int64 `json:"amount"` Name string `json:"name"` Permissions *ptypes.AccountPermissions `json:"permissions"` } type GenesisValidator struct { PubKey account.PubKeyEd25519 `json:"pub_key"` - Amount uint64 `json:"amount"` + Amount int64 `json:"amount"` Name string `json:"name"` UnbondTo []BasicAccount `json:"unbond_to"` } @@ -59,7 +60,8 @@ func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { var err error binary.ReadJSON(&genState, jsonBlob, &err) if err != nil { - panic(Fmt("Couldn't read GenesisDoc: %v", err)) + log.Error(Fmt("Couldn't read GenesisDoc: %v", err)) + os.Exit(1) } return } @@ -67,7 +69,8 @@ func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*GenesisDoc, *State) { jsonBlob, err := ioutil.ReadFile(genDocFile) if err != nil { - panic(Fmt("Couldn't read GenesisDoc file: %v", err)) + log.Error(Fmt("Couldn't read GenesisDoc file: %v", err)) + os.Exit(1) } genDoc := GenesisDocFromJSON(jsonBlob) return genDoc, MakeGenesisState(db, genDoc) @@ -85,9 +88,9 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State { // Make accounts state tree accounts := merkle.NewIAVLTree(binary.BasicCodec, account.AccountCodec, defaultAccountsCacheCapacity, db) for _, genAcc := range genDoc.Accounts { - perm := ptypes.NewDefaultAccountPermissions() + perm := ptypes.ZeroAccountPermissions if genAcc.Permissions != nil { - perm = genAcc.Permissions + perm = *genAcc.Permissions } acc := &account.Account{ Address: genAcc.Address, @@ -101,12 +104,12 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State { // global permissions are saved as the 0 address // so they are included in the accounts tree - globalPerms := ptypes.NewDefaultAccountPermissions() + globalPerms := ptypes.DefaultAccountPermissions if genDoc.Params != nil && genDoc.Params.GlobalPermissions != nil { - globalPerms = genDoc.Params.GlobalPermissions + globalPerms = *genDoc.Params.GlobalPermissions // XXX: make sure the set bits are all true // Without it the HasPermission() functions will fail - globalPerms.Base.SetBit = ptypes.AllSet + globalPerms.Base.SetBit = ptypes.AllPermFlags } permsAcc := &account.Account{ @@ -151,7 +154,7 @@ func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State { // Make namereg tree nameReg := merkle.NewIAVLTree(binary.BasicCodec, NameRegCodec, 0, db) - // TODO: add names to genesis.json + // TODO: add names, contracts to genesis.json // IAVLTrees must be persisted before copy operations. accounts.Save() diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/genesis_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/genesis_test.go index eadd1738c6efbdd6191458f6419ac3c6e03b8c48..5a83d33c3e0c49d72ee2cc42db5f91da591dde99 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/genesis_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/genesis_test.go @@ -16,7 +16,7 @@ var send1, name1, call1 = 1, 1, 0 var perms, setbit = 66, 70 var accName = "me" var roles1 = []string{"master", "universal-ruler"} -var amt1 uint64 = 1000000 +var amt1 int64 = 1000000 var g1 = fmt.Sprintf(` { "chain_id":"%s", diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/permissions_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/permissions_test.go index 7b4a482de98338de53fadef719c551e44de82706..61887f337dcfb046afaa2b1fc988dabcec3eb825 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/permissions_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/permissions_test.go @@ -92,16 +92,17 @@ func makeUsers(n int) []*account.PrivAccount { } var ( - PermsAllFalse = ptypes.NewAccountPermissions() + PermsAllFalse = ptypes.ZeroAccountPermissions ) -func newBaseGenDoc(globalPerm, accountPerm *ptypes.AccountPermissions) GenesisDoc { +func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) GenesisDoc { genAccounts := []GenesisAccount{} for _, u := range user[:5] { + accountPermCopy := accountPerm // Create new instance for custom overridability. genAccounts = append(genAccounts, GenesisAccount{ Address: u.Address, Amount: 1000000, - Permissions: accountPerm.Copy(), + Permissions: &accountPermCopy, }) } @@ -109,7 +110,7 @@ func newBaseGenDoc(globalPerm, accountPerm *ptypes.AccountPermissions) GenesisDo GenesisTime: time.Now(), ChainID: chainID, Params: &GenesisParams{ - GlobalPermissions: globalPerm, + GlobalPermissions: &globalPerm, }, Accounts: genAccounts, Validators: []GenesisValidator{ @@ -353,7 +354,7 @@ func TestCallPermission(t *testing.T) { Code: []byte{0x60}, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } st.UpdateAccount(simpleAcc) @@ -377,7 +378,7 @@ func TestCallPermission(t *testing.T) { Code: contractCode, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } blockCache.UpdateAccount(caller1Acc) @@ -421,7 +422,7 @@ func TestCallPermission(t *testing.T) { Code: contractCode2, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } caller1Acc.Permissions.Base.Set(ptypes.Call, false) caller2Acc.Permissions.Base.Set(ptypes.Call, true) @@ -478,7 +479,7 @@ func TestCreatePermission(t *testing.T) { t.Fatal("Transaction failed", err) } // ensure the contract is there - contractAddr := NewContractAddress(tx.Input.Address, uint64(tx.Input.Sequence)) + contractAddr := NewContractAddress(tx.Input.Address, tx.Input.Sequence) contractAcc := blockCache.GetAccount(contractAddr) if contractAcc == nil { t.Fatalf("failed to create contract %X", contractAddr) @@ -503,7 +504,7 @@ func TestCreatePermission(t *testing.T) { t.Fatal("Transaction failed", err) } // ensure the contract is there - contractAddr = NewContractAddress(tx.Input.Address, uint64(tx.Input.Sequence)) + contractAddr = NewContractAddress(tx.Input.Address, tx.Input.Sequence) contractAcc = blockCache.GetAccount(contractAddr) if contractAcc == nil { t.Fatalf("failed to create contract %X", contractAddr) @@ -553,7 +554,7 @@ func TestCreatePermission(t *testing.T) { Code: code, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } contractAcc.Permissions.Base.Set(ptypes.Call, true) contractAcc.Permissions.Base.Set(ptypes.CreateContract, true) @@ -805,7 +806,7 @@ func TestCreateAccountPermission(t *testing.T) { Code: contractCode, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } blockCache.UpdateAccount(caller1Acc) @@ -856,7 +857,7 @@ func TestSNativeCALL(t *testing.T) { Code: nil, Sequence: 0, StorageRoot: Zero256.Bytes(), - Permissions: ptypes.NewAccountPermissions(), + Permissions: ptypes.ZeroAccountPermissions, } doug.Permissions.Base.Set(ptypes.Call, true) blockCache.UpdateAccount(doug) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/priv_validator.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/priv_validator.go index 1a9e1048ddce084516658c5342e86d8202f8c00e..c0aba08a59ae61ce6b17c0f0eeb8281217109c5d 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/priv_validator.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/priv_validator.go @@ -1,7 +1,5 @@ package state -// TODO: This logic is crude. Should be more transactional. - import ( "errors" "fmt" @@ -23,18 +21,17 @@ const ( stepPropose = 1 stepPrevote = 2 stepPrecommit = 3 - stepCommit = 4 ) -func voteToStep(vote *types.Vote) uint8 { +func voteToStep(vote *types.Vote) int8 { switch vote.Type { case types.VoteTypePrevote: return stepPrevote case types.VoteTypePrecommit: return stepPrecommit - case types.VoteTypeCommit: - return stepCommit default: + // SANITY CHECK (binary decoding should catch bad vote types + // before they get here (right?!) panic("Unknown vote type") } } @@ -43,9 +40,9 @@ type PrivValidator struct { Address []byte `json:"address"` PubKey account.PubKeyEd25519 `json:"pub_key"` PrivKey account.PrivKeyEd25519 `json:"priv_key"` - LastHeight uint `json:"last_height"` - LastRound uint `json:"last_round"` - LastStep uint8 `json:"last_step"` + LastHeight int `json:"last_height"` + LastRound int `json:"last_round"` + LastStep int8 `json:"last_step"` // For persistence. // Overloaded for testing. @@ -74,7 +71,7 @@ func GenPrivValidator() *PrivValidator { func LoadPrivValidator(filePath string) *PrivValidator { privValJSONBytes, err := ioutil.ReadFile(filePath) if err != nil { - panic(err) + Exit(err.Error()) } privVal := binary.ReadJSON(&PrivValidator{}, privValJSONBytes, &err).(*PrivValidator) if err != nil { @@ -98,6 +95,7 @@ func (privVal *PrivValidator) Save() { func (privVal *PrivValidator) save() { if privVal.filePath == "" { + // SANITY CHECK panic("Cannot save PrivValidator: filePath not set") } jsonBytes := binary.JSONBytes(privVal) @@ -108,7 +106,6 @@ func (privVal *PrivValidator) save() { } } -// TODO: test func (privVal *PrivValidator) SignVote(chainID string, vote *types.Vote) error { privVal.mtx.Lock() defer privVal.mtx.Unlock() @@ -119,10 +116,6 @@ func (privVal *PrivValidator) SignVote(chainID string, vote *types.Vote) error { } // More cases for when the height matches if privVal.LastHeight == vote.Height { - // If attempting any sign after commit, panic - if privVal.LastStep == stepCommit { - return errors.New("SignVote on matching height after a commit") - } // If round regression, panic if privVal.LastRound > vote.Round { return errors.New("Round regression in SignVote") @@ -175,9 +168,10 @@ func (privVal *PrivValidator) SignRebondTx(chainID string, rebondTx *types.Rebon if privVal.LastHeight < rebondTx.Height { // Persist height/round/step + // Prevent doing anything else for this rebondTx.Height. privVal.LastHeight = rebondTx.Height - privVal.LastRound = math.MaxUint64 // We can't do anything else for this rebondTx.Height. - privVal.LastStep = math.MaxUint8 + privVal.LastRound = math.MaxInt32 // MaxInt64 overflows on 32bit architectures. + privVal.LastStep = math.MaxInt8 privVal.save() // Sign diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state.go index 0743a002861b258443f18d3f939ad6cad40a5e9e..263f5c2486a733f22e2f421e6047876996560ad9 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state.go @@ -2,12 +2,12 @@ package state import ( "bytes" - "fmt" "io" "time" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/account" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/binary" + . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" dbm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/db" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/events" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle" @@ -16,10 +16,10 @@ import ( var ( stateKey = []byte("stateKey") - minBondAmount = uint64(1) // TODO adjust - defaultAccountsCacheCapacity = 1000 // TODO adjust - unbondingPeriodBlocks = uint(60 * 24 * 365) // TODO probably better to make it time based. - validatorTimeoutBlocks = uint(10) // TODO adjust + minBondAmount = int64(1) // TODO adjust + defaultAccountsCacheCapacity = 1000 // TODO adjust + unbondingPeriodBlocks = int(60 * 24 * 365) // TODO probably better to make it time based. + validatorTimeoutBlocks = int(10) // TODO adjust ) //----------------------------------------------------------------------------- @@ -28,7 +28,7 @@ var ( type State struct { DB dbm.DB ChainID string - LastBlockHeight uint + LastBlockHeight int LastBlockHash []byte LastBlockParts types.PartSetHeader LastBlockTime time.Time @@ -50,7 +50,7 @@ func LoadState(db dbm.DB) *State { } else { r, n, err := bytes.NewReader(buf), new(int64), new(error) s.ChainID = binary.ReadString(r, n, err) - s.LastBlockHeight = binary.ReadUvarint(r, n, err) + s.LastBlockHeight = binary.ReadVarint(r, n, err) s.LastBlockHash = binary.ReadByteSlice(r, n, err) s.LastBlockParts = binary.ReadBinary(types.PartSetHeader{}, r, n, err).(types.PartSetHeader) s.LastBlockTime = binary.ReadTime(r, n, err) @@ -67,7 +67,8 @@ func LoadState(db dbm.DB) *State { s.nameReg = merkle.NewIAVLTree(binary.BasicCodec, NameRegCodec, 0, db) s.nameReg.Load(nameRegHash) if *err != nil { - panic(*err) + // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED + Exit(Fmt("Data has been corrupted or its spec has changed: %v\n", *err)) } // TODO: ensure that buf is completely read. } @@ -80,7 +81,7 @@ func (s *State) Save() { s.nameReg.Save() buf, n, err := new(bytes.Buffer), new(int64), new(error) binary.WriteString(s.ChainID, buf, n, err) - binary.WriteUvarint(s.LastBlockHeight, buf, n, err) + binary.WriteVarint(s.LastBlockHeight, buf, n, err) binary.WriteByteSlice(s.LastBlockHash, buf, n, err) binary.WriteBinary(s.LastBlockParts, buf, n, err) binary.WriteTime(s.LastBlockTime, buf, n, err) @@ -91,6 +92,7 @@ func (s *State) Save() { binary.WriteByteSlice(s.validatorInfos.Hash(), buf, n, err) binary.WriteByteSlice(s.nameReg.Hash(), buf, n, err) if *err != nil { + // SOMETHING HAS GONE HORRIBLY WRONG panic(*err) } s.DB.Set(stateKey, buf.Bytes()) @@ -126,11 +128,11 @@ func (s *State) Hash() []byte { s.validatorInfos, s.nameReg, } - return merkle.HashFromHashables(hashables) + return merkle.SimpleHashFromHashables(hashables) } // Mutates the block in place and updates it with new state hash. -func (s *State) SetBlockStateHash(block *types.Block) error { +func (s *State) ComputeBlockStateHash(block *types.Block) error { sCopy := s.Copy() // sCopy has no event cache in it, so this won't fire events err := execBlock(sCopy, block, types.PartSetHeader{}) @@ -149,6 +151,7 @@ func (s *State) SetDB(db dbm.DB) { //------------------------------------- // State.accounts +// Returns nil if account does not exist with given address. // The returned Account is a copy, so mutating it // has no side effects. // Implements Statelike @@ -213,11 +216,13 @@ func (s *State) unbondValidator(val *Validator) { // Move validator to UnbondingValidators val, removed := s.BondedValidators.Remove(val.Address) if !removed { + // SOMETHING HAS GONE HORRIBLY WRONG panic("Couldn't remove validator for unbonding") } val.UnbondHeight = s.LastBlockHeight + 1 added := s.UnbondingValidators.Add(val) if !added { + // SOMETHING HAS GONE HORRIBLY WRONG panic("Couldn't add validator for unbonding") } } @@ -226,11 +231,13 @@ func (s *State) rebondValidator(val *Validator) { // Move validator to BondingValidators val, removed := s.UnbondingValidators.Remove(val.Address) if !removed { + // SOMETHING HAS GONE HORRIBLY WRONG panic("Couldn't remove validator for rebonding") } val.BondHeight = s.LastBlockHeight + 1 added := s.BondedValidators.Add(val) if !added { + // SOMETHING HAS GONE HORRIBLY WRONG panic("Couldn't add validator for rebonding") } } @@ -238,17 +245,21 @@ func (s *State) rebondValidator(val *Validator) { func (s *State) releaseValidator(val *Validator) { // Update validatorInfo valInfo := s.GetValidatorInfo(val.Address) + // SANITY CHECK if valInfo == nil { panic("Couldn't find validatorInfo for release") } + // SANITY CHECK END valInfo.ReleasedHeight = s.LastBlockHeight + 1 s.SetValidatorInfo(valInfo) // Send coins back to UnbondTo outputs accounts, err := getOrMakeOutputs(s, nil, valInfo.UnbondTo) + // SANITY CHECK if err != nil { panic("Couldn't get or make unbondTo accounts") } + // SANITY CHECK END adjustByOutputs(accounts, valInfo.UnbondTo) for _, acc := range accounts { s.UpdateAccount(acc) @@ -257,6 +268,7 @@ func (s *State) releaseValidator(val *Validator) { // Remove validator from UnbondingValidators _, removed := s.UnbondingValidators.Remove(val.Address) if !removed { + // SOMETHING HAS GONE HORRIBLY WRONG panic("Couldn't remove validator for release") } } @@ -264,9 +276,11 @@ func (s *State) releaseValidator(val *Validator) { func (s *State) destroyValidator(val *Validator) { // Update validatorInfo valInfo := s.GetValidatorInfo(val.Address) + // SANITY CHECK if valInfo == nil { panic("Couldn't find validatorInfo for release") } + // SANITY CHECK END valInfo.DestroyedHeight = s.LastBlockHeight + 1 valInfo.DestroyedAmount = val.VotingPower s.SetValidatorInfo(valInfo) @@ -276,6 +290,7 @@ func (s *State) destroyValidator(val *Validator) { if !removed { _, removed := s.UnbondingValidators.Remove(val.Address) if !removed { + // SOMETHING HAS GONE HORRIBLY WRONG panic("Couldn't remove validator for destruction") } } @@ -357,5 +372,5 @@ type InvalidTxError struct { } func (txErr InvalidTxError) Error() string { - return fmt.Sprintf("Invalid tx: [%v] reason: [%v]", txErr.Tx, txErr.Reason) + return Fmt("Invalid tx: [%v] reason: [%v]", txErr.Tx, txErr.Reason) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state_test.go index 0809c63fa4d86c257a721aa17ad8637c9f638e9d..50e753abc6db51eb42e32518a4f9d3b65e638b83 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/state_test.go @@ -71,28 +71,29 @@ func TestCopyState(t *testing.T) { } } -func makeBlock(t *testing.T, state *State, commits []types.Commit, txs []types.Tx) *types.Block { +func makeBlock(t *testing.T, state *State, validation *types.Validation, txs []types.Tx) *types.Block { + if validation == nil { + validation = &types.Validation{} + } block := &types.Block{ Header: &types.Header{ ChainID: state.ChainID, Height: state.LastBlockHeight + 1, Time: state.LastBlockTime.Add(time.Minute), Fees: 0, - NumTxs: uint(len(txs)), + NumTxs: len(txs), LastBlockHash: state.LastBlockHash, LastBlockParts: state.LastBlockParts, StateHash: nil, }, - Validation: &types.Validation{ - Commits: commits, - }, + LastValidation: validation, Data: &types.Data{ Txs: txs, }, } // Fill in block StateHash - err := state.SetBlockStateHash(block) + err := state.ComputeBlockStateHash(block) if err != nil { t.Error("Error appending initial block:", err) } @@ -178,7 +179,7 @@ func TestTxSequence(t *testing.T) { // Test a variety of sequence numbers for the tx. // The tx should only pass when i == 1. for i := -1; i < 3; i++ { - sequence := acc0.Sequence + uint(i) + sequence := acc0.Sequence + i tx := types.NewSendTx() tx.AddInputWithNonce(acc0PubKey, 1, sequence) tx.AddOutput(acc1.Address, 1) @@ -215,15 +216,15 @@ func TestNameTxs(t *testing.T) { state, privAccounts, _ := RandGenesisState(3, true, 1000, 1, true, 1000) types.MinNameRegistrationPeriod = 5 - startingBlock := uint64(state.LastBlockHeight) + startingBlock := state.LastBlockHeight // try some bad names. these should all fail names := []string{"", "\n", "123#$%", "\x00", string([]byte{20, 40, 60, 80}), "baffledbythespectacleinallofthisyouseeehesaidwithouteyes", "no spaces please"} data := "something about all this just doesn't feel right." - fee := uint64(1000) - numDesiredBlocks := uint64(5) + fee := int64(1000) + numDesiredBlocks := 5 for _, name := range names { - amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) + amt := fee + int64(numDesiredBlocks)*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) tx.Sign(state.ChainID, privAccounts[0]) @@ -236,7 +237,7 @@ func TestNameTxs(t *testing.T) { name := "hold_it_chum" datas := []string{"cold&warm", "!@#$%^&*()", "<<<>>>>", "because why would you ever need a ~ or a & or even a % in a json file? make your case and we'll talk"} for _, data := range datas { - amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) + amt := fee + int64(numDesiredBlocks)*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) tx.Sign(state.ChainID, privAccounts[0]) @@ -245,7 +246,7 @@ func TestNameTxs(t *testing.T) { } } - validateEntry := func(t *testing.T, entry *types.NameRegEntry, name, data string, addr []byte, expires uint64) { + validateEntry := func(t *testing.T, entry *types.NameRegEntry, name, data string, addr []byte, expires int) { if entry == nil { t.Fatalf("Could not find name %s", name) @@ -267,7 +268,7 @@ func TestNameTxs(t *testing.T) { // try a good one, check data, owner, expiry name = "looking_good/karaoke_bar" data = "on this side of neptune there are 1234567890 people: first is OMNIVORE. Or is it. Ok this is pretty restrictive. No exclamations :(. Faces tho :')" - amt := fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) + amt := fee + int64(numDesiredBlocks)*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) tx, _ := types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) tx.Sign(state.ChainID, privAccounts[0]) if err := execTxWithState(state, tx, true); err != nil { @@ -303,7 +304,7 @@ func TestNameTxs(t *testing.T) { validateEntry(t, entry, name, data, privAccounts[0].Address, startingBlock+numDesiredBlocks*3) // fail to update it as non-owner - state.LastBlockHeight = uint(entry.Expires - 1) + state.LastBlockHeight = entry.Expires - 1 tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee) tx.Sign(state.ChainID, privAccounts[1]) if err := execTxWithState(state, tx, true); err == nil { @@ -311,27 +312,27 @@ func TestNameTxs(t *testing.T) { } // once expires, non-owner succeeds - state.LastBlockHeight = uint(entry.Expires) + state.LastBlockHeight = entry.Expires tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee) tx.Sign(state.ChainID, privAccounts[1]) if err := execTxWithState(state, tx, true); err != nil { t.Fatal(err) } entry = state.GetNameRegEntry(name) - validateEntry(t, entry, name, data, privAccounts[1].Address, uint64(state.LastBlockHeight)+numDesiredBlocks) + validateEntry(t, entry, name, data, privAccounts[1].Address, state.LastBlockHeight+numDesiredBlocks) // update it as new owner, with new data (longer), but keep the expiry! data = "In the beginning there was no thing, not even the beginning. It hadn't been here, no there, nor for that matter anywhere, not especially because it had not to even exist, let alone to not. Nothing especially odd about that." oldCredit := amt - fee numDesiredBlocks = 10 - amt = fee + (numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) - oldCredit) + amt = fee + (int64(numDesiredBlocks)*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) - oldCredit) tx, _ = types.NewNameTx(state, privAccounts[1].PubKey, name, data, amt, fee) tx.Sign(state.ChainID, privAccounts[1]) if err := execTxWithState(state, tx, true); err != nil { t.Fatal(err) } entry = state.GetNameRegEntry(name) - validateEntry(t, entry, name, data, privAccounts[1].Address, uint64(state.LastBlockHeight)+numDesiredBlocks) + validateEntry(t, entry, name, data, privAccounts[1].Address, state.LastBlockHeight+numDesiredBlocks) // test removal amt = fee @@ -350,15 +351,15 @@ func TestNameTxs(t *testing.T) { // test removal by key1 after expiry name = "looking_good/karaoke_bar" data = "some data" - amt = fee + numDesiredBlocks*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) + amt = fee + int64(numDesiredBlocks)*types.NameCostPerByte*types.NameCostPerBlock*types.BaseEntryCost(name, data) tx, _ = types.NewNameTx(state, privAccounts[0].PubKey, name, data, amt, fee) tx.Sign(state.ChainID, privAccounts[0]) if err := execTxWithState(state, tx, true); err != nil { t.Fatal(err) } entry = state.GetNameRegEntry(name) - validateEntry(t, entry, name, data, privAccounts[0].Address, uint64(state.LastBlockHeight)+numDesiredBlocks) - state.LastBlockHeight = uint(entry.Expires) + validateEntry(t, entry, name, data, privAccounts[0].Address, state.LastBlockHeight+numDesiredBlocks) + state.LastBlockHeight = entry.Expires amt = fee data = "" @@ -473,7 +474,7 @@ attack the network, they'll generate the longest chain and outpace attackers. network itself requires minimal structure. Messages are broadcast on a best effort basis, and nodes can leave and rejoin the network at will, accepting the longest proof-of-work chain as proof of what happened while they were gone ` - entryAmount := uint64(10000) + entryAmount := int64(10000) state := state.Copy() tx := &types.NameTx{ @@ -620,21 +621,19 @@ func TestAddValidator(t *testing.T) { // The validation for the next block should only require 1 signature // (the new validator wasn't active for block0) - commit0 := &types.Vote{ + precommit0 := &types.Vote{ Height: 1, Round: 0, - Type: types.VoteTypeCommit, + Type: types.VoteTypePrecommit, BlockHash: block0.Hash(), BlockParts: block0Parts.Header(), } - privValidators[0].SignVote(s0.ChainID, commit0) + privValidators[0].SignVote(s0.ChainID, precommit0) block1 := makeBlock(t, s0, - []types.Commit{ - types.Commit{ - Address: privValidators[0].Address, - Round: 0, - Signature: commit0.Signature, + &types.Validation{ + Precommits: []*types.Vote{ + precommit0, }, }, nil, ) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/test.go index b9fae8868ce8f5b741a9bf61a5439c88752d20aa..dddcbf45a56b5bdee22b385706c04f251334aa6c 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/test.go @@ -23,29 +23,29 @@ func Tempfile(prefix string) (*os.File, string) { return file, file.Name() } -func RandAccount(randBalance bool, minBalance uint64) (*account.Account, *account.PrivAccount) { +func RandAccount(randBalance bool, minBalance int64) (*account.Account, *account.PrivAccount) { privAccount := account.GenPrivAccount() - perms := ptypes.NewDefaultAccountPermissions() + perms := ptypes.DefaultAccountPermissions acc := &account.Account{ Address: privAccount.PubKey.Address(), PubKey: privAccount.PubKey, - Sequence: RandUint(), + Sequence: RandInt(), Balance: minBalance, Permissions: perms, } if randBalance { - acc.Balance += uint64(RandUint32()) + acc.Balance += int64(RandUint32()) } return acc, privAccount } -func RandValidator(randBonded bool, minBonded uint64) (*ValidatorInfo, *Validator, *PrivValidator) { +func RandValidator(randBonded bool, minBonded int64) (*ValidatorInfo, *Validator, *PrivValidator) { privVal := GenPrivValidator() _, tempFilePath := Tempfile("priv_validator_") privVal.SetFile(tempFilePath) bonded := minBonded if randBonded { - bonded += uint64(RandUint32()) + bonded += int64(RandUint32()) } valInfo := &ValidatorInfo{ Address: privVal.Address, @@ -69,16 +69,16 @@ func RandValidator(randBonded bool, minBonded uint64) (*ValidatorInfo, *Validato return valInfo, val, privVal } -func RandGenesisState(numAccounts int, randBalance bool, minBalance uint64, numValidators int, randBonded bool, minBonded uint64) (*State, []*account.PrivAccount, []*PrivValidator) { - db := dbm.NewMemDB() +func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*GenesisDoc, []*account.PrivAccount, []*PrivValidator) { accounts := make([]GenesisAccount, numAccounts) privAccounts := make([]*account.PrivAccount, numAccounts) + defaultPerms := ptypes.DefaultAccountPermissions for i := 0; i < numAccounts; i++ { account, privAccount := RandAccount(randBalance, minBalance) accounts[i] = GenesisAccount{ Address: account.Address, Amount: account.Balance, - Permissions: ptypes.NewDefaultAccountPermissions(), + Permissions: &defaultPerms, // This will get copied into each state.Account. } privAccounts[i] = privAccount } @@ -99,15 +99,19 @@ func RandGenesisState(numAccounts int, randBalance bool, minBalance uint64, numV privValidators[i] = privVal } sort.Sort(PrivValidatorsByAddress(privValidators)) - s0 := MakeGenesisState(db, &GenesisDoc{ + return &GenesisDoc{ GenesisTime: time.Now(), ChainID: "tendermint_test", Accounts: accounts, Validators: validators, - Params: &GenesisParams{ - GlobalPermissions: ptypes.NewDefaultAccountPermissions(), - }, - }) + }, privAccounts, privValidators + +} + +func RandGenesisState(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*State, []*account.PrivAccount, []*PrivValidator) { + db := dbm.NewMemDB() + genDoc, privAccounts, privValidators := RandGenesisDoc(numAccounts, randBalance, minBalance, numValidators, randBonded, minBonded) + s0 := MakeGenesisState(db, genDoc) s0.Save() return s0, privAccounts, privValidators } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/tx_cache.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/tx_cache.go index 8b8c371a209e2159f36e66d9c22eef1afd353bee..e3f4752c7bdd92e7d99cf2e9f9e9263e3e7a2d3e 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/tx_cache.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/tx_cache.go @@ -69,7 +69,7 @@ func (cache *TxCache) CreateAccount(creator *vm.Account) *vm.Account { nonce := creator.Nonce creator.Nonce += 1 - addr := LeftPadWord256(NewContractAddress(creator.Address.Postfix(20), nonce)) + addr := LeftPadWord256(NewContractAddress(creator.Address.Postfix(20), int(nonce))) // Create account from address. account, removed := vmUnpack(cache.accounts[addr]) @@ -86,6 +86,7 @@ func (cache *TxCache) CreateAccount(creator *vm.Account) *vm.Account { cache.accounts[addr] = vmAccountInfo{account, false} return account } else { + // NONCE HANDLING SANITY CHECK OR SHA3 IS BROKEN panic(Fmt("Could not create account, address already exists: %X", addr)) } } @@ -107,10 +108,12 @@ func (cache *TxCache) GetStorage(addr Word256, key Word256) Word256 { // NOTE: Set value to zero to removed from the trie. func (cache *TxCache) SetStorage(addr Word256, key Word256, value Word256) { + // SANITY CHECK _, removed := vmUnpack(cache.accounts[addr]) if removed { panic("SetStorage() on a removed account") } + // SANITY CHECK END cache.storages[Tuple256{addr, key}] = value } @@ -147,10 +150,10 @@ func (cache *TxCache) AddLog(log *vm.Log) { //----------------------------------------------------------------------------- // Convenience function to return address of new contract -func NewContractAddress(caller []byte, nonce uint64) []byte { +func NewContractAddress(caller []byte, nonce int) []byte { temp := make([]byte, 32+8) copy(temp, caller) - PutUint64BE(temp[32:], nonce) + PutInt64BE(temp[32:], int64(nonce)) return sha3.Sha3(temp)[:20] } @@ -160,9 +163,9 @@ func toVMAccount(acc *ac.Account) *vm.Account { Address: LeftPadWord256(acc.Address), Balance: acc.Balance, Code: acc.Code, // This is crazy. - Nonce: uint64(acc.Sequence), + Nonce: int64(acc.Sequence), StorageRoot: LeftPadWord256(acc.StorageRoot), - Permissions: acc.Permissions.Copy(), + Permissions: acc.Permissions, // Copy Other: acc.PubKey, } } @@ -185,9 +188,9 @@ func toStateAccount(acc *vm.Account) *ac.Account { PubKey: pubKey, Balance: acc.Balance, Code: acc.Code, - Sequence: uint(acc.Nonce), + Sequence: int(acc.Nonce), StorageRoot: storageRoot, - Permissions: acc.Permissions, + Permissions: acc.Permissions, // Copy } } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator.go index 4db4e90e079ae68c1215163bdf6683d22a400f59..75d430fe03fa174cb23c6142d773db0e2bcac79f 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator.go @@ -15,11 +15,11 @@ type ValidatorInfo struct { Address []byte `json:"address"` PubKey account.PubKeyEd25519 `json:"pub_key"` UnbondTo []*types.TxOutput `json:"unbond_to"` - FirstBondHeight uint `json:"first_bond_height"` - FirstBondAmount uint64 `json:"first_bond_amount"` - DestroyedHeight uint `json:"destroyed_height"` // If destroyed - DestroyedAmount uint64 `json:"destroyed_amount"` // If destroyed - ReleasedHeight uint `json:"released_height"` // If released + FirstBondHeight int `json:"first_bond_height"` + FirstBondAmount int64 `json:"first_bond_amount"` + DestroyedHeight int `json:"destroyed_height"` // If destroyed + DestroyedAmount int64 `json:"destroyed_amount"` // If destroyed + ReleasedHeight int `json:"released_height"` // If released } func (valInfo *ValidatorInfo) Copy() *ValidatorInfo { @@ -48,14 +48,15 @@ var ValidatorInfoCodec = binary.Codec{ type Validator struct { Address []byte `json:"address"` PubKey account.PubKeyEd25519 `json:"pub_key"` - BondHeight uint `json:"bond_height"` - UnbondHeight uint `json:"unbond_height"` - LastCommitHeight uint `json:"last_commit_height"` - VotingPower uint64 `json:"voting_power"` + BondHeight int `json:"bond_height"` + UnbondHeight int `json:"unbond_height"` + LastCommitHeight int `json:"last_commit_height"` + VotingPower int64 `json:"voting_power"` Accum int64 `json:"accum"` } // Creates a new copy of the validator so we can mutate accum. +// Panics if the validator is nil. func (v *Validator) Copy() *Validator { vCopy := *v return &vCopy @@ -76,12 +77,16 @@ func (v *Validator) CompareAccum(other *Validator) *Validator { } else if bytes.Compare(v.Address, other.Address) > 0 { return other } else { + // SANITY CHECK panic("Cannot compare identical validators") } } } func (v *Validator) String() string { + if v == nil { + return "nil-Validator" + } return fmt.Sprintf("Validator{%X %v %v-%v-%v VP:%v A:%v}", v.Address, v.PubKey, @@ -93,7 +98,7 @@ func (v *Validator) String() string { } func (v *Validator) Hash() []byte { - return binary.BinarySha256(v) + return binary.BinaryRipemd160(v) } //------------------------------------- diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator_set.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator_set.go index 7b235c2f5c0dde9c386f54090a7d4548807cd239..2b2a37dc5eaa8aa3385d4d6f799ff9d8045534bb 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator_set.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator_set.go @@ -2,7 +2,6 @@ package state import ( "bytes" - "errors" "fmt" "sort" "strings" @@ -29,7 +28,7 @@ type ValidatorSet struct { // cached (unexported) proposer *Validator - totalVotingPower uint64 + totalVotingPower int64 } func NewValidatorSet(vals []*Validator) *ValidatorSet { @@ -44,7 +43,7 @@ func NewValidatorSet(vals []*Validator) *ValidatorSet { } // TODO: mind the overflow when times and votingPower shares too large. -func (valSet *ValidatorSet) IncrementAccum(times uint) { +func (valSet *ValidatorSet) IncrementAccum(times int) { // Add VotingPower * times to each validator and order into heap. validatorsHeap := NewHeap() for _, val := range valSet.Validators { @@ -53,7 +52,7 @@ func (valSet *ValidatorSet) IncrementAccum(times uint) { } // Decrement the validator with most accum, times times. - for i := uint(0); i < times; i++ { + for i := 0; i < times; i++ { mostest := validatorsHeap.Peek().(*Validator) if i == times-1 { valSet.proposer = mostest @@ -83,27 +82,27 @@ func (valSet *ValidatorSet) HasAddress(address []byte) bool { return idx != len(valSet.Validators) && bytes.Compare(valSet.Validators[idx].Address, address) == 0 } -func (valSet *ValidatorSet) GetByAddress(address []byte) (index uint, val *Validator) { +func (valSet *ValidatorSet) GetByAddress(address []byte) (index int, val *Validator) { idx := sort.Search(len(valSet.Validators), func(i int) bool { return bytes.Compare(address, valSet.Validators[i].Address) <= 0 }) if idx != len(valSet.Validators) && bytes.Compare(valSet.Validators[idx].Address, address) == 0 { - return uint(idx), valSet.Validators[idx].Copy() + return idx, valSet.Validators[idx].Copy() } else { return 0, nil } } -func (valSet *ValidatorSet) GetByIndex(index uint) (address []byte, val *Validator) { +func (valSet *ValidatorSet) GetByIndex(index int) (address []byte, val *Validator) { val = valSet.Validators[index] return val.Address, val.Copy() } -func (valSet *ValidatorSet) Size() uint { - return uint(len(valSet.Validators)) +func (valSet *ValidatorSet) Size() int { + return len(valSet.Validators) } -func (valSet *ValidatorSet) TotalVotingPower() uint64 { +func (valSet *ValidatorSet) TotalVotingPower() int64 { if valSet.totalVotingPower == 0 { for _, val := range valSet.Validators { valSet.totalVotingPower += val.VotingPower @@ -113,6 +112,9 @@ func (valSet *ValidatorSet) TotalVotingPower() uint64 { } func (valSet *ValidatorSet) Proposer() (proposer *Validator) { + if len(valSet.Validators) == 0 { + return nil + } if valSet.proposer == nil { for _, val := range valSet.Validators { valSet.proposer = valSet.proposer.CompareAccum(val) @@ -129,7 +131,7 @@ func (valSet *ValidatorSet) Hash() []byte { for i, val := range valSet.Validators { hashables[i] = val } - return merkle.HashFromHashables(hashables) + return merkle.SimpleHashFromHashables(hashables) } func (valSet *ValidatorSet) Add(val *Validator) (added bool) { @@ -191,9 +193,9 @@ func (valSet *ValidatorSet) Remove(address []byte) (val *Validator, removed bool } } -func (valSet *ValidatorSet) Iterate(fn func(index uint, val *Validator) bool) { +func (valSet *ValidatorSet) Iterate(fn func(index int, val *Validator) bool) { for i, val := range valSet.Validators { - stop := fn(uint(i), val.Copy()) + stop := fn(i, val.Copy()) if stop { break } @@ -201,45 +203,52 @@ func (valSet *ValidatorSet) Iterate(fn func(index uint, val *Validator) bool) { } // Verify that +2/3 of the set had signed the given signBytes -func (valSet *ValidatorSet) VerifyValidation(chainID string, hash []byte, parts types.PartSetHeader, height uint, v *types.Validation) error { - if valSet.Size() != uint(len(v.Commits)) { - return errors.New(Fmt("Invalid validation -- wrong set size: %v vs %v", - valSet.Size(), len(v.Commits))) +func (valSet *ValidatorSet) VerifyValidation(chainID string, + hash []byte, parts types.PartSetHeader, height int, v *types.Validation) error { + if valSet.Size() != len(v.Precommits) { + return fmt.Errorf("Invalid validation -- wrong set size: %v vs %v", valSet.Size(), len(v.Precommits)) + } + if height != v.Height() { + return fmt.Errorf("Invalid validation -- wrong height: %v vs %v", height, v.Height()) } - talliedVotingPower := uint64(0) - seenValidators := map[string]struct{}{} + talliedVotingPower := int64(0) + round := v.Round() - for idx, commit := range v.Commits { - // may be zero, in which case skip. - if commit.Signature.IsZero() { + for idx, precommit := range v.Precommits { + // may be nil if validator skipped. + if precommit == nil { continue } - _, val := valSet.GetByIndex(uint(idx)) - commitSignBytes := account.SignBytes(chainID, &types.Vote{ - Height: height, Round: commit.Round, Type: types.VoteTypeCommit, - BlockHash: hash, - BlockParts: parts, - }) - - // Validate - if _, seen := seenValidators[string(val.Address)]; seen { - return fmt.Errorf("Duplicate validator for commit %v for Validation %v", commit, v) + if precommit.Height != height { + return fmt.Errorf("Invalid validation -- wrong height: %v vs %v", height, precommit.Height) } - - if !val.PubKey.VerifyBytes(commitSignBytes, commit.Signature) { - return fmt.Errorf("Invalid signature for commit %v for Validation %v", commit, v) + if precommit.Round != round { + return fmt.Errorf("Invalid validation -- wrong round: %v vs %v", round, precommit.Round) } - - // Tally - seenValidators[string(val.Address)] = struct{}{} + if precommit.Type != types.VoteTypePrecommit { + return fmt.Errorf("Invalid validation -- not precommit @ index %v", idx) + } + _, val := valSet.GetByIndex(idx) + // Validate signature + precommitSignBytes := account.SignBytes(chainID, precommit) + if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { + return fmt.Errorf("Invalid validation -- invalid signature: %v", precommit) + } + if !bytes.Equal(precommit.BlockHash, hash) { + continue // Not an error, but doesn't count + } + if !parts.Equals(precommit.BlockParts) { + continue // Not an error, but doesn't count + } + // Good precommit! talliedVotingPower += val.VotingPower } if talliedVotingPower > valSet.TotalVotingPower()*2/3 { return nil } else { - return fmt.Errorf("insufficient voting power %v, needed %v", + return fmt.Errorf("Invalid validation -- insufficient voting power: got %v, needed %v", talliedVotingPower, (valSet.TotalVotingPower()*2/3 + 1)) } } @@ -249,8 +258,11 @@ func (valSet *ValidatorSet) String() string { } func (valSet *ValidatorSet) StringIndented(indent string) string { + if valSet == nil { + return "nil-ValidatorSet" + } valStrings := []string{} - valSet.Iterate(func(index uint, val *Validator) bool { + valSet.Iterate(func(index int, val *Validator) bool { valStrings = append(valStrings, val.String()) return false }) @@ -288,9 +300,9 @@ func (vs ValidatorsByAddress) Swap(i, j int) { //------------------------------------- // Use with Heap for sorting validators by accum -type accumComparable uint64 +type accumComparable int64 // We want to find the validator with the greatest accum. func (ac accumComparable) Less(o interface{}) bool { - return uint64(ac) < uint64(o.(accumComparable)) + return int64(ac) > int64(o.(accumComparable)) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator_set_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator_set_test.go index 83c310ffc2c6138339045ef2a42b95a01a1c5a2b..210e483431146d3f187a61d04b5ee55c932ae50f 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator_set_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/state/validator_set_test.go @@ -5,7 +5,7 @@ import ( . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" "bytes" - "fmt" + "strings" "testing" ) @@ -13,9 +13,9 @@ func randValidator_() *Validator { return &Validator{ Address: RandBytes(20), PubKey: account.PubKeyEd25519(RandBytes(64)), - BondHeight: uint(RandUint32()), - VotingPower: RandUint64(), - Accum: int64(RandUint64()), + BondHeight: RandInt(), + VotingPower: RandInt64(), + Accum: RandInt64(), } } @@ -43,12 +43,39 @@ func TestCopy(t *testing.T) { } func TestProposerSelection(t *testing.T) { - vset := randValidatorSet(10) + vset := NewValidatorSet([]*Validator{ + &Validator{ + Address: []byte("foo"), + PubKey: account.PubKeyEd25519(RandBytes(64)), + BondHeight: RandInt(), + VotingPower: 1000, + Accum: 0, + }, + &Validator{ + Address: []byte("bar"), + PubKey: account.PubKeyEd25519(RandBytes(64)), + BondHeight: RandInt(), + VotingPower: 300, + Accum: 0, + }, + &Validator{ + Address: []byte("baz"), + PubKey: account.PubKeyEd25519(RandBytes(64)), + BondHeight: RandInt(), + VotingPower: 330, + Accum: 0, + }, + }) + proposers := []string{} for i := 0; i < 100; i++ { val := vset.Proposer() - fmt.Printf("Proposer: %v\n", val) + proposers = append(proposers, string(val.Address)) vset.IncrementAccum(1) } + expected := `bar foo baz foo bar foo foo baz foo bar foo foo baz foo foo bar foo baz foo foo bar foo foo baz foo bar foo foo baz foo bar foo foo baz foo foo bar foo baz foo foo bar foo baz foo foo bar foo baz foo foo bar foo baz foo foo foo baz bar foo foo foo baz foo bar foo foo baz foo bar foo foo baz foo bar foo foo baz foo bar foo foo baz foo foo bar foo baz foo foo bar foo baz foo foo bar foo baz foo foo` + if expected != strings.Join(proposers, " ") { + t.Errorf("Expected sequence of proposers was\n%v\nbut got \n%v", expected, strings.Join(proposers, " ")) + } } func BenchmarkValidatorSetCopy(b *testing.B) { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/block.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/block.go index 790158691e91e003afa11fc065ccb28b76a0fd99..7197f084b72997d7f93d245eb854c4aac5092bdf 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/block.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/block.go @@ -2,7 +2,6 @@ package types import ( "bytes" - "crypto/sha256" "errors" "fmt" "strings" @@ -15,28 +14,28 @@ import ( ) type Block struct { - *Header `json:"header"` - *Validation `json:"validation"` - *Data `json:"data"` + *Header `json:"header"` + *Data `json:"data"` + LastValidation *Validation `json:"last_validation"` } // Basic validation that doesn't involve state data. -func (b *Block) ValidateBasic(chainID string, lastBlockHeight uint, lastBlockHash []byte, +func (b *Block) ValidateBasic(chainID string, lastBlockHeight int, lastBlockHash []byte, lastBlockParts PartSetHeader, lastBlockTime time.Time) error { if b.ChainID != chainID { - return errors.New("Wrong Block.Header.ChainID") + return errors.New(Fmt("Wrong Block.Header.ChainID. Expected %v, got %v", chainID, b.ChainID)) } if b.Height != lastBlockHeight+1 { - return errors.New("Wrong Block.Header.Height") + return errors.New(Fmt("Wrong Block.Header.Height. Expected %v, got %v", lastBlockHeight+1, b.Height)) } - if b.NumTxs != uint(len(b.Data.Txs)) { - return errors.New("Wrong Block.Header.NumTxs") + if b.NumTxs != len(b.Data.Txs) { + return errors.New(Fmt("Wrong Block.Header.NumTxs. Expected %v, got %v", len(b.Data.Txs), b.NumTxs)) } if !bytes.Equal(b.LastBlockHash, lastBlockHash) { - return errors.New("Wrong Block.Header.LastBlockHash") + return errors.New(Fmt("Wrong Block.Header.LastBlockHash. Expected %X, got %X", lastBlockHash, b.LastBlockHash)) } if !b.LastBlockParts.Equals(lastBlockParts) { - return errors.New("Wrong Block.Header.LastBlockParts") + return errors.New(Fmt("Wrong Block.Header.LastBlockParts. Expected %v, got %v", lastBlockParts, b.LastBlockParts)) } /* TODO: Determine bounds See blockchain/reactor "stopSyncingDurationMinutes" @@ -46,7 +45,7 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight uint, lastBlockHas } */ if b.Header.Height != 1 { - if err := b.Validation.ValidateBasic(); err != nil { + if err := b.LastValidation.ValidateBasic(); err != nil { return err } } @@ -58,12 +57,12 @@ func (b *Block) ValidateBasic(chainID string, lastBlockHeight uint, lastBlockHas // If the block is incomplete (e.g. missing Header.StateHash) // then the hash is nil, to prevent the usage of that hash. func (b *Block) Hash() []byte { - if b.Header == nil || b.Validation == nil || b.Data == nil { + if b.Header == nil || b.Data == nil || b.LastValidation == nil { return nil } hashHeader := b.Header.Hash() - hashValidation := b.Validation.Hash() hashData := b.Data.Hash() + hashLastValidation := b.LastValidation.Hash() // If hashHeader is nil, required fields are missing. if len(hashHeader) == 0 { @@ -71,8 +70,8 @@ func (b *Block) Hash() []byte { } // Merkle hash from subhashes. - hashes := [][]byte{hashHeader, hashValidation, hashData} - return merkle.HashFromHashes(hashes) + hashes := [][]byte{hashHeader, hashData, hashLastValidation} + return merkle.SimpleHashFromHashes(hashes) } func (b *Block) MakePartSet() *PartSet { @@ -106,8 +105,8 @@ func (b *Block) StringIndented(indent string) string { %s %v %s}#%X`, indent, b.Header.StringIndented(indent+" "), - indent, b.Validation.StringIndented(indent+" "), indent, b.Data.StringIndented(indent+" "), + indent, b.LastValidation.StringIndented(indent+" "), indent, b.Hash()) } @@ -123,10 +122,10 @@ func (b *Block) StringShort() string { type Header struct { ChainID string `json:"chain_id"` - Height uint `json:"height"` + Height int `json:"height"` Time time.Time `json:"time"` - Fees uint64 `json:"fees"` - NumTxs uint `json:"num_txs"` + Fees int64 `json:"fees"` + NumTxs int `json:"num_txs"` LastBlockHash []byte `json:"last_block_hash"` LastBlockParts PartSetHeader `json:"last_block_parts"` StateHash []byte `json:"state_hash"` @@ -138,15 +137,7 @@ func (h *Header) Hash() []byte { return nil } - buf := new(bytes.Buffer) - hasher, n, err := sha256.New(), new(int64), new(error) - binary.WriteBinary(h, buf, n, err) - if *err != nil { - panic(err) - } - hasher.Write(buf.Bytes()) - hash := hasher.Sum(nil) - return hash + return binary.BinaryRipemd160(h) } func (h *Header) StringIndented(indent string) string { @@ -158,7 +149,7 @@ func (h *Header) StringIndented(indent string) string { %s Height: %v %s Time: %v %s Fees: %v -%s NumTxs: %v +%s NumTxs: %v %s LastBlockHash: %X %s LastBlockParts: %v %s StateHash: %X @@ -174,54 +165,107 @@ func (h *Header) StringIndented(indent string) string { indent, h.Hash()) } -//----------------------------------------------------------------------------- +//------------------------------------- -type Commit struct { - Address []byte `json:"address"` - Round uint `json:"round"` - Signature account.SignatureEd25519 `json:"signature"` +// NOTE: Validation is empty for height 1, but never nil. +type Validation struct { + // NOTE: The Precommits are in order of address to preserve the bonded ValidatorSet order. + // Any peer with a block can gossip precommits by index with a peer without recalculating the + // active ValidatorSet. + Precommits []*Vote `json:"precommits"` + + // Volatile + firstPrecommit *Vote + hash []byte + bitArray *BitArray } -func (commit Commit) IsZero() bool { - return commit.Round == 0 && commit.Signature.IsZero() +func (v *Validation) FirstPrecommit() *Vote { + if len(v.Precommits) == 0 { + return nil + } + if v.firstPrecommit != nil { + return v.firstPrecommit + } + for _, precommit := range v.Precommits { + if precommit != nil { + v.firstPrecommit = precommit + return precommit + } + } + return nil } -func (commit Commit) String() string { - return fmt.Sprintf("Commit{A:%X R:%v %X}", commit.Address, commit.Round, Fingerprint(commit.Signature)) +func (v *Validation) Height() int { + if len(v.Precommits) == 0 { + return 0 + } + return v.FirstPrecommit().Height } -//------------------------------------- +func (v *Validation) Round() int { + if len(v.Precommits) == 0 { + return 0 + } + return v.FirstPrecommit().Round +} -// NOTE: The Commits are in order of address to preserve the bonded ValidatorSet order. -// Any peer with a block can gossip commits by index with a peer without recalculating the -// active ValidatorSet. -type Validation struct { - Commits []Commit `json:"commits"` // Commits (or nil) of all active validators in address order. +func (v *Validation) Type() byte { + return VoteTypePrecommit +} - // Volatile - hash []byte - bitArray *BitArray +func (v *Validation) Size() int { + if v == nil { + return 0 + } + return len(v.Precommits) +} + +func (v *Validation) BitArray() *BitArray { + if v.bitArray == nil { + v.bitArray = NewBitArray(len(v.Precommits)) + for i, precommit := range v.Precommits { + v.bitArray.SetIndex(i, precommit != nil) + } + } + return v.bitArray +} + +func (v *Validation) GetByIndex(index int) *Vote { + return v.Precommits[index] +} + +func (v *Validation) IsCommit() bool { + if len(v.Precommits) == 0 { + return false + } + return true } func (v *Validation) ValidateBasic() error { - if len(v.Commits) == 0 { - return errors.New("No commits in validation") + if len(v.Precommits) == 0 { + return errors.New("No precommits in validation") } - lastAddress := []byte{} - for i := 0; i < len(v.Commits); i++ { - commit := v.Commits[i] - if commit.IsZero() { - if len(commit.Address) > 0 { - return errors.New("Zero commits should not have an address") - } - } else { - if len(commit.Address) == 0 { - return errors.New("Nonzero commits should have an address") - } - if len(lastAddress) > 0 && bytes.Compare(lastAddress, commit.Address) != -1 { - return errors.New("Invalid commit order") - } - lastAddress = commit.Address + height, round := v.Height(), v.Round() + for _, precommit := range v.Precommits { + // It's OK for precommits to be missing. + if precommit == nil { + continue + } + // Ensure that all votes are precommits + if precommit.Type != VoteTypePrecommit { + return fmt.Errorf("Invalid validation vote. Expected precommit, got %v", + precommit.Type) + } + // Ensure that all heights are the same + if precommit.Height != height { + return fmt.Errorf("Invalid validation precommit height. Expected %v, got %v", + height, precommit.Height) + } + // Ensure that all rounds are the same + if precommit.Round != round { + return fmt.Errorf("Invalid validation precommit round. Expected %v, got %v", + round, precommit.Round) } } return nil @@ -229,11 +273,11 @@ func (v *Validation) ValidateBasic() error { func (v *Validation) Hash() []byte { if v.hash == nil { - bs := make([]interface{}, len(v.Commits)) - for i, commit := range v.Commits { - bs[i] = commit + bs := make([]interface{}, len(v.Precommits)) + for i, precommit := range v.Precommits { + bs[i] = precommit } - v.hash = merkle.HashFromBinaries(bs) + v.hash = merkle.SimpleHashFromBinaries(bs) } return v.hash } @@ -242,27 +286,17 @@ func (v *Validation) StringIndented(indent string) string { if v == nil { return "nil-Validation" } - commitStrings := make([]string, len(v.Commits)) - for i, commit := range v.Commits { - commitStrings[i] = commit.String() + precommitStrings := make([]string, len(v.Precommits)) + for i, precommit := range v.Precommits { + precommitStrings[i] = precommit.String() } return fmt.Sprintf(`Validation{ -%s %v +%s Precommits: %v %s}#%X`, - indent, strings.Join(commitStrings, "\n"+indent+" "), + indent, strings.Join(precommitStrings, "\n"+indent+" "), indent, v.hash) } -func (v *Validation) BitArray() *BitArray { - if v.bitArray == nil { - v.bitArray = NewBitArray(uint(len(v.Commits))) - for i, commit := range v.Commits { - v.bitArray.SetIndex(uint(i), !commit.IsZero()) - } - } - return v.bitArray -} - //----------------------------------------------------------------------------- type Data struct { @@ -278,7 +312,7 @@ func (data *Data) Hash() []byte { for i, tx := range data.Txs { bs[i] = account.SignBytes(config.GetString("chain_id"), tx) } - data.hash = merkle.HashFromBinaries(bs) + data.hash = merkle.SimpleHashFromBinaries(bs) // NOTE: leaves are TxIDs. } return data.hash } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/block_meta.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/block_meta.go index 540da8d1926ef729d9f5c351fece9979ed124c2d..b72c0c860fa4c47946ee4c7a3f0ff126e70905d1 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/block_meta.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/block_meta.go @@ -1,15 +1,15 @@ package types type BlockMeta struct { - Hash []byte `json:"hash"` // The block hash - Header *Header `json:"header"` // The block's Header - Parts PartSetHeader `json:"parts"` // The PartSetHeader, for transfer + Hash []byte `json:"hash"` // The block hash + Header *Header `json:"header"` // The block's Header + PartsHeader PartSetHeader `json:"parts_header"` // The PartSetHeader, for transfer } func NewBlockMeta(block *Block, blockParts *PartSet) *BlockMeta { return &BlockMeta{ - Hash: block.Hash(), - Header: block.Header, - Parts: blockParts.Header(), + Hash: block.Hash(), + Header: block.Header, + PartsHeader: blockParts.Header(), } } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/events.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/events.go index e51d9e283fc7c3adea35ea994228044334d71f9d..39744ad332aea44beacbfdac176841218e375bb4 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/events.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/events.go @@ -55,14 +55,14 @@ type CallData struct { Caller []byte `json:"caller"` Callee []byte `json:"callee"` Data []byte `json:"data"` - Value uint64 `json:"value"` - Gas uint64 `json:"gas"` + Value int64 `json:"value"` + Gas int64 `json:"gas"` } type EventMsgCall struct { CallData *CallData `json:"call_data"` Origin []byte `json:"origin"` - TxId []byte `json:"tx_id"` + TxID []byte `json:"tx_id"` Return []byte `json:"return"` Exception string `json:"exception"` } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/names.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/names.go index bebecb479163ce93254a34e807c575cfecf234c2..9ce4cc6f42b3a369c0937d6d7c789cd994c922fc 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/names.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/names.go @@ -5,12 +5,12 @@ import ( ) var ( - MinNameRegistrationPeriod uint64 = 5 + MinNameRegistrationPeriod int = 5 // cost for storing a name for a block is // CostPerBlock*CostPerByte*(len(data) + 32) - NameCostPerByte uint64 = 1 - NameCostPerBlock uint64 = 1 + NameCostPerByte int64 = 1 + NameCostPerBlock int64 = 1 MaxNameLength = 32 MaxDataLength = 1 << 16 @@ -31,15 +31,15 @@ func validateNameRegEntryData(data string) bool { } // base cost is "effective" number of bytes -func BaseEntryCost(name, data string) uint64 { - return uint64(len(data) + 32) +func BaseEntryCost(name, data string) int64 { + return int64(len(data) + 32) } type NameRegEntry struct { Name string `json:"name"` // registered name for the entry Owner []byte `json:"owner"` // address that created the entry Data string `json:"data"` // data to store under this name - Expires uint64 `json:"expires"` // block at which this entry expires + Expires int `json:"expires"` // block at which this entry expires } func (entry *NameRegEntry) Copy() *NameRegEntry { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/node.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/node.go index 8cb1255a011b34d0997f61d06e860ef6a6f70494..c700ed2fa5df115804455b64aa7a0b1f317bf017 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/node.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/node.go @@ -6,10 +6,12 @@ import ( ) type NodeInfo struct { - Moniker string `json:"moniker"` - ChainID string `json:"chain_id"` - Version string `json:"version"` + Moniker string `json:"moniker"` + ChainID string `json:"chain_id"` + Version string `json:"version"` + Revision string `json:"revision"` + UUID string `json:"uuid"` Host string `json:"host"` P2PPort uint16 `json:"p2p_port"` RPCPort uint16 `json:"rpc_port"` diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/part_set.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/part_set.go index 6e9f72b2e5ab0801a4f1cbee0e1cf1f2fa130095..e6c8d509b81422e499985e2cfa817a4fcb549efe 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/part_set.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/part_set.go @@ -2,13 +2,13 @@ package types import ( "bytes" - "crypto/sha256" "errors" "fmt" "io" - "strings" "sync" + "github.com/eris-ltd/eris-db/Godeps/_workspace/src/code.google.com/p/go.crypto/ripemd160" + "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/binary" . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/merkle" @@ -20,13 +20,12 @@ const ( var ( ErrPartSetUnexpectedIndex = errors.New("Error part set unexpected index") - ErrPartSetInvalidTrail = errors.New("Error part set invalid trail") + ErrPartSetInvalidProof = errors.New("Error part set invalid proof") ) type Part struct { - Index uint `json:"index"` - Trail [][]byte `json:"trail"` - Bytes []byte `json:"bytes"` + Proof merkle.SimpleProof `json:"proof"` + Bytes []byte `json:"bytes"` // Cache hash []byte @@ -36,7 +35,7 @@ func (part *Part) Hash() []byte { if part.hash != nil { return part.hash } else { - hasher := sha256.New() + hasher := ripemd160.New() _, err := hasher.Write(part.Bytes) if err != nil { panic(err) @@ -51,25 +50,19 @@ func (part *Part) String() string { } func (part *Part) StringIndented(indent string) string { - trailStrings := make([]string, len(part.Trail)) - for i, hash := range part.Trail { - trailStrings[i] = fmt.Sprintf("%X", hash) - } return fmt.Sprintf(`Part{ -%s Index: %v -%s Trail: -%s %v +%s Proof: %v +%s Bytes: %X %s}`, - indent, part.Index, - indent, - indent, strings.Join(trailStrings, "\n"+indent+" "), + indent, part.Proof.StringIndented(indent+" "), + indent, part.Bytes, indent) } //------------------------------------- type PartSetHeader struct { - Total uint `json:"total"` + Total int `json:"total"` Hash []byte `json:"hash"` } @@ -92,13 +85,13 @@ func (psh PartSetHeader) WriteSignBytes(w io.Writer, n *int64, err *error) { //------------------------------------- type PartSet struct { - total uint + total int hash []byte mtx sync.Mutex parts []*Part partsBitArray *BitArray - count uint + count int } // Returns an immutable, full PartSet from the data bytes. @@ -108,27 +101,26 @@ func NewPartSetFromData(data []byte) *PartSet { total := (len(data) + partSize - 1) / partSize parts := make([]*Part, total) parts_ := make([]merkle.Hashable, total) - partsBitArray := NewBitArray(uint(total)) + partsBitArray := NewBitArray(total) for i := 0; i < total; i++ { part := &Part{ - Index: uint(i), Bytes: data[i*partSize : MinInt(len(data), (i+1)*partSize)], } parts[i] = part parts_[i] = part - partsBitArray.SetIndex(uint(i), true) + partsBitArray.SetIndex(i, true) } - // Compute merkle trails - trails, rootTrail := merkle.HashTrailsFromHashables(parts_) + // Compute merkle proofs + proofs := merkle.SimpleProofsFromHashables(parts_) for i := 0; i < total; i++ { - parts[i].Trail = trails[i].Flatten() + parts[i].Proof = *proofs[i] } return &PartSet{ - total: uint(total), - hash: rootTrail.Hash, + total: total, + hash: proofs[0].RootHash, parts: parts, partsBitArray: partsBitArray, - count: uint(total), + count: total, } } @@ -138,7 +130,7 @@ func NewPartSetFromHeader(header PartSetHeader) *PartSet { total: header.Total, hash: header.Hash, parts: make([]*Part, header.Total), - partsBitArray: NewBitArray(uint(header.Total)), + partsBitArray: NewBitArray(header.Total), count: 0, } } @@ -182,14 +174,14 @@ func (ps *PartSet) HashesTo(hash []byte) bool { return bytes.Equal(ps.hash, hash) } -func (ps *PartSet) Count() uint { +func (ps *PartSet) Count() int { if ps == nil { return 0 } return ps.count } -func (ps *PartSet) Total() uint { +func (ps *PartSet) Total() int { if ps == nil { return 0 } @@ -201,28 +193,28 @@ func (ps *PartSet) AddPart(part *Part) (bool, error) { defer ps.mtx.Unlock() // Invalid part index - if part.Index >= ps.total { + if part.Proof.Index >= ps.total { return false, ErrPartSetUnexpectedIndex } // If part already exists, return false. - if ps.parts[part.Index] != nil { + if ps.parts[part.Proof.Index] != nil { return false, nil } - // Check hash trail - if !merkle.VerifyHashTrail(uint(part.Index), uint(ps.total), part.Hash(), part.Trail, ps.hash) { - return false, ErrPartSetInvalidTrail + // Check hash proof + if !part.Proof.Verify(part.Hash(), ps.Hash()) { + return false, ErrPartSetInvalidProof } // Add part - ps.parts[part.Index] = part - ps.partsBitArray.SetIndex(uint(part.Index), true) + ps.parts[part.Proof.Index] = part + ps.partsBitArray.SetIndex(part.Proof.Index, true) ps.count++ return true, nil } -func (ps *PartSet) GetPart(index uint) *Part { +func (ps *PartSet) GetPart(index int) *Part { ps.mtx.Lock() defer ps.mtx.Unlock() return ps.parts[index] diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/part_set_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/part_set_test.go index 5a65ace5c2bb1eab59b6aaae23cd69ccd37a18a1..49ae5fca07bb7bd6614b0580017281f491669dab 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/part_set_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/part_set_test.go @@ -27,7 +27,7 @@ func TestBasicPartSet(t *testing.T) { // Test adding parts to a new partSet. partSet2 := NewPartSetFromHeader(partSet.Header()) - for i := uint(0); i < partSet.Total(); i++ { + for i := 0; i < partSet.Total(); i++ { part := partSet.GetPart(i) //t.Logf("\n%v", part) added, err := partSet2.AddPart(part) @@ -58,7 +58,7 @@ func TestBasicPartSet(t *testing.T) { } -func TestWrongTrail(t *testing.T) { +func TestWrongProof(t *testing.T) { // Construct random data of size partSize * 100 data := RandBytes(partSize * 100) @@ -69,7 +69,7 @@ func TestWrongTrail(t *testing.T) { // Test adding a part with wrong trail. part := partSet.GetPart(0) - part.Trail[0][0] += byte(0x01) + part.Proof.InnerHashes[0][0] += byte(0x01) added, err := partSet2.AddPart(part) if added || err == nil { t.Errorf("Expected to fail adding a part with bad trail.") diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/tx.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/tx.go index c78cff3c0badde8d36971b432f4ea9e693c52043..724cad9394034489585300e076e35d617779b45a 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/tx.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/tx.go @@ -24,8 +24,8 @@ var ( ) type ErrTxInvalidSequence struct { - Got uint64 - Expected uint64 + Got int + Expected int } func (e ErrTxInvalidSequence) Error() string { @@ -79,8 +79,8 @@ var _ = binary.RegisterInterface( type TxInput struct { Address []byte `json:"address"` // Hash of the PubKey - Amount uint64 `json:"amount"` // Must not exceed account balance - Sequence uint `json:"sequence"` // Must be 1 greater than the last committed TxInput + Amount int64 `json:"amount"` // Must not exceed account balance + Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput Signature account.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx PubKey account.PubKey `json:"pub_key"` // Must not be nil, may be nil } @@ -107,7 +107,7 @@ func (txIn *TxInput) String() string { type TxOutput struct { Address []byte `json:"address"` // Hash of the PubKey - Amount uint64 `json:"amount"` // The sum of all outputs must not exceed the inputs. + Amount int64 `json:"amount"` // The sum of all outputs must not exceed the inputs. } func (txOut *TxOutput) ValidateBasic() error { @@ -163,8 +163,8 @@ func (tx *SendTx) String() string { type CallTx struct { Input *TxInput `json:"input"` Address []byte `json:"address"` - GasLimit uint64 `json:"gas_limit"` - Fee uint64 `json:"fee"` + GasLimit int64 `json:"gas_limit"` + Fee int64 `json:"fee"` Data []byte `json:"data"` } @@ -186,7 +186,7 @@ type NameTx struct { Input *TxInput `json:"input"` Name string `json:"name"` Data string `json:"data"` - Fee uint64 `json:"fee"` + Fee int64 `json:"fee"` } func (tx *NameTx) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { @@ -220,7 +220,7 @@ func (tx *NameTx) ValidateStrings() error { return nil } -func (tx *NameTx) BaseEntryCost() uint64 { +func (tx *NameTx) BaseEntryCost() int64 { return BaseEntryCost(tx.Name, tx.Data) } @@ -266,7 +266,7 @@ func (tx *BondTx) String() string { type UnbondTx struct { Address []byte `json:"address"` - Height uint `json:"height"` + Height int `json:"height"` Signature account.SignatureEd25519 `json:"signature"` } @@ -283,7 +283,7 @@ func (tx *UnbondTx) String() string { type RebondTx struct { Address []byte `json:"address"` - Height uint `json:"height"` + Height int `json:"height"` Signature account.SignatureEd25519 `json:"signature"` } @@ -314,7 +314,8 @@ func (tx *DupeoutTx) String() string { //----------------------------------------------------------------------------- -func TxId(chainID string, tx Tx) []byte { +// This should match the leaf hashes of Block.Data.Hash()'s SimpleMerkleTree. +func TxID(chainID string, tx Tx) []byte { signBytes := account.SignBytes(chainID, tx) return binary.BinaryRipemd160(signBytes) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/tx_utils.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/tx_utils.go index 39f9bb59d00f7169aa2a78576fbffdde73cd8d25..e0bc09e16a7f9f3bbbbe306c205d0e8019b8a198 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/tx_utils.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/tx_utils.go @@ -19,7 +19,7 @@ func NewSendTx() *SendTx { } } -func (tx *SendTx) AddInput(st AccountGetter, pubkey account.PubKey, amt uint64) error { +func (tx *SendTx) AddInput(st AccountGetter, pubkey account.PubKey, amt int64) error { addr := pubkey.Address() acc := st.GetAccount(addr) if acc == nil { @@ -28,7 +28,7 @@ func (tx *SendTx) AddInput(st AccountGetter, pubkey account.PubKey, amt uint64) return tx.AddInputWithNonce(pubkey, amt, acc.Sequence+1) } -func (tx *SendTx) AddInputWithNonce(pubkey account.PubKey, amt uint64, nonce uint) error { +func (tx *SendTx) AddInputWithNonce(pubkey account.PubKey, amt int64, nonce int) error { addr := pubkey.Address() tx.Inputs = append(tx.Inputs, &TxInput{ Address: addr, @@ -40,7 +40,7 @@ func (tx *SendTx) AddInputWithNonce(pubkey account.PubKey, amt uint64, nonce uin return nil } -func (tx *SendTx) AddOutput(addr []byte, amt uint64) error { +func (tx *SendTx) AddOutput(addr []byte, amt int64) error { tx.Outputs = append(tx.Outputs, &TxOutput{ Address: addr, Amount: amt, @@ -60,7 +60,7 @@ func (tx *SendTx) SignInput(chainID string, i int, privAccount *account.PrivAcco //---------------------------------------------------------------------------- // CallTx interface for creating tx -func NewCallTx(st AccountGetter, from account.PubKey, to, data []byte, amt, gasLimit, fee uint64) (*CallTx, error) { +func NewCallTx(st AccountGetter, from account.PubKey, to, data []byte, amt, gasLimit, fee int64) (*CallTx, error) { addr := from.Address() acc := st.GetAccount(addr) if acc == nil { @@ -71,7 +71,7 @@ func NewCallTx(st AccountGetter, from account.PubKey, to, data []byte, amt, gasL return NewCallTxWithNonce(from, to, data, amt, gasLimit, fee, nonce), nil } -func NewCallTxWithNonce(from account.PubKey, to, data []byte, amt, gasLimit, fee uint64, nonce uint) *CallTx { +func NewCallTxWithNonce(from account.PubKey, to, data []byte, amt, gasLimit, fee int64, nonce int) *CallTx { addr := from.Address() input := &TxInput{ Address: addr, @@ -98,7 +98,7 @@ func (tx *CallTx) Sign(chainID string, privAccount *account.PrivAccount) { //---------------------------------------------------------------------------- // NameTx interface for creating tx -func NewNameTx(st AccountGetter, from account.PubKey, name, data string, amt, fee uint64) (*NameTx, error) { +func NewNameTx(st AccountGetter, from account.PubKey, name, data string, amt, fee int64) (*NameTx, error) { addr := from.Address() acc := st.GetAccount(addr) if acc == nil { @@ -109,7 +109,7 @@ func NewNameTx(st AccountGetter, from account.PubKey, name, data string, amt, fe return NewNameTxWithNonce(from, name, data, amt, fee, nonce), nil } -func NewNameTxWithNonce(from account.PubKey, name, data string, amt, fee uint64, nonce uint) *NameTx { +func NewNameTxWithNonce(from account.PubKey, name, data string, amt, fee int64, nonce int) *NameTx { addr := from.Address() input := &TxInput{ Address: addr, @@ -147,7 +147,7 @@ func NewBondTx(pubkey account.PubKey) (*BondTx, error) { }, nil } -func (tx *BondTx) AddInput(st AccountGetter, pubkey account.PubKey, amt uint64) error { +func (tx *BondTx) AddInput(st AccountGetter, pubkey account.PubKey, amt int64) error { addr := pubkey.Address() acc := st.GetAccount(addr) if acc == nil { @@ -156,7 +156,7 @@ func (tx *BondTx) AddInput(st AccountGetter, pubkey account.PubKey, amt uint64) return tx.AddInputWithNonce(pubkey, amt, acc.Sequence+1) } -func (tx *BondTx) AddInputWithNonce(pubkey account.PubKey, amt uint64, nonce uint) error { +func (tx *BondTx) AddInputWithNonce(pubkey account.PubKey, amt int64, nonce int) error { addr := pubkey.Address() tx.Inputs = append(tx.Inputs, &TxInput{ Address: addr, @@ -168,7 +168,7 @@ func (tx *BondTx) AddInputWithNonce(pubkey account.PubKey, amt uint64, nonce uin return nil } -func (tx *BondTx) AddOutput(addr []byte, amt uint64) error { +func (tx *BondTx) AddOutput(addr []byte, amt int64) error { tx.UnbondTo = append(tx.UnbondTo, &TxOutput{ Address: addr, Amount: amt, @@ -194,3 +194,31 @@ func (tx *BondTx) SignInput(chainID string, i int, privAccount *account.PrivAcco tx.Inputs[i].Signature = privAccount.Sign(chainID, tx) return nil } + +//---------------------------------------------------------------------- +// UnbondTx interface for creating tx + +func NewUnbondTx(addr []byte, height int) *UnbondTx { + return &UnbondTx{ + Address: addr, + Height: height, + } +} + +func (tx *UnbondTx) Sign(chainID string, privAccount *account.PrivAccount) { + tx.Signature = privAccount.Sign(chainID, tx).(account.SignatureEd25519) +} + +//---------------------------------------------------------------------- +// RebondTx interface for creating tx + +func NewRebondTx(addr []byte, height int) *RebondTx { + return &RebondTx{ + Address: addr, + Height: height, + } +} + +func (tx *RebondTx) Sign(chainID string, privAccount *account.PrivAccount) { + tx.Signature = privAccount.Sign(chainID, tx).(account.SignatureEd25519) +} diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote.go index 0512344719a12d7f114f1cb7b140188e4cc99d0a..acacbceea6d8fb69405d629dd762cd33958c842e 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/types/vote.go @@ -27,11 +27,9 @@ func (err *ErrVoteConflictingSignature) Error() string { } // Represents a prevote, precommit, or commit vote from validators for consensus. -// Commit votes get aggregated into the next block's Validaiton. -// See the whitepaper for details. type Vote struct { - Height uint `json:"height"` - Round uint `json:"round"` + Height int `json:"height"` + Round int `json:"round"` Type byte `json:"type"` BlockHash []byte `json:"block_hash"` // empty if vote is nil. BlockParts PartSetHeader `json:"block_parts"` // zero if vote is nil. @@ -42,7 +40,6 @@ type Vote struct { const ( VoteTypePrevote = byte(0x01) VoteTypePrecommit = byte(0x02) - VoteTypeCommit = byte(0x03) ) func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int64, err *error) { @@ -57,17 +54,32 @@ func (vote *Vote) Copy() *Vote { } func (vote *Vote) String() string { + if vote == nil { + return "nil-Vote" + } var typeString string switch vote.Type { case VoteTypePrevote: typeString = "Prevote" case VoteTypePrecommit: typeString = "Precommit" - case VoteTypeCommit: - typeString = "Commit" default: panic("Unknown vote type") } return fmt.Sprintf("Vote{%v/%02d/%v(%v) %X#%v %v}", vote.Height, vote.Round, vote.Type, typeString, Fingerprint(vote.BlockHash), vote.BlockParts, vote.Signature) } + +//-------------------------------------------------------------------------------- +// TODO: Move blocks/Validation to here? + +// Common interface between *consensus.VoteSet and types.Validation +type VoteSetReader interface { + Height() int + Round() int + Type() byte + Size() int + BitArray() *BitArray + GetByIndex(int) *Vote + IsCommit() bool +} diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/gas.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/gas.go index ebe5573a399651d65d053577e92d78a45f70d08f..dac978f1490b0c8381d2e78a712148db9499a05b 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/gas.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/gas.go @@ -1,17 +1,17 @@ package vm const ( - GasSha3 uint64 = 1 - GasGetAccount uint64 = 1 - GasStorageUpdate uint64 = 1 + GasSha3 int64 = 1 + GasGetAccount int64 = 1 + GasStorageUpdate int64 = 1 - GasStackOp uint64 = 1 + GasStackOp int64 = 1 - GasEcRecover uint64 = 1 - GasSha256Word uint64 = 1 - GasSha256Base uint64 = 1 - GasRipemd160Word uint64 = 1 - GasRipemd160Base uint64 = 1 - GasIdentityWord uint64 = 1 - GasIdentityBase uint64 = 1 + GasEcRecover int64 = 1 + GasSha256Word int64 = 1 + GasSha256Base int64 = 1 + GasRipemd160Word int64 = 1 + GasRipemd160Base int64 = 1 + GasIdentityWord int64 = 1 + GasIdentityBase int64 = 1 ) diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/native.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/native.go index 9b9337f92cd93e2be7becf4ff48ad012c9a88b90..82937344b553ec6cd128e63f202eec54a79944df 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/native.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/native.go @@ -11,17 +11,17 @@ import ( var nativeContracts = make(map[Word256]NativeContract) func init() { - nativeContracts[Uint64ToWord256(1)] = ecrecoverFunc - nativeContracts[Uint64ToWord256(2)] = sha256Func - nativeContracts[Uint64ToWord256(3)] = ripemd160Func - nativeContracts[Uint64ToWord256(4)] = identityFunc + nativeContracts[Int64ToWord256(1)] = ecrecoverFunc + nativeContracts[Int64ToWord256(2)] = sha256Func + nativeContracts[Int64ToWord256(3)] = ripemd160Func + nativeContracts[Int64ToWord256(4)] = identityFunc } //----------------------------------------------------------------------------- -type NativeContract func(input []byte, gas *uint64) (output []byte, err error) +type NativeContract func(input []byte, gas *int64) (output []byte, err error) -func ecrecoverFunc(input []byte, gas *uint64) (output []byte, err error) { +func ecrecoverFunc(input []byte, gas *int64) (output []byte, err error) { // Deduct gas gasRequired := GasEcRecover if *gas < gasRequired { @@ -42,9 +42,9 @@ func ecrecoverFunc(input []byte, gas *uint64) (output []byte, err error) { return LeftPadBytes(hashed, 32), nil } -func sha256Func(input []byte, gas *uint64) (output []byte, err error) { +func sha256Func(input []byte, gas *int64) (output []byte, err error) { // Deduct gas - gasRequired := uint64((len(input)+31)/32)*GasSha256Word + GasSha256Base + gasRequired := int64((len(input)+31)/32)*GasSha256Word + GasSha256Base if *gas < gasRequired { return nil, ErrInsufficientGas } else { @@ -57,9 +57,9 @@ func sha256Func(input []byte, gas *uint64) (output []byte, err error) { return hasher.Sum(nil), nil } -func ripemd160Func(input []byte, gas *uint64) (output []byte, err error) { +func ripemd160Func(input []byte, gas *int64) (output []byte, err error) { // Deduct gas - gasRequired := uint64((len(input)+31)/32)*GasRipemd160Word + GasRipemd160Base + gasRequired := int64((len(input)+31)/32)*GasRipemd160Word + GasRipemd160Base if *gas < gasRequired { return nil, ErrInsufficientGas } else { @@ -72,9 +72,9 @@ func ripemd160Func(input []byte, gas *uint64) (output []byte, err error) { return LeftPadBytes(hasher.Sum(nil), 32), nil } -func identityFunc(input []byte, gas *uint64) (output []byte, err error) { +func identityFunc(input []byte, gas *int64) (output []byte, err error) { // Deduct gas - gasRequired := uint64((len(input)+31)/32)*GasIdentityWord + GasIdentityBase + gasRequired := int64((len(input)+31)/32)*GasIdentityWord + GasIdentityBase if *gas < gasRequired { return nil, ErrInsufficientGas } else { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/snative.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/snative.go index 73165be40ff1b8dc07b29fe648b03e7585dcada1..8a9f2fd80cd0b0120b45afa1f9424bf4e4abda59 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/snative.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/snative.go @@ -224,9 +224,9 @@ func (e ErrInvalidPermission) Error() string { // Checks if a permission flag is valid (a known base chain or snative permission) func ValidPermN(n ptypes.PermFlag) bool { - if n > ptypes.TopBasePermission && n < ptypes.FirstSNativePerm { + if n > ptypes.TopBasePermFlag && n < ptypes.FirstSNativePermFlag { return false - } else if n > ptypes.TopSNativePermission { + } else if n > ptypes.TopSNativePermFlag { return false } return true diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/stack.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/stack.go index 2047a436d110e5b90cc1ff06d59365785b7e07e9..1a7d3d9d76826c7d30b5970c9c5eaedf92701db0 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/stack.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/stack.go @@ -10,11 +10,11 @@ type Stack struct { data []Word256 ptr int - gas *uint64 + gas *int64 err *error } -func NewStack(capacity int, gas *uint64, err *error) *Stack { +func NewStack(capacity int, gas *int64, err *error) *Stack { return &Stack{ data: make([]Word256, capacity), ptr: 0, @@ -23,7 +23,7 @@ func NewStack(capacity int, gas *uint64, err *error) *Stack { } } -func (st *Stack) useGas(gasToUse uint64) { +func (st *Stack) useGas(gasToUse int64) { if *st.gas > gasToUse { *st.gas -= gasToUse } else { @@ -55,8 +55,8 @@ func (st *Stack) PushBytes(bz []byte) { st.Push(LeftPadWord256(bz)) } -func (st *Stack) Push64(i uint64) { - st.Push(Uint64ToWord256(i)) +func (st *Stack) Push64(i int64) { + st.Push(Int64ToWord256(i)) } func (st *Stack) Pop() Word256 { @@ -73,9 +73,9 @@ func (st *Stack) PopBytes() []byte { return st.Pop().Bytes() } -func (st *Stack) Pop64() uint64 { +func (st *Stack) Pop64() int64 { d := st.Pop() - return Uint64FromWord256(d) + return Int64FromWord256(d) } func (st *Stack) Len() int { diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/fake_app_state.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/fake_app_state.go index 71fadbde416995976cfb42999f2a1e2a294d1131..ca4e72c9981350df78eef54786e44cb5aac6360d 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/fake_app_state.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/fake_app_state.go @@ -80,6 +80,6 @@ func createAddress(creator *Account) Word256 { creator.Nonce += 1 temp := make([]byte, 32+8) copy(temp, creator.Address[:]) - PutUint64BE(temp[32:], nonce) + PutInt64BE(temp[32:], nonce) return LeftPadWord256(sha3.Sha3(temp)[:20]) } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/vm_test.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/vm_test.go index 7dd79cc5d0fc0176679fc27df0ec7d1d4442a1b1..f5de60f34fdc7a8d8344e644cbd49425ad6db134 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/vm_test.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/test/vm_test.go @@ -10,16 +10,22 @@ import ( . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/common" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/events" + ptypes "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/permission/types" "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types" . "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/vm" ) func newAppState() *FakeAppState { - return &FakeAppState{ + fas := &FakeAppState{ accounts: make(map[string]*Account), storage: make(map[string]Word256), logs: nil, } + // For default permissions + fas.accounts[ptypes.GlobalPermissionsAddress256.String()] = &Account{ + Permissions: ptypes.DefaultAccountPermissions, + } + return fas } func newParams() Params { @@ -43,13 +49,13 @@ func TestVM(t *testing.T) { // Create accounts account1 := &Account{ - Address: Uint64ToWord256(100), + Address: Int64ToWord256(100), } account2 := &Account{ - Address: Uint64ToWord256(101), + Address: Int64ToWord256(101), } - var gas uint64 = 100000 + var gas int64 = 100000 N := []byte{0x0f, 0x0f} // Loop N times code := []byte{0x60, 0x00, 0x60, 0x20, 0x52, 0x5B, byte(0x60 + len(N) - 1)} @@ -79,7 +85,7 @@ func TestSubcurrency(t *testing.T) { ourVm := NewVM(st, newParams(), Zero256, nil) - var gas uint64 = 1000 + var gas int64 = 1000 code_parts := []string{"620f42403355", "7c0100000000000000000000000000000000000000000000000000000000", "600035046315cf268481141561004657", @@ -103,13 +109,13 @@ func TestSendCall(t *testing.T) { // Create accounts account1 := &Account{ - Address: Uint64ToWord256(100), + Address: Int64ToWord256(100), } account2 := &Account{ - Address: Uint64ToWord256(101), + Address: Int64ToWord256(101), } account3 := &Account{ - Address: Uint64ToWord256(102), + Address: Int64ToWord256(102), } // account1 will call account2 which will trigger CALL opcode to account3 @@ -146,7 +152,7 @@ func TestSendCall(t *testing.T) { } // subscribes to an AccReceive, runs the vm, returns the exception -func runVMWaitEvents(t *testing.T, ourVm *VM, caller, callee *Account, subscribeAddr, contractCode []byte, gas uint64) string { +func runVMWaitEvents(t *testing.T, ourVm *VM, caller, callee *Account, subscribeAddr, contractCode []byte, gas int64) string { // we need to catch the event from the CALL to check for exceptions evsw := new(events.EventSwitch) evsw.Start() diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/types.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/types.go index 6f744486c184d4519ae789e8a8b077398954c19c..2010c13b4c99e2b4d934ff4473c1431956a6d99d 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/types.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/types.go @@ -11,13 +11,13 @@ const ( type Account struct { Address Word256 - Balance uint64 + Balance int64 Code []byte - Nonce uint64 + Nonce int64 StorageRoot Word256 Other interface{} // For holding all other data. - Permissions *ptypes.AccountPermissions + Permissions ptypes.AccountPermissions } func (acc *Account) String() string { @@ -29,7 +29,7 @@ type Log struct { Address Word256 Topics []Word256 Data []byte - Height uint64 + Height int64 } type AppState interface { @@ -49,8 +49,8 @@ type AppState interface { } type Params struct { - BlockHeight uint64 + BlockHeight int64 BlockHash Word256 BlockTime int64 - GasLimit uint64 + GasLimit int64 } diff --git a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go index 1a1e8874e169beedd587b6c34fa0e58bf38331d8..bb17c2a7c72c3c58d9dadbe7177ec345ea6bace4 100644 --- a/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go +++ b/Godeps/_workspace/src/github.com/tendermint/tendermint/vm/vm.go @@ -60,9 +60,6 @@ type VM struct { callDepth int evc events.Fireable - - perms bool // permission checking can be turned off - snative bool // access to snatives } func NewVM(appState AppState, params Params, origin Word256, txid []byte) *VM { @@ -80,25 +77,17 @@ func (vm *VM) SetFireable(evc events.Fireable) { vm.evc = evc } -// to allow calls to native DougContracts (off by default) -func (vm *VM) EnableSNatives() { - vm.snative = true -} - -// run permission checks before call and create -func (vm *VM) EnablePermissions() { - vm.perms = true -} - -// XXX: it is the duty of the contract writer to call known permissions +// CONTRACT: it is the duty of the contract writer to call known permissions // we do not convey if a permission is not set // (unlike in state/execution, where we guarantee HasPermission is called // on known permissions and panics else) +// If the perm is not defined in the acc nor set by default in GlobalPermissions, +// prints a log warning and returns false. func HasPermission(appState AppState, acc *Account, perm ptypes.PermFlag) bool { v, err := acc.Permissions.Base.Get(perm) if _, ok := err.(ptypes.ErrValueNotSet); ok { if appState == nil { - fmt.Printf("\n\n***** Unknown permission %b! ********\n\n", perm) + log.Warn(Fmt("\n\n***** Unknown permission %b! ********\n\n", perm)) return false } return HasPermission(nil, appState.GetAccount(ptypes.GlobalPermissionsAddress256), perm) @@ -110,7 +99,7 @@ func HasPermission(appState AppState, acc *Account, perm ptypes.PermFlag) bool { // value: To be transferred from caller to callee. Refunded upon error. // gas: Available gas. No refunds for gas. // code: May be nil, since the CALL opcode may be used to send value from contracts to accounts -func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, gas *uint64) (output []byte, err error) { +func (vm *VM) Call(caller, callee *Account, code, input []byte, value int64, gas *int64) (output []byte, err error) { exception := new(string) defer func() { @@ -125,8 +114,9 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga } }() + // SNATIVE ACCESS // if code is empty, callee may be snative contract - if vm.snative && len(code) == 0 { + if len(code) == 0 { if snativeContract, ok := RegisteredSNativeContracts[callee.Address]; ok { output, err = snativeContract(vm.appState, caller, input) if err != nil { @@ -135,6 +125,7 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga return } } + // SNATIVE ACCESS END if err = transfer(caller, callee, value); err != nil { *exception = err.Error() @@ -159,14 +150,14 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value uint64, ga } // Just like Call() but does not transfer 'value' or modify the callDepth. -func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, gas *uint64) (output []byte, err error) { +func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas *int64) (output []byte, err error) { dbg.Printf("(%d) (%X) %X (code=%d) gas: %v (d) %X\n", vm.callDepth, caller.Address[:4], callee.Address, len(callee.Code), *gas, input) var ( - pc uint64 = 0 - stack = NewStack(dataStackCapacity, gas, &err) - memory = make([]byte, memoryCapacity) - ok = false // convenience + pc int64 = 0 + stack = NewStack(dataStackCapacity, gas, &err) + memory = make([]byte, memoryCapacity) + ok = false // convenience ) for { @@ -438,7 +429,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga if idx < 32 { res = val[idx] } - stack.Push64(uint64(res)) + stack.Push64(int64(res)) dbg.Printf(" => 0x%X\n", res) case SHA3: // 0x20 @@ -494,7 +485,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga dbg.Printf(" => 0x%X\n", res) case CALLDATASIZE: // 0x36 - stack.Push64(uint64(len(input))) + stack.Push64(int64(len(input))) dbg.Printf(" => %d\n", len(input)) case CALLDATACOPY: // 0x37 @@ -513,7 +504,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga dbg.Printf(" => [%v, %v, %v] %X\n", memOff, inputOff, length, data) case CODESIZE: // 0x38 - l := uint64(len(code)) + l := int64(len(code)) stack.Push64(l) dbg.Printf(" => %d\n", l) @@ -546,7 +537,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga return nil, firstErr(err, ErrUnknownAddress) } code := acc.Code - l := uint64(len(code)) + l := int64(len(code)) stack.Push64(l) dbg.Printf(" => %d\n", l) @@ -584,11 +575,11 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga case TIMESTAMP: // 0x42 time := vm.params.BlockTime - stack.Push64(uint64(time)) + stack.Push64(int64(time)) dbg.Printf(" => 0x%X\n", time) case BLOCKHEIGHT: // 0x43 - number := uint64(vm.params.BlockHeight) + number := int64(vm.params.BlockHeight) stack.Push64(number) dbg.Printf(" => 0x%X\n", number) @@ -654,7 +645,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga stack.Push64(pc) case MSIZE: // 0x59 - stack.Push64(uint64(len(memory))) + stack.Push64(int64(len(memory))) case GAS: // 0x5A stack.Push64(*gas) @@ -665,7 +656,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga // Do nothing case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: - a := uint64(op - PUSH1 + 1) + a := int64(op - PUSH1 + 1) codeSegment, ok := subslice(code, pc+1, a) if !ok { return nil, firstErr(err, ErrCodeOutOfBounds) @@ -708,7 +699,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga dbg.Printf(" => %v\n", log) case CREATE: // 0xF0 - if vm.perms && !HasPermission(vm.appState, callee, ptypes.CreateContract) { + if !HasPermission(vm.appState, callee, ptypes.CreateContract) { return nil, ErrPermission{"create_contract"} } contractValue := stack.Pop64() @@ -736,7 +727,7 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga } case CALL, CALLCODE: // 0xF1, 0xF2 - if vm.perms && !HasPermission(vm.appState, callee, ptypes.Call) { + if !HasPermission(vm.appState, callee, ptypes.Call) { return nil, ErrPermission{"call"} } gasLimit := stack.Pop64() @@ -782,12 +773,12 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga ret, err = vm.Call(callee, callee, acc.Code, args, value, gas) } else { if acc == nil { - if _, ok := RegisteredSNativeContracts[addr]; vm.snative && ok { + if _, ok := RegisteredSNativeContracts[addr]; ok { acc = &Account{Address: addr} } else { // if we have not seen the account before, create it // so we can send funds - if vm.perms && !HasPermission(vm.appState, caller, ptypes.CreateAccount) { + if !HasPermission(vm.appState, caller, ptypes.CreateAccount) { return nil, ErrPermission{"create_account"} } acc = &Account{Address: addr} @@ -852,8 +843,8 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value uint64, ga } } -func subslice(data []byte, offset, length uint64) (ret []byte, ok bool) { - size := uint64(len(data)) +func subslice(data []byte, offset, length int64) (ret []byte, ok bool) { + size := int64(len(data)) if size < offset { return nil, false } else if size < offset+length { @@ -872,15 +863,15 @@ func rightMostBytes(data []byte, n int) []byte { return data[offset:] } -func codeGetOp(code []byte, n uint64) OpCode { - if uint64(len(code)) <= n { +func codeGetOp(code []byte, n int64) OpCode { + if int64(len(code)) <= n { return OpCode(0) // stop } else { return OpCode(code[n]) } } -func jump(code []byte, to uint64, pc *uint64) (err error) { +func jump(code []byte, to int64, pc *int64) (err error) { dest := codeGetOp(code, to) if dest != JUMPDEST { dbg.Printf(" ~> %v invalid jump dest %v\n", to, dest) @@ -899,7 +890,7 @@ func firstErr(errA, errB error) error { } } -func useGas(gas *uint64, gasToUse uint64) bool { +func useGas(gas *int64, gasToUse int64) bool { if *gas > gasToUse { *gas -= gasToUse return true @@ -908,7 +899,7 @@ func useGas(gas *uint64, gasToUse uint64) bool { } } -func transfer(from, to *Account, amount uint64) error { +func transfer(from, to *Account, amount int64) error { if from.Balance < amount { return ErrInsufficientBalance } else { diff --git a/erisdb/params.go b/erisdb/params.go index dc0c532af12b6286e11ce471b1a0f68851186f1c..a1b919b979a63ffee3204cf5ff37ae37408c1742 100644 --- a/erisdb/params.go +++ b/erisdb/params.go @@ -31,7 +31,7 @@ type ( // Get a block HeightParam struct { - Height uint `json:"height"` + Height int `json:"height"` } // Get a series of blocks @@ -77,7 +77,7 @@ type ( PrivKey []byte `json:"priv_key"` Data []byte `json:"data"` Address []byte `json:"address"` - Fee uint64 `json:"fee"` - GasLimit uint64 `json:"gas_limit"` + Fee int64 `json:"fee"` + GasLimit int64 `json:"gas_limit"` } ) diff --git a/erisdb/pipe/blockchain.go b/erisdb/pipe/blockchain.go index 19b11b892e94a0e93e4b69370c0fe19a1d5c5b1b..2f564135289e1735ee2d73dbf70e9d333a0a6865 100644 --- a/erisdb/pipe/blockchain.go +++ b/erisdb/pipe/blockchain.go @@ -68,7 +68,7 @@ func (this *blockchain) GenesisHash() ([]byte, error) { } // Get the latest block height. -func (this *blockchain) LatestBlockHeight() (uint, error) { +func (this *blockchain) LatestBlockHeight() (int, error) { return this.blockStore.Height(), nil } @@ -81,8 +81,8 @@ func (this *blockchain) LatestBlock() (*types.Block, error) { // TODO Caps on total number of blocks should be set. func (this *blockchain) Blocks(fda []*FilterData) (*Blocks, error) { newFda := fda - var minHeight uint - var maxHeight uint + var minHeight int + var maxHeight int height := this.blockStore.Height() if height == 0 { return &Blocks{0, 0, []*types.BlockMeta{}}, nil @@ -116,7 +116,7 @@ func (this *blockchain) Blocks(fda []*FilterData) (*Blocks, error) { } // Get the block at height 'height' -func (this *blockchain) Block(height uint) (*types.Block, error) { +func (this *blockchain) Block(height int) (*types.Block, error) { if height == 0 { return nil, fmt.Errorf("height must be greater than 0") } @@ -137,54 +137,54 @@ func (this *accounts) matchBlock(block, fda []*FilterData) bool { // Ops: All type BlockHeightFilter struct { op string - value uint - match func(uint, uint) bool + value int + match func(int, int) bool } func (this *BlockHeightFilter) Configure(fd *FilterData) error { op := fd.Op - var val uint + var val int if fd.Value == "min" { val = 0 } else if fd.Value == "max" { val = math.MaxUint32 } else { - tv, err := strconv.ParseUint(fd.Value, 10, 0) + tv, err := strconv.ParseInt(fd.Value, 10, 0) if err != nil { return fmt.Errorf("Wrong value type.") } - val = uint(tv) + val = int(tv) } if op == "==" { - this.match = func(a, b uint) bool { + this.match = func(a, b int) bool { return a == b } } else if op == "!=" { - this.match = func(a, b uint) bool { + this.match = func(a, b int) bool { return a != b } } else if op == "<=" { - this.match = func(a, b uint) bool { + this.match = func(a, b int) bool { return a <= b } } else if op == ">=" { - this.match = func(a, b uint) bool { + this.match = func(a, b int) bool { return a >= b } } else if op == "<" { - this.match = func(a, b uint) bool { + this.match = func(a, b int) bool { return a < b } } else if op == ">" { - this.match = func(a, b uint) bool { + this.match = func(a, b int) bool { return a > b } } else { return fmt.Errorf("Op: " + this.op + " is not supported for 'height' filtering") } this.op = op - this.value = uint(val) + this.value = val return nil } @@ -197,25 +197,25 @@ func (this *BlockHeightFilter) Match(v interface{}) bool { } // TODO i should start using named return params... -func getHeightMinMax(fda []*FilterData, height uint) (uint, uint, []*FilterData, error) { +func getHeightMinMax(fda []*FilterData, height int) (int, int, []*FilterData, error) { - min := uint(0) + min := 0 max := height for len(fda) > 0 { fd := fda[0] if strings.EqualFold(fd.Field, "height") { - var val uint + var val int if fd.Value == "min" { val = 0 } else if fd.Value == "max" { val = height } else { - v, err := strconv.ParseUint(fd.Value, 10, 0) + v, err := strconv.ParseInt(fd.Value, 10, 0) if err != nil { return 0, 0, nil, fmt.Errorf("Wrong value type") } - val = uint(v) + val = int(v) } switch fd.Op { case "==": @@ -261,7 +261,7 @@ func getHeightMinMax(fda []*FilterData, height uint) (uint, uint, []*FilterData, return min, max, fda, nil } -func min(x, y uint) uint { +func min(x, y int) int { if x > y { return y } diff --git a/erisdb/pipe/consensus.go b/erisdb/pipe/consensus.go index 344dc877427d3e9eac4023699e36f3a6862dc463..7595caf29d818f82ed10b543c5ab1002fe901545 100644 --- a/erisdb/pipe/consensus.go +++ b/erisdb/pipe/consensus.go @@ -33,17 +33,17 @@ func (this *consensus) State() (*ConsensusState, error) { // Get all validators. func (this *consensus) Validators() (*ValidatorList, error) { - var blockHeight uint + var blockHeight int bondedValidators := make([]*state.Validator, 0) unbondingValidators := make([]*state.Validator, 0) s := this.consensusState.GetState() blockHeight = s.LastBlockHeight - s.BondedValidators.Iterate(func(index uint, val *state.Validator) bool { + s.BondedValidators.Iterate(func(index int, val *state.Validator) bool { bondedValidators = append(bondedValidators, val) return false }) - s.UnbondingValidators.Iterate(func(index uint, val *state.Validator) bool { + s.UnbondingValidators.Iterate(func(index int, val *state.Validator) bool { unbondingValidators = append(unbondingValidators, val) return false }) diff --git a/erisdb/pipe/pipe.go b/erisdb/pipe/pipe.go index c2a203a1556b624310e25d004e7133e2705c8f7f..22c080442772e9c9bed12b11318698d0adc53c3e 100644 --- a/erisdb/pipe/pipe.go +++ b/erisdb/pipe/pipe.go @@ -32,10 +32,10 @@ type ( Info() (*BlockchainInfo, error) GenesisHash() ([]byte, error) ChainId() (string, error) - LatestBlockHeight() (uint, error) + LatestBlockHeight() (int, error) LatestBlock() (*types.Block, error) Blocks([]*FilterData) (*Blocks, error) - Block(height uint) (*types.Block, error) + Block(height int) (*types.Block, error) } Consensus interface { @@ -62,7 +62,7 @@ type ( Call(address, data []byte) (*Call, error) CallCode(code, data []byte) (*Call, error) BroadcastTx(tx types.Tx) (*Receipt, error) - Transact(privKey, address, data []byte, gasLimit, fee uint64) (*Receipt, error) + Transact(privKey, address, data []byte, gasLimit, fee int64) (*Receipt, error) UnconfirmedTxs() (*UnconfirmedTxs, error) SignTx(tx types.Tx, privAccounts []*account.PrivAccount) (types.Tx, error) } diff --git a/erisdb/pipe/transactor.go b/erisdb/pipe/transactor.go index c97d328c4dc985bb2b39851bb3291c5f6a4f0d33..7a96ba9447696029722ab4e281349d9e174233fa 100644 --- a/erisdb/pipe/transactor.go +++ b/erisdb/pipe/transactor.go @@ -47,14 +47,14 @@ func (this *transactor) Call(address, data []byte) (*Call, error) { caller := &vm.Account{Address: cmn.Zero256} txCache := state.NewTxCache(cache) params := vm.Params{ - BlockHeight: uint64(st.LastBlockHeight), + BlockHeight: int64(st.LastBlockHeight), BlockHash: cmn.LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: 10000000, } vmach := vm.NewVM(txCache, params, caller.Address, nil) - gas := uint64(1000000000) + gas := int64(1000000000) ret, err := vmach.Call(caller, callee, callee.Code, data, 0, &gas) if err != nil { return nil, err @@ -72,14 +72,14 @@ func (this *transactor) CallCode(code, data []byte) (*Call, error) { caller := &vm.Account{Address: cmn.Zero256} txCache := state.NewTxCache(cache) params := vm.Params{ - BlockHeight: uint64(st.LastBlockHeight), + BlockHeight: int64(st.LastBlockHeight), BlockHash: cmn.LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: 10000000, } vmach := vm.NewVM(txCache, params, caller.Address, nil) - gas := uint64(1000000000) + gas := int64(1000000000) ret, err := vmach.Call(caller, callee, code, data, 0, &gas) if err != nil { return nil, err @@ -94,14 +94,14 @@ func (this *transactor) BroadcastTx(tx types.Tx) (*Receipt, error) { return nil, fmt.Errorf("Error broadcasting transaction: %v", err) } chainId := config.GetString("chain_id") - txHash := types.TxId(chainId, tx) + txHash := types.TxID(chainId, tx) var createsContract uint8 var contractAddr []byte // check if creates new contract if callTx, ok := tx.(*types.CallTx); ok { if len(callTx.Address) == 0 { createsContract = 1 - contractAddr = state.NewContractAddress(callTx.Input.Address, uint64(callTx.Input.Sequence)) + contractAddr = state.NewContractAddress(callTx.Input.Address, callTx.Input.Sequence) } } return &Receipt{txHash, createsContract, contractAddr}, nil @@ -113,7 +113,7 @@ func (this *transactor) UnconfirmedTxs() (*UnconfirmedTxs, error) { return &UnconfirmedTxs{transactions}, nil } -func (this *transactor) Transact(privKey, address, data []byte, gasLimit, fee uint64) (*Receipt, error) { +func (this *transactor) Transact(privKey, address, data []byte, gasLimit, fee int64) (*Receipt, error) { fmt.Printf("ADDRESS: %v\n", address) var addr []byte if len(address) == 0 { @@ -132,7 +132,7 @@ func (this *transactor) Transact(privKey, address, data []byte, gasLimit, fee ui pa := account.GenPrivAccountFromKey(key) cache := this.mempoolReactor.Mempool.GetCache() acc := cache.GetAccount(pa.Address) - var sequence uint + var sequence int if acc == nil { sequence = 1 } else { @@ -213,7 +213,7 @@ func toVMAccount(acc *account.Account) *vm.Account { Address: cmn.LeftPadWord256(acc.Address), Balance: acc.Balance, Code: acc.Code, // This is crazy. - Nonce: uint64(acc.Sequence), + Nonce: int64(acc.Sequence), StorageRoot: cmn.LeftPadWord256(acc.StorageRoot), Other: acc.PubKey, } diff --git a/erisdb/pipe/types.go b/erisdb/pipe/types.go index bf75a020ab7ed865cabdb92db4d6e644d0d0c8f2..d74271289896c716a0402439c7882c40ed64f5da 100644 --- a/erisdb/pipe/types.go +++ b/erisdb/pipe/types.go @@ -35,7 +35,7 @@ type ( BlockchainInfo struct { ChainId string `json:"chain_id"` GenesisHash []byte `json:"genesis_hash"` - LatestBlockHeight uint `json:"latest_block_height"` + LatestBlockHeight int `json:"latest_block_height"` LatestBlock *types.BlockMeta `json:"latest_block"` } @@ -46,7 +46,7 @@ type ( // Get the latest LatestBlockHeight struct { - Height uint `json:"height"` + Height int `json:"height"` } ChainId struct { @@ -55,8 +55,8 @@ type ( // GetBlocks Blocks struct { - MinHeight uint `json:"min_height"` - MaxHeight uint `json:"max_height"` + MinHeight int `json:"min_height"` + MaxHeight int `json:"max_height"` BlockMetas []*types.BlockMeta `json:"block_metas"` } @@ -64,8 +64,8 @@ type ( // ConsensusState ConsensusState struct { - Height uint `json:"height"` - Round uint `json:"round"` + Height int `json:"height"` + Round int `json:"round"` Step uint8 `json:"step"` StartTime string `json:"start_time"` CommitTime string `json:"commit_time"` @@ -75,7 +75,7 @@ type ( // Validators ValidatorList struct { - BlockHeight uint `json:"block_height"` + BlockHeight int `json:"block_height"` BondedValidators []*sm.Validator `json:"bonded_validators"` UnbondingValidators []*sm.Validator `json:"unbonding_validators"` } @@ -135,7 +135,7 @@ type ( // Call or CallCode Call struct { Return string `json:"return"` - GasUsed uint64 `json:"gas_used"` + GasUsed int64 `json:"gas_used"` // TODO ... } diff --git a/erisdb/restServer.go b/erisdb/restServer.go index 4544afa70ce5df2930d0b9b58764a197e2d0b46b..2cf2432d2be57975ff52d379a742e7fd1aa2a693 100644 --- a/erisdb/restServer.go +++ b/erisdb/restServer.go @@ -208,7 +208,7 @@ func (this *RestServer) handleBlocks(c *gin.Context) { } func (this *RestServer) handleBlock(c *gin.Context) { - height := c.MustGet("height").(uint) + height := c.MustGet("height").(int) block, err := this.pipe.Blockchain().Block(height) if err != nil { c.AbortWithError(500, err) @@ -451,7 +451,7 @@ func heightParam(c *gin.Context) { if h < 0 { c.AbortWithError(400, fmt.Errorf("Negative number used as height.")) } - c.Set("height", uint(h)) + c.Set("height", h) c.Next() } diff --git a/test/mock/pipe.go b/test/mock/pipe.go index caa5bbeafb27bcaa2c065c4a5dc90ef2e9a929a6..166ed9aeac2f261ffdffeb85187497f68400790d 100644 --- a/test/mock/pipe.go +++ b/test/mock/pipe.go @@ -115,7 +115,7 @@ func (this *blockchain) GenesisHash() ([]byte, error) { return this.testOutput.GenesisHash.Hash, nil } -func (this *blockchain) LatestBlockHeight() (uint, error) { +func (this *blockchain) LatestBlockHeight() (int, error) { return this.testOutput.LatestBlockHeight.Height, nil } @@ -127,7 +127,7 @@ func (this *blockchain) Blocks([]*ep.FilterData) (*ep.Blocks, error) { return this.testOutput.Blocks, nil } -func (this *blockchain) Block(height uint) (*types.Block, error) { +func (this *blockchain) Block(height int) (*types.Block, error) { return this.testOutput.Block, nil } @@ -211,7 +211,7 @@ func (this *transactor) UnconfirmedTxs() (*ep.UnconfirmedTxs, error) { return this.testOutput.UnconfirmedTxs, nil } -func (this *transactor) Transact(privKey, address, data []byte, gasLimit, fee uint64) (*ep.Receipt, error) { +func (this *transactor) Transact(privKey, address, data []byte, gasLimit, fee int64) (*ep.Receipt, error) { if address == nil || len(address) == 0 { return this.testOutput.TxCreateReceipt, nil }