RegisterInterface() so we can implement custom JSON codec
This commit is contained in:
parent
7a8a0fefc7
commit
13ca6fb241
|
@ -2,11 +2,9 @@ package account
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/tendermint/go-ed25519"
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
)
|
||||
|
||||
// PrivKey is part of PrivAccount and state.PrivValidator.
|
||||
|
@ -19,23 +17,11 @@ const (
|
|||
PrivKeyTypeEd25519 = byte(0x01)
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
// For binary.readReflect
|
||||
|
||||
func PrivKeyDecoder(r Unreader, n *int64, err *error) interface{} {
|
||||
switch t := PeekByte(r, n, err); t {
|
||||
case PrivKeyTypeEd25519:
|
||||
return ReadBinary(PrivKeyEd25519{}, r, n, err)
|
||||
default:
|
||||
*err = Errorf("Unknown PrivKey type %X", t)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var _ = RegisterType(&TypeInfo{
|
||||
Type: reflect.TypeOf((*PrivKey)(nil)).Elem(),
|
||||
Decoder: PrivKeyDecoder,
|
||||
})
|
||||
// for binary.readReflect
|
||||
var _ = RegisterInterface(
|
||||
struct{ PrivKey }{},
|
||||
ConcreteType{PrivKeyEd25519{}},
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
|
|
|
@ -2,11 +2,9 @@ package account
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/tendermint/go-ed25519"
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
)
|
||||
|
||||
// PubKey is part of Account and Validator.
|
||||
|
@ -22,25 +20,12 @@ const (
|
|||
PubKeyTypeEd25519 = byte(0x01)
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
// for binary.readReflect
|
||||
|
||||
func PubKeyDecoder(r Unreader, n *int64, err *error) interface{} {
|
||||
switch t := PeekByte(r, n, err); t {
|
||||
case PubKeyTypeNil:
|
||||
return PubKeyNil{}
|
||||
case PubKeyTypeEd25519:
|
||||
return ReadBinary(PubKeyEd25519{}, r, n, err)
|
||||
default:
|
||||
*err = Errorf("Unknown PubKey type %X", t)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var _ = RegisterType(&TypeInfo{
|
||||
Type: reflect.TypeOf((*PubKey)(nil)).Elem(),
|
||||
Decoder: PubKeyDecoder,
|
||||
})
|
||||
var _ = RegisterInterface(
|
||||
struct{ PubKey }{},
|
||||
ConcreteType{PubKeyNil{}},
|
||||
ConcreteType{PubKeyEd25519{}},
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package account
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/tendermint/go-ed25519"
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
|
@ -19,23 +18,11 @@ const (
|
|||
SignatureTypeEd25519 = byte(0x01)
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
// for binary.readReflect
|
||||
|
||||
func SignatureDecoder(r Unreader, n *int64, err *error) interface{} {
|
||||
switch t := PeekByte(r, n, err); t {
|
||||
case SignatureTypeEd25519:
|
||||
return ReadBinary(SignatureEd25519{}, r, n, err)
|
||||
default:
|
||||
*err = Errorf("Unknown Signature type %X", t)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var _ = RegisterType(&TypeInfo{
|
||||
Type: reflect.TypeOf((*Signature)(nil)).Elem(),
|
||||
Decoder: SignatureDecoder,
|
||||
})
|
||||
var _ = RegisterInterface(
|
||||
struct{ Signature }{},
|
||||
ConcreteType{SignatureEd25519{}},
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
|
|
|
@ -96,62 +96,15 @@ foo := Foo{Dog{}}
|
|||
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
||||
WriteBinary(foo, buf, n, err)
|
||||
|
||||
// This errors with "Unknown field type interface"
|
||||
// This errors because we don't know whether to read a Dog or Cat.
|
||||
foo2 := ReadBinary(Foo{}, buf, n, err)
|
||||
```
|
||||
|
||||
In the above example, `ReadBinary()` fails because the `Greeter` field for `Foo{}`
|
||||
is ambiguous -- it could be either a `Dog{}` or a `Cat{}`, like a union structure.
|
||||
In this case, the user must define a custom encoder/decoder as follows:
|
||||
The solution is to declare the concrete implementation types for interfaces:
|
||||
|
||||
```go
|
||||
const (
|
||||
GreeterTypeDog = byte(0x01)
|
||||
GreeterTypeCat = byte(0x02)
|
||||
)
|
||||
|
||||
func GreeterEncoder(o interface{}, w io.Writer, n *int64, err *error) {
|
||||
switch o.(type) {
|
||||
case Dog:
|
||||
WriteByte(GreeterTypeDog, w, n, err)
|
||||
WriteBinary(o, w, n, err)
|
||||
case Cat:
|
||||
WriteByte(GreeterTypeCat, w, n, err)
|
||||
WriteBinary(o, w, n, err)
|
||||
default:
|
||||
*err = errors.New(fmt.Sprintf("Unknown greeter type %v", o))
|
||||
}
|
||||
}
|
||||
|
||||
func GreeterDecoder(r Unreader, n *int64, err *error) interface{} {
|
||||
switch t := ReadByte(r, n, err); t {
|
||||
case GreeterTypeDog:
|
||||
return ReadBinary(Dog{}, r, n, err)
|
||||
case GreeterTypeCat:
|
||||
return ReadBinary(Cat{}, r, n, err)
|
||||
default:
|
||||
*err = errors.New(fmt.Sprintf("Unknown greeter type byte %X", t))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// This tells the reflection system to use the custom encoder/decoder functions
|
||||
// for encoding/decoding interface struct-field types.
|
||||
var _ = RegisterType(&TypeInfo{
|
||||
Type: reflect.TypeOf((*Greeter)(nil)).Elem(),
|
||||
Encoder: GreeterEncoder,
|
||||
Decoder: GreeterDecoder,
|
||||
})
|
||||
```
|
||||
|
||||
Sometimes you want to always prefix a globally unique type byte while encoding,
|
||||
whether or not the declared type is an interface or concrete type.
|
||||
In this case, you can declare a "TypeByte() byte" function on the struct (as
|
||||
a value receiver, not a pointer receiver!), and you can skip the declaration of
|
||||
a custom encoder. The decoder must "peek" the byte instead of consuming it.
|
||||
|
||||
```go
|
||||
|
||||
type Dog struct{}
|
||||
func (d Dog) TypeByte() byte { return GreeterTypeDog }
|
||||
func (d Dog) Greet() string { return "Woof!" }
|
||||
|
@ -160,21 +113,18 @@ type Cat struct{}
|
|||
func (c Cat) TypeByte() byte { return GreeterTypeCat }
|
||||
func (c Cat) Greet() string { return "Meow!" }
|
||||
|
||||
func GreeterDecoder(r Unreader, n *int64, err *error) interface{} {
|
||||
// We must peek the type byte because ReadBinary() expects it.
|
||||
switch t := PeekByte(r, n, err); t {
|
||||
case GreeterTypeDog:
|
||||
return ReadBinary(Dog{}, r, n, err)
|
||||
case GreeterTypeCat:
|
||||
return ReadBinary(Cat{}, r, n, err)
|
||||
default:
|
||||
*err = errors.New(fmt.Sprintf("Unknown greeter type byte %X", t))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var _ = RegisterType(&TypeInfo{
|
||||
Type: reflect.TypeOf((*Greeter)(nil)).Elem(),
|
||||
Decoder: GreeterDecoder,
|
||||
var _ = RegisterInterface(
|
||||
struct{Greeter}{},
|
||||
ConcreteType{Dog{}},
|
||||
ConcreteType{Cat{}},
|
||||
})
|
||||
```
|
||||
|
||||
NOTE: The TypeByte() is written and expected to be read even when the struct
|
||||
is encoded or decoded directly:
|
||||
|
||||
```go
|
||||
WriteBinary(Dog{}, buf, n, err) // Writes GreeterTypeDog byte
|
||||
dog_ := ReadBinary(Dog{}, buf, n, err) // Expects to read GreeterTypeDog byte
|
||||
dog := dog_.(Dog) // ok if *err != nil, otherwise dog_ == nil.
|
||||
```
|
|
@ -12,20 +12,41 @@ import (
|
|||
|
||||
type TypeInfo struct {
|
||||
Type reflect.Type // The type
|
||||
Encoder Encoder // Optional custom encoder function
|
||||
Decoder Decoder // Optional custom decoder function
|
||||
|
||||
// Custom encoder/decoder
|
||||
Encoder Encoder
|
||||
Decoder Decoder
|
||||
|
||||
// If Type is kind reflect.Interface
|
||||
ConcreteTypes map[byte]reflect.Type
|
||||
|
||||
// If Type is concrete
|
||||
HasTypeByte bool
|
||||
TypeByte byte
|
||||
}
|
||||
|
||||
// e.g. If o is struct{Foo}{}, return is the Foo interface type.
|
||||
func GetTypeFromStructDeclaration(o interface{}) reflect.Type {
|
||||
rt := reflect.TypeOf(o)
|
||||
if rt.NumField() != 1 {
|
||||
panic("Unexpected number of fields in struct-wrapped declaration of type")
|
||||
}
|
||||
return rt.Field(0).Type
|
||||
}
|
||||
|
||||
// If o implements HasTypeByte, returns (true, typeByte)
|
||||
func GetTypeByteFromStruct(o interface{}) (hasTypeByte bool, typeByte byte) {
|
||||
if _, ok := o.(HasTypeByte); ok {
|
||||
return true, o.(HasTypeByte).TypeByte()
|
||||
} else {
|
||||
return false, byte(0x00)
|
||||
}
|
||||
}
|
||||
|
||||
// If a type implements TypeByte, the byte is included
|
||||
// as the first byte for encoding. This is used to encode
|
||||
// interfaces/union types. In this case the decoding should
|
||||
// be done manually with a switch statement, and so the
|
||||
// reflection-based decoder provided here does not expect this
|
||||
// prefix byte.
|
||||
// See the reactor implementations for use-cases.
|
||||
// as the first byte for encoding and decoding.
|
||||
// This is primarily used to encode interfaces types.
|
||||
// The interface should be declared with RegisterInterface()
|
||||
type HasTypeByte interface {
|
||||
TypeByte() byte
|
||||
}
|
||||
|
@ -45,11 +66,50 @@ func GetTypeInfo(rt reflect.Type) *TypeInfo {
|
|||
return info
|
||||
}
|
||||
|
||||
// For use with the RegisterInterface declaration
|
||||
type ConcreteType struct {
|
||||
O interface{}
|
||||
}
|
||||
|
||||
// Must use this to register an interface to properly decode the
|
||||
// underlying concrete type.
|
||||
func RegisterInterface(o interface{}, args ...interface{}) *TypeInfo {
|
||||
it := GetTypeFromStructDeclaration(o)
|
||||
if it.Kind() != reflect.Interface {
|
||||
panic("RegisterInterface expects an interface")
|
||||
}
|
||||
concreteTypes := make(map[byte]reflect.Type, 0)
|
||||
for _, arg := range args {
|
||||
switch arg.(type) {
|
||||
case ConcreteType:
|
||||
concreteTypeInfo := arg.(ConcreteType)
|
||||
concreteType := reflect.TypeOf(concreteTypeInfo.O)
|
||||
hasTypeByte, typeByte := GetTypeByteFromStruct(concreteTypeInfo.O)
|
||||
//fmt.Println(Fmt("HasTypeByte: %v typeByte: %X type: %X", hasTypeByte, typeByte, concreteType))
|
||||
if !hasTypeByte {
|
||||
panic(Fmt("Expected concrete type %v to implement HasTypeByte", concreteType))
|
||||
}
|
||||
if concreteTypes[typeByte] != nil {
|
||||
panic(Fmt("Duplicate TypeByte for type %v and %v", concreteType, concreteTypes[typeByte]))
|
||||
}
|
||||
concreteTypes[typeByte] = concreteType
|
||||
default:
|
||||
panic(Fmt("Unexpected argument type %v", reflect.TypeOf(arg)))
|
||||
}
|
||||
}
|
||||
typeInfo := &TypeInfo{
|
||||
Type: it,
|
||||
ConcreteTypes: concreteTypes,
|
||||
}
|
||||
typeInfos[it] = typeInfo
|
||||
return typeInfo
|
||||
}
|
||||
|
||||
// Registers and possibly modifies the TypeInfo.
|
||||
// NOTE: not goroutine safe, so only call upon program init.
|
||||
func RegisterType(info *TypeInfo) *TypeInfo {
|
||||
|
||||
// Also register the underlying struct's info, if info.Type is a pointer.
|
||||
// Also register the dereferenced struct if info.Type is a pointer.
|
||||
// Or, if info.Type is not a pointer, register the pointer.
|
||||
var rt, ptrRt reflect.Type
|
||||
if info.Type.Kind() == reflect.Ptr {
|
||||
|
@ -96,8 +156,6 @@ func readReflect(rv reflect.Value, rt reflect.Type, r Unreader, n *int64, err *e
|
|||
// Custom decoder
|
||||
if typeInfo.Decoder != nil {
|
||||
decoded := typeInfo.Decoder(r, n, err)
|
||||
//decodedRv := reflect.Indirect(reflect.ValueOf(decoded))
|
||||
//rv.Set(decodedRv)
|
||||
rv.Set(reflect.ValueOf(decoded))
|
||||
return
|
||||
}
|
||||
|
@ -126,6 +184,19 @@ func readReflect(rv reflect.Value, rt reflect.Type, r Unreader, n *int64, err *e
|
|||
}
|
||||
|
||||
switch rt.Kind() {
|
||||
case reflect.Interface:
|
||||
typeByte := PeekByte(r, n, err)
|
||||
if *err != nil {
|
||||
return
|
||||
}
|
||||
concreteType, ok := typeInfo.ConcreteTypes[typeByte]
|
||||
if !ok {
|
||||
panic(Fmt("TypeByte %X not registered for interface %v", typeByte, rt))
|
||||
}
|
||||
newRv := reflect.New(concreteType)
|
||||
readReflect(newRv.Elem(), concreteType, r, n, err)
|
||||
rv.Set(newRv.Elem())
|
||||
|
||||
case reflect.Slice:
|
||||
elemRt := rt.Elem()
|
||||
if elemRt.Kind() == reflect.Uint8 {
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type SimpleStruct struct {
|
||||
String string
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
type Animal interface{}
|
||||
|
||||
const (
|
||||
AnimalTypeCat = byte(0x01)
|
||||
AnimalTypeDog = byte(0x02)
|
||||
AnimalTypeSnake = byte(0x03)
|
||||
AnimalTypeViper = byte(0x04)
|
||||
)
|
||||
|
||||
// Implements Animal
|
||||
type Cat struct {
|
||||
SimpleStruct
|
||||
}
|
||||
|
||||
func (cat Cat) TypeByte() byte { return AnimalTypeCat }
|
||||
|
||||
// Implements Animal
|
||||
type Dog struct {
|
||||
SimpleStruct
|
||||
}
|
||||
|
||||
func (dog Dog) TypeByte() byte { return AnimalTypeDog }
|
||||
|
||||
// Implements Animal
|
||||
type Snake []byte
|
||||
|
||||
func (snake Snake) TypeByte() byte { return AnimalTypeSnake }
|
||||
|
||||
// Implements Animal
|
||||
type Viper struct {
|
||||
Bytes []byte
|
||||
}
|
||||
|
||||
func (viper *Viper) TypeByte() byte { return AnimalTypeViper }
|
||||
|
||||
var _ = RegisterInterface(
|
||||
struct{ Animal }{},
|
||||
ConcreteType{Cat{}},
|
||||
ConcreteType{Dog{}},
|
||||
ConcreteType{Snake{}},
|
||||
ConcreteType{&Viper{}},
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
cat := Cat{
|
||||
SimpleStruct{
|
||||
String: "String",
|
||||
Bytes: []byte("Bytes"),
|
||||
},
|
||||
}
|
||||
|
||||
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
||||
WriteBinary(cat, buf, n, err)
|
||||
if *err != nil {
|
||||
t.Fatalf("Failed to write cat: %v", *err)
|
||||
}
|
||||
t.Logf("Wrote bytes: %X", buf.Bytes())
|
||||
bufBytes := buf.Bytes()
|
||||
|
||||
// Read onto a struct
|
||||
cat2_ := ReadBinary(Cat{}, buf, n, err)
|
||||
if *err != nil {
|
||||
t.Fatalf("Failed to read cat: %v", *err)
|
||||
}
|
||||
cat2 := cat2_.(Cat)
|
||||
|
||||
if cat2.String != "String" {
|
||||
t.Errorf("Expected cat2.String == 'String', got %v", cat2.String)
|
||||
}
|
||||
if string(cat2.Bytes) != "Bytes" {
|
||||
t.Errorf("Expected cat2.Bytes == 'Bytes', got %X", cat2.Bytes)
|
||||
}
|
||||
|
||||
// Read onto a ptr
|
||||
r := bytes.NewReader(bufBytes)
|
||||
cat3_ := ReadBinary(&Cat{}, r, n, err)
|
||||
if *err != nil {
|
||||
t.Fatalf("Failed to read cat: %v", *err)
|
||||
}
|
||||
cat3 := cat3_.(*Cat)
|
||||
|
||||
if cat3.String != "String" {
|
||||
t.Errorf("Expected cat3.String == 'String', got %v", cat3.String)
|
||||
}
|
||||
if string(cat3.Bytes) != "Bytes" {
|
||||
t.Errorf("Expected cat3.Bytes == 'Bytes', got %X", cat3.Bytes)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
type ComplexStruct struct {
|
||||
Name string
|
||||
Animal Animal
|
||||
}
|
||||
|
||||
func TestComplexStruct(t *testing.T) {
|
||||
c := ComplexStruct{
|
||||
Name: "Complex",
|
||||
Animal: Cat{
|
||||
SimpleStruct{
|
||||
String: "String",
|
||||
Bytes: []byte("Bytes"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
||||
WriteBinary(c, buf, n, err)
|
||||
if *err != nil {
|
||||
t.Fatalf("Failed to write c: %v", *err)
|
||||
}
|
||||
|
||||
t.Logf("Wrote bytes: %X", buf.Bytes())
|
||||
|
||||
c2_ := ReadBinary(&ComplexStruct{}, buf, n, err)
|
||||
if *err != nil {
|
||||
t.Fatalf("Failed to read c: %v", *err)
|
||||
}
|
||||
c2 := c2_.(*ComplexStruct)
|
||||
|
||||
if cat, ok := c2.Animal.(Cat); ok {
|
||||
if cat.String != "String" {
|
||||
t.Errorf("Expected cat.String == 'String', got %v", cat.String)
|
||||
}
|
||||
if string(cat.Bytes) != "Bytes" {
|
||||
t.Errorf("Expected cat.Bytes == 'Bytes', got %X", cat.Bytes)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Expected c2.Animal to be of type cat, got %v", reflect.ValueOf(c2.Animal).Elem().Type())
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
type ComplexArrayStruct struct {
|
||||
Animals []Animal
|
||||
}
|
||||
|
||||
func TestComplexArrayStruct(t *testing.T) {
|
||||
c := ComplexArrayStruct{
|
||||
Animals: []Animal{
|
||||
Cat{
|
||||
SimpleStruct{
|
||||
String: "String",
|
||||
Bytes: []byte("Bytes"),
|
||||
},
|
||||
},
|
||||
&Dog{ // Even though it's a *Dog, we'll get a Dog{} back.
|
||||
SimpleStruct{
|
||||
String: "Woof",
|
||||
Bytes: []byte("Bark"),
|
||||
},
|
||||
},
|
||||
Snake([]byte("hiss")),
|
||||
&Viper{
|
||||
Bytes: []byte("hizz"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
||||
WriteBinary(c, buf, n, err)
|
||||
if *err != nil {
|
||||
t.Fatalf("Failed to write c: %v", *err)
|
||||
}
|
||||
|
||||
t.Logf("Wrote bytes: %X", buf.Bytes())
|
||||
|
||||
c2_ := ReadBinary(&ComplexArrayStruct{}, buf, n, err)
|
||||
if *err != nil {
|
||||
t.Fatalf("Failed to read c: %v", *err)
|
||||
}
|
||||
c2 := c2_.(*ComplexArrayStruct)
|
||||
|
||||
if cat, ok := c2.Animals[0].(Cat); ok {
|
||||
if cat.String != "String" {
|
||||
t.Errorf("Expected cat.String == 'String', got %v", cat.String)
|
||||
}
|
||||
if string(cat.Bytes) != "Bytes" {
|
||||
t.Errorf("Expected cat.Bytes == 'Bytes', got %X", cat.Bytes)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Expected c2.Animals[0] to be of type cat, got %v", reflect.ValueOf(c2.Animals[0]).Elem().Type())
|
||||
}
|
||||
|
||||
if dog, ok := c2.Animals[1].(Dog); ok {
|
||||
if dog.String != "Woof" {
|
||||
t.Errorf("Expected dog.String == 'Woof', got %v", dog.String)
|
||||
}
|
||||
if string(dog.Bytes) != "Bark" {
|
||||
t.Errorf("Expected dog.Bytes == 'Bark', got %X", dog.Bytes)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Expected c2.Animals[1] to be of type dog, got %v", reflect.ValueOf(c2.Animals[1]).Elem().Type())
|
||||
}
|
||||
|
||||
if snake, ok := c2.Animals[2].(Snake); ok {
|
||||
if string(snake) != "hiss" {
|
||||
t.Errorf("Expected string(snake) == 'hiss', got %v", string(snake))
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Expected c2.Animals[2] to be of type Snake, got %v", reflect.ValueOf(c2.Animals[2]).Elem().Type())
|
||||
}
|
||||
|
||||
if viper, ok := c2.Animals[3].(*Viper); ok {
|
||||
if string(viper.Bytes) != "hizz" {
|
||||
t.Errorf("Expected string(viper.Bytes) == 'hizz', got %v", string(viper.Bytes))
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Expected c2.Animals[3] to be of type *Viper, got %v", reflect.ValueOf(c2.Animals[3]).Elem().Type())
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
type ComplexStruct2 struct {
|
||||
Cat Cat
|
||||
Dog *Dog
|
||||
Snake Snake
|
||||
Snake2 *Snake
|
||||
Viper Viper
|
||||
Viper2 *Viper
|
||||
}
|
||||
|
||||
func TestComplexStruct2(t *testing.T) {
|
||||
|
||||
snake_ := Snake([]byte("hiss"))
|
||||
snakePtr_ := &snake_
|
||||
|
||||
c := ComplexStruct2{
|
||||
Cat: Cat{
|
||||
SimpleStruct{
|
||||
String: "String",
|
||||
Bytes: []byte("Bytes"),
|
||||
},
|
||||
},
|
||||
Dog: &Dog{
|
||||
SimpleStruct{
|
||||
String: "Woof",
|
||||
Bytes: []byte("Bark"),
|
||||
},
|
||||
},
|
||||
Snake: Snake([]byte("hiss")),
|
||||
Snake2: snakePtr_,
|
||||
Viper: Viper{Bytes: []byte("hizz")},
|
||||
Viper2: &Viper{Bytes: []byte("hizz")},
|
||||
}
|
||||
|
||||
buf, n, err := new(bytes.Buffer), new(int64), new(error)
|
||||
WriteBinary(c, buf, n, err)
|
||||
if *err != nil {
|
||||
t.Fatalf("Failed to write c: %v", *err)
|
||||
}
|
||||
|
||||
t.Logf("Wrote bytes: %X", buf.Bytes())
|
||||
|
||||
c2_ := ReadBinary(&ComplexStruct2{}, buf, n, err)
|
||||
if *err != nil {
|
||||
t.Fatalf("Failed to read c: %v", *err)
|
||||
}
|
||||
c2 := c2_.(*ComplexStruct2)
|
||||
|
||||
cat := c2.Cat
|
||||
if cat.String != "String" {
|
||||
t.Errorf("Expected cat.String == 'String', got %v", cat.String)
|
||||
}
|
||||
if string(cat.Bytes) != "Bytes" {
|
||||
t.Errorf("Expected cat.Bytes == 'Bytes', got %X", cat.Bytes)
|
||||
}
|
||||
|
||||
dog := c2.Dog
|
||||
if dog.String != "Woof" {
|
||||
t.Errorf("Expected dog.String == 'Woof', got %v", dog.String)
|
||||
}
|
||||
if string(dog.Bytes) != "Bark" {
|
||||
t.Errorf("Expected dog.Bytes == 'Bark', got %X", dog.Bytes)
|
||||
}
|
||||
|
||||
snake := c2.Snake
|
||||
if string(snake) != "hiss" {
|
||||
t.Errorf("Expected string(snake) == 'hiss', got %v", string(snake))
|
||||
}
|
||||
|
||||
snake2 := c2.Snake2
|
||||
if string(*snake2) != "hiss" {
|
||||
t.Errorf("Expected string(snake2) == 'hiss', got %v", string(*snake2))
|
||||
}
|
||||
|
||||
viper := c2.Viper
|
||||
if string(viper.Bytes) != "hizz" {
|
||||
t.Errorf("Expected string(viper.Bytes) == 'hizz', got %v", string(viper.Bytes))
|
||||
}
|
||||
|
||||
viper2 := c2.Viper2
|
||||
if string(viper2.Bytes) != "hizz" {
|
||||
t.Errorf("Expected string(viper2.Bytes) == 'hizz', got %v", string(viper2.Bytes))
|
||||
}
|
||||
}
|
34
block/tx.go
34
block/tx.go
|
@ -3,11 +3,9 @@ package block
|
|||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
. "github.com/tendermint/tendermint/account"
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -49,31 +47,15 @@ const (
|
|||
TxTypeDupeout = byte(0x14)
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
// for binary.readReflect
|
||||
|
||||
func TxDecoder(r Unreader, n *int64, err *error) interface{} {
|
||||
switch t := PeekByte(r, n, err); t {
|
||||
case TxTypeSend:
|
||||
return ReadBinary(&SendTx{}, r, n, err)
|
||||
case TxTypeBond:
|
||||
return ReadBinary(&BondTx{}, r, n, err)
|
||||
case TxTypeUnbond:
|
||||
return ReadBinary(&UnbondTx{}, r, n, err)
|
||||
case TxTypeRebond:
|
||||
return ReadBinary(&RebondTx{}, r, n, err)
|
||||
case TxTypeDupeout:
|
||||
return ReadBinary(&DupeoutTx{}, r, n, err)
|
||||
default:
|
||||
*err = Errorf("Unknown Tx type %X", t)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var _ = RegisterType(&TypeInfo{
|
||||
Type: reflect.TypeOf((*Tx)(nil)).Elem(),
|
||||
Decoder: TxDecoder,
|
||||
})
|
||||
var _ = RegisterInterface(
|
||||
struct{ Tx }{},
|
||||
ConcreteType{&SendTx{}},
|
||||
ConcreteType{&BondTx{}},
|
||||
ConcreteType{&UnbondTx{}},
|
||||
ConcreteType{&RebondTx{}},
|
||||
ConcreteType{&DupeoutTx{}},
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -23,20 +23,11 @@ func getString(prompt string) string {
|
|||
return input[:len(input)-1]
|
||||
}
|
||||
|
||||
func getByteSliceFromBase64(prompt string) []byte {
|
||||
input := getString(prompt)
|
||||
bytes, err := hex.DecodeString(input)
|
||||
if err != nil {
|
||||
Exit(Fmt("Not in hexadecimal format: %v\nError: %v\n", input, err))
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
func getByteSliceFromHex(prompt string) []byte {
|
||||
input := getString(prompt)
|
||||
bytes, err := hex.DecodeString(input)
|
||||
if err != nil {
|
||||
Exit(Fmt("Not in hexadecimal format: %v\nError: %v\n", input, err))
|
||||
Exit(Fmt("Not in hex format: %v\nError: %v\n", input, err))
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
@ -57,13 +48,12 @@ func gen_tx() {
|
|||
state := state_.LoadState(stateDB)
|
||||
|
||||
// Get source pubkey
|
||||
srcPubKeyBytes := getByteSliceFromBase64("Enter source pubkey: ")
|
||||
srcPubKeyBytes := getByteSliceFromHex("Enter source pubkey: ")
|
||||
r, n, err := bytes.NewReader(srcPubKeyBytes), new(int64), new(error)
|
||||
srcPubKey_ := account_.PubKeyDecoder(r, n, err)
|
||||
srcPubKey := binary.ReadBinary(struct{ account_.PubKey }{}, r, n, err).(struct{ account_.PubKey }).PubKey
|
||||
if *err != nil {
|
||||
Exit(Fmt("Invalid PubKey. Error: %v", err))
|
||||
}
|
||||
srcPubKey := srcPubKey_.(account_.PubKey)
|
||||
|
||||
// Get the state of the account.
|
||||
var srcAccount *account_.Account
|
||||
|
@ -112,13 +102,12 @@ func gen_tx() {
|
|||
fmt.Printf("Generated tx: %X\n", binary.BinaryBytes(tx))
|
||||
|
||||
// Get source privkey (for signing)
|
||||
srcPrivKeyBytes := getByteSliceFromBase64("Enter source privkey (for signing): ")
|
||||
srcPrivKeyBytes := getByteSliceFromHex("Enter source privkey (for signing): ")
|
||||
r, n, err = bytes.NewReader(srcPrivKeyBytes), new(int64), new(error)
|
||||
srcPrivKey_ := account_.PrivKeyDecoder(r, n, err)
|
||||
srcPrivKey := binary.ReadBinary(struct{ account_.PrivKey }{}, r, n, err).(struct{ account_.PrivKey }).PrivKey
|
||||
if *err != nil {
|
||||
Exit(Fmt("Invalid PrivKey. Error: %v", err))
|
||||
}
|
||||
srcPrivKey := srcPrivKey_.(account_.PrivKey)
|
||||
|
||||
// Sign
|
||||
tx.Inputs[0].Signature = srcPrivKey.Sign(account_.SignBytes(tx))
|
||||
|
|
|
@ -6,7 +6,8 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/tendermint/tendermint/block"
|
||||
. "github.com/tendermint/tendermint/binary"
|
||||
. "github.com/tendermint/tendermint/block"
|
||||
. "github.com/tendermint/tendermint/common"
|
||||
)
|
||||
|
||||
|
@ -19,11 +20,11 @@ func MempoolHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
reader, n := bytes.NewReader(txBytes), new(int64)
|
||||
tx_ := block.TxDecoder(reader, n, &err)
|
||||
tx_ := ReadBinary(struct{ Tx }{}, reader, n, &err).(struct{ Tx })
|
||||
if err != nil {
|
||||
ReturnJSON(API_INVALID_PARAM, Fmt("Invalid tx_bytes: %v", err))
|
||||
}
|
||||
tx := tx_.(block.Tx)
|
||||
tx := tx_.Tx
|
||||
|
||||
err = mempoolReactor.BroadcastTx(tx)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue