diff --git a/binary/reflect.go b/binary/reflect.go index 3aea034e3..4bc0971bc 100644 --- a/binary/reflect.go +++ b/binary/reflect.go @@ -250,6 +250,33 @@ func readReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, r io.Rea } switch rt.Kind() { + case reflect.Array: + elemRt := rt.Elem() + length := rt.Len() + if elemRt.Kind() == reflect.Uint8 { + // Special case: Bytearrays + buf := make([]byte, length) + ReadFull(buf, r, n, err) + if *err != nil { + return + } + log.Debug("Read bytearray", "bytes", buf) + reflect.Copy(rv, reflect.ValueOf(buf)) + } else { + for i := 0; i < length; i++ { + elemRv := rv.Index(i) + readReflectBinary(elemRv, elemRt, opts, r, n, err) + if *err != nil { + return + } + if MaxBinaryReadSize < *n { + *err = ErrBinaryReadSizeOverflow + return + } + } + log.Debug(Fmt("Read %v-array", elemRt), "length", length) + } + case reflect.Slice: elemRt := rt.Elem() if elemRt.Kind() == reflect.Uint8 { @@ -438,6 +465,27 @@ func writeReflectBinary(rv reflect.Value, rt reflect.Type, opts Options, w io.Wr // All other types switch rt.Kind() { + case reflect.Array: + elemRt := rt.Elem() + length := rt.Len() + if elemRt.Kind() == reflect.Uint8 { + // Special case: Bytearrays + if rv.CanAddr() { + byteslice := rv.Slice(0, length).Bytes() + WriteTo(byteslice, w, n, err) + } else { + buf := make([]byte, length) + reflect.Copy(reflect.ValueOf(buf), rv) + WriteTo(buf, w, n, err) + } + } else { + // Write elems + for i := 0; i < length; i++ { + elemRv := rv.Index(i) + writeReflectBinary(elemRv, elemRt, opts, w, n, err) + } + } + case reflect.Slice: elemRt := rt.Elem() if elemRt.Kind() == reflect.Uint8 { @@ -602,6 +650,44 @@ func readReflectJSON(rv reflect.Value, rt reflect.Type, o interface{}, err *erro } switch rt.Kind() { + case reflect.Array: + elemRt := rt.Elem() + length := rt.Len() + if elemRt.Kind() == reflect.Uint8 { + // Special case: Bytearrays + oString, ok := o.(string) + if !ok { + *err = errors.New(Fmt("Expected string but got type %v", reflect.TypeOf(o))) + return + } + buf, err_ := hex.DecodeString(oString) + if err_ != nil { + *err = err_ + return + } + if len(buf) != length { + *err = errors.New(Fmt("Expected bytearray of length %v but got %v", length, len(buf))) + return + } + log.Debug("Read bytearray", "bytes", buf) + rv.Set(reflect.ValueOf(buf)) + } else { + oSlice, ok := o.([]interface{}) + if !ok { + *err = errors.New(Fmt("Expected array of %v but got type %v", rt, reflect.TypeOf(o))) + return + } + if len(oSlice) != length { + *err = errors.New(Fmt("Expected array of length %v but got %v", length, len(oSlice))) + return + } + for i := 0; i < length; i++ { + elemRv := rv.Index(i) + readReflectJSON(elemRv, elemRt, oSlice[i], err) + } + log.Debug(Fmt("Read %v-array", elemRt), "length", length) + } + case reflect.Slice: elemRt := rt.Elem() if elemRt.Kind() == reflect.Uint8 { @@ -773,13 +859,32 @@ func writeReflectJSON(rv reflect.Value, rt reflect.Type, w io.Writer, n *int64, // All other types switch rt.Kind() { + case reflect.Array: + elemRt := rt.Elem() + length := rt.Len() + if elemRt.Kind() == reflect.Uint8 { + // Special case: Bytearray + bytearray := rv.Interface() + WriteTo([]byte(Fmt("\"%X\"", bytearray)), w, n, err) + } else { + WriteTo([]byte("["), w, n, err) + // Write elems + for i := 0; i < length; i++ { + elemRv := rv.Index(i) + writeReflectJSON(elemRv, elemRt, w, n, err) + if i < length-1 { + WriteTo([]byte(","), w, n, err) + } + } + WriteTo([]byte("]"), w, n, err) + } + case reflect.Slice: elemRt := rt.Elem() if elemRt.Kind() == reflect.Uint8 { // Special case: Byteslices byteslice := rv.Bytes() WriteTo([]byte(Fmt("\"%X\"", byteslice)), w, n, err) - //WriteByteSlice(byteslice, w, n, err) } else { WriteTo([]byte("["), w, n, err) // Write elems diff --git a/types/events.go b/types/events.go index 39744ad33..c6d46bcbb 100644 --- a/types/events.go +++ b/types/events.go @@ -18,6 +18,10 @@ func EventStringAccReceive(addr []byte) string { return fmt.Sprintf("Acc/%X/Receive", addr) } +func EventStringLogEvent(addr []byte) string { + return fmt.Sprintf("Log/%X", addr) +} + func EventStringBond() string { return "Bond" } @@ -66,19 +70,3 @@ type EventMsgCall struct { Return []byte `json:"return"` Exception string `json:"exception"` } - -/* -Acc/XYZ/Input -> full tx or {full tx, return value, exception} -Acc/XYZ/Output -> full tx -Acc/XYZ/Receive -> full tx, return value, exception, (optionally?) calldata -Bond -> full tx -Unbond -> full tx -Rebond -> full tx -Dupeout -> full tx -NewBlock -> full block -Fork -> block A, block B - -Log -> Fuck this -NewPeer -> peer -Alert -> alert msg -*/ diff --git a/vm/test/log_event_test.go b/vm/test/log_event_test.go new file mode 100644 index 000000000..3c53b324d --- /dev/null +++ b/vm/test/log_event_test.go @@ -0,0 +1,87 @@ +package vm + +import ( + "bytes" + "reflect" + "testing" + + . "github.com/tendermint/tendermint/common" + "github.com/tendermint/tendermint/events" + "github.com/tendermint/tendermint/types" + . "github.com/tendermint/tendermint/vm" +) + +var expectedData = []byte{0x10} +var expectedHeight int64 = 0 +var expectedTopics = []Word256{ + Int64ToWord256(1), + Int64ToWord256(2), + Int64ToWord256(3), + Int64ToWord256(4)} + +// Tests logs and events. +func TestLog4(t *testing.T) { + + st := newAppState() + // Create accounts + account1 := &Account{ + Address: LeftPadWord256(makeBytes(20)), + } + account2 := &Account{ + Address: LeftPadWord256(makeBytes(20)), + } + st.accounts[account1.Address.String()] = account1 + st.accounts[account2.Address.String()] = account2 + + ourVm := NewVM(st, newParams(), Zero256, nil) + + eventSwitch := &events.EventSwitch{} + eventSwitch.Start() + eventId := types.EventStringLogEvent(account2.Address.Postfix(20)) + + doneChan := make(chan struct{}, 1) + + eventSwitch.AddListenerForEvent("test", eventId, func(event interface{}) { + logEvent := event.(*Log) + // No need to test address as this event would not happen if it wasn't correct + if !reflect.DeepEqual(logEvent.Topics, expectedTopics) { + t.Errorf("Event topics are wrong. Got: %v. Expected: %v", logEvent.Topics, expectedTopics) + } + if !bytes.Equal(logEvent.Data, expectedData) { + t.Errorf("Event data is wrong. Got: %s. Expected: %s", logEvent.Data, expectedData) + } + if logEvent.Height != expectedHeight { + t.Errorf("Event block height is wrong. Got: %d. Expected: %d", logEvent.Height, expectedHeight) + } + doneChan <- struct{}{} + }) + + ourVm.SetFireable(eventSwitch) + + var gas int64 = 100000 + + mstore8 := byte(MSTORE8) + push1 := byte(PUSH1) + log4 := byte(LOG4) + stop := byte(STOP) + + code := []byte{ + push1, 16, // data value + push1, 0, // memory slot + mstore8, + push1, 4, // topic 4 + push1, 3, // topic 3 + push1, 2, // topic 2 + push1, 1, // topic 1 + push1, 1, // size of data + push1, 0, // data starts at this offset + log4, + stop, + } + + _, err := ourVm.Call(account1, account2, code, []byte{}, 0, &gas) + <-doneChan + if err != nil { + t.Fatal(err) + } +} diff --git a/vm/vm.go b/vm/vm.go index 16f9dc1b1..c678fc8e8 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -696,7 +696,13 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas vm.params.BlockHeight, } vm.appState.AddLog(log) - dbg.Printf(" => %v\n", log) + if vm.evc != nil { + eventId := types.EventStringLogEvent(callee.Address.Postfix(20)) + fmt.Printf("eventId: %s\n", eventId) + vm.evc.FireEvent(eventId, log) + } + // Using sol-log for this as well since 'log' will print garbage. + dbg.Printf(" => T:%X D:%X\n", log.Topics, log.Data) case CREATE: // 0xF0 if !HasPermission(vm.appState, callee, ptypes.CreateContract) {