diff --git a/go.mod b/go.mod index 0e44bbbc4..06a63a4f2 100644 --- a/go.mod +++ b/go.mod @@ -19,4 +19,5 @@ require ( golang.org/x/tools v0.0.0-20180318012157-96caea41033d gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect + gopkg.in/h2non/gock.v1 v1.0.12 // indirect ) diff --git a/pkg/rpc/stack_param.go b/pkg/rpc/stack_param.go index 825b7f047..bea6bf882 100644 --- a/pkg/rpc/stack_param.go +++ b/pkg/rpc/stack_param.go @@ -1,6 +1,7 @@ package rpc import ( + "encoding/binary" "encoding/hex" "encoding/json" "strconv" @@ -97,7 +98,7 @@ func (t *StackParamType) UnmarshalJSON(data []byte) (err error) { return } -// StackParam respresent a stack parameter. +// StackParam represent a stack parameter. type StackParam struct { Type StackParamType `json:"type"` Value interface{} `json:"value"` @@ -172,3 +173,89 @@ func (p *StackParam) UnmarshalJSON(data []byte) (err error) { } return } + +type StackParams []StackParam + +func (p StackParams) TryParseArray(vals ...interface{}) error { + var ( + err error + i int + par StackParam + ) + if len(p) != len(vals) { + return errors.New("receiver array doesn't fit the StackParams length") + } + for i, par = range p { + if err = par.TryParse(vals[i]); err != nil { + return err + } + } + return nil +} + +func (p StackParam) TryParse(dest interface{}) error { + var ( + err error + ok bool + data []byte + ) + switch p.Type { + case ByteArray: + if data, ok = p.Value.([]byte); !ok { + return errors.Errorf("failed to cast %s to []byte", p.Value) + } + switch dest := dest.(type) { + case *util.Uint160: + if *dest, err = util.Uint160DecodeBytes(data); err != nil { + return err + } + return nil + case *[]byte: + *dest = data + return nil + case *util.Uint256: + if *dest, err = util.Uint256DecodeBytes(data); err != nil { + return err + } + return nil + case *int64, *int32, *int16, *int8, *int, *uint64, *uint32, *uint16, *uint8, *uint: + i := bytesToUint64(data) + switch dest := dest.(type) { + case *int64: + *dest = int64(i) + case *int32: + *dest = int32(i) + case *int16: + *dest = int16(i) + case *int8: + *dest = int8(i) + case *int: + *dest = int(i) + case *uint64: + *dest = i + case *uint32: + *dest = uint32(i) + case *uint16: + *dest = uint16(i) + case *uint8: + *dest = uint8(i) + case *uint: + *dest = uint(i) + } + case *string: + *dest = string(data) + return nil + default: + return errors.Errorf("cannot cast stackparam of type %s to type %s", p.Type, dest) + } + default: + return errors.New("cannot define stackparam type") + } + return nil +} + +func bytesToUint64(b []byte) uint64 { + data := make([]byte, 8) + copy(data[8-len(b):], util.ArrayReverse(b)) + return binary.BigEndian.Uint64(data) +} diff --git a/pkg/rpc/stack_param_test.go b/pkg/rpc/stack_param_test.go index 9291a7392..0dbe2997d 100644 --- a/pkg/rpc/stack_param_test.go +++ b/pkg/rpc/stack_param_test.go @@ -1,6 +1,7 @@ package rpc import ( + "encoding/hex" "encoding/json" "reflect" "testing" @@ -103,3 +104,120 @@ func TestStackParam_UnmarshalJSON(t *testing.T) { } } } + +const ( + hash160 = "0bcd2978634d961c24f5aea0802297ff128724d6" + hash256 = "7fe610b7c8259ae949accacb091a1bc53219c51a1cb8752fbc6457674c13ec0b" + testString = "myteststring" +) + +func TestStackParam_TryParse(t *testing.T) { + // ByteArray to util.Uint160 conversion + data, err := hex.DecodeString(hash160) + var ( + outputUint160, expectedUint160 util.Uint160 + input = StackParam{ + Type: ByteArray, + Value: data, + } + ) + expectedUint160, err = util.Uint160DecodeString(hash160) + if err = input.TryParse(&outputUint160); err != nil { + t.Errorf("failed to parse stackparam to Uint160: %v", err) + } + if !reflect.DeepEqual(outputUint160, expectedUint160) { + t.Errorf("got (%v), expected (%v)", outputUint160, expectedUint160) + } + + // ByteArray to util.Uint256 conversion + data, err = hex.DecodeString(hash256) + var ( + outputUint256, expectedUint256 util.Uint256 + uint256input = StackParam{ + Type: ByteArray, + Value: data, + } + ) + expectedUint256, err = util.Uint256DecodeString(hash256) + if err = uint256input.TryParse(&outputUint256); err != nil { + t.Errorf("failed to parse stackparam to []byte: %v", err) + } + if !reflect.DeepEqual(outputUint256, expectedUint256) { + t.Errorf("got (%v), expected (%v)", outputUint256, expectedUint256) + } + + // ByteArray to []byte conversion + var ( + outputBytes []byte + expectedBytes = expectedUint160.Bytes() + ) + if err = input.TryParse(&outputBytes); err != nil { + t.Errorf("failed to parse stackparam to []byte: %v", err) + } + if !reflect.DeepEqual(outputBytes, expectedBytes) { + t.Errorf("got (%v), expected (%v)", outputBytes, expectedBytes) + } + + // ByteArray to int64 conversion + data, err = hex.DecodeString("637829cd0b") + var ( + outputInt, expectedInt int64 + intinput = StackParam{ + Type: ByteArray, + Value: data, + } + ) + expectedInt = 50686687331 + if err = intinput.TryParse(&outputInt); err != nil { + t.Errorf("failed to parse stackparam to []byte: %v", err) + } + if !reflect.DeepEqual(outputInt, expectedInt) { + t.Errorf("got (%v), expected (%v)", outputInt, expectedInt) + } + + // ByteArray to string conversion + data = []byte(testString) + var ( + outputStr, expectedStr string + strinput = StackParam{ + Type: ByteArray, + Value: data, + } + ) + expectedStr = testString + if err = strinput.TryParse(&outputStr); err != nil { + t.Errorf("failed to parse stackparam to []byte: %v", err) + } + if !reflect.DeepEqual(outputStr, expectedStr) { + t.Errorf("got (%v), expected (%v)", outputStr, expectedStr) + } + + // StackParams to []util.Uint160 + data, err = hex.DecodeString(hash160) + expUint160, err := util.Uint160DecodeString(hash160) + var ( + params = StackParams{ + StackParam{ + Type: ByteArray, + Value: data, + }, + StackParam{ + Type: ByteArray, + Value: data, + }, + } + expectedArray = []util.Uint160{ + expUint160, + expUint160, + } + out1, out2 = &util.Uint160{}, &util.Uint160{} + ) + if err = params.TryParseArray(out1, out2); err != nil { + t.Errorf("failed to parse stackparam to []byte: %v", err) + } + outArray := []util.Uint160{*out1, *out2} + if !reflect.DeepEqual(outArray, expectedArray) { + t.Errorf("got (%v), expected (%v)", outArray, expectedArray) + } + +} diff --git a/pkg/util/array.go b/pkg/util/array.go index b6636f81a..09f1783b1 100644 --- a/pkg/util/array.go +++ b/pkg/util/array.go @@ -7,7 +7,7 @@ func ArrayReverse(b []byte) []byte { return b } dest := make([]byte, len(b)) - for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { + for i, j := 0, len(b)-1; i <= j; i, j = i+1, j-1 { dest[i], dest[j] = b[j], b[i] } return dest diff --git a/pkg/util/array_test.go b/pkg/util/array_test.go index 8f587cef9..c75e7421e 100644 --- a/pkg/util/array_test.go +++ b/pkg/util/array_test.go @@ -6,9 +6,9 @@ import ( ) func TestArrayReverse(t *testing.T) { - arr := []byte{0x01, 0x02, 0x03, 0x04} + arr := []byte{0x01, 0x02, 0x03, 0x04, 0x05} have := ArrayReverse(arr) - want := []byte{0x04, 0x03, 0x02, 0x01} + want := []byte{0x05, 0x04, 0x03, 0x02, 0x01} if bytes.Compare(have, want) != 0 { t.Fatalf("expected %v got %v", want, have) }