state: implement marshalers for oracle-related state
This commit is contained in:
parent
30526046e7
commit
ca1b8a7df0
2 changed files with 350 additions and 0 deletions
225
pkg/core/native/oracle_types.go
Normal file
225
pkg/core/native/oracle_types.go
Normal file
|
@ -0,0 +1,225 @@
|
|||
package native
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"math/big"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
)
|
||||
|
||||
// IDList is a list of oracle request IDs.
|
||||
type IDList []uint64
|
||||
|
||||
// NodeList represents list or oracle nodes.
|
||||
type NodeList keys.PublicKeys
|
||||
|
||||
// OracleRequest represents oracle request.
|
||||
type OracleRequest struct {
|
||||
OriginalTxID util.Uint256
|
||||
GasForResponse uint64
|
||||
URL string
|
||||
Filter *string
|
||||
CallbackContract util.Uint160
|
||||
CallbackMethod string
|
||||
UserData []byte
|
||||
}
|
||||
|
||||
// Bytes return l serizalized to a byte-slice.
|
||||
func (l IDList) Bytes() []byte {
|
||||
w := io.NewBufBinWriter()
|
||||
l.EncodeBinary(w.BinWriter)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable.
|
||||
func (l IDList) EncodeBinary(w *io.BinWriter) {
|
||||
stackitem.EncodeBinaryStackItem(l.toStackItem(), w)
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable.
|
||||
func (l *IDList) DecodeBinary(r *io.BinReader) {
|
||||
item := stackitem.DecodeBinaryStackItem(r)
|
||||
if r.Err != nil || item == nil {
|
||||
return
|
||||
}
|
||||
r.Err = l.fromStackItem(item)
|
||||
}
|
||||
|
||||
func (l IDList) toStackItem() stackitem.Item {
|
||||
arr := make([]stackitem.Item, len(l))
|
||||
for i := range l {
|
||||
arr[i] = stackitem.NewBigInteger(new(big.Int).SetUint64(l[i]))
|
||||
}
|
||||
return stackitem.NewArray(arr)
|
||||
}
|
||||
|
||||
func (l *IDList) fromStackItem(it stackitem.Item) error {
|
||||
arr, ok := it.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return errors.New("not an array")
|
||||
}
|
||||
*l = make(IDList, len(arr))
|
||||
for i := range arr {
|
||||
bi, err := arr[i].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
(*l)[i] = bi.Uint64()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bytes return l serizalized to a byte-slice.
|
||||
func (l NodeList) Bytes() []byte {
|
||||
w := io.NewBufBinWriter()
|
||||
l.EncodeBinary(w.BinWriter)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable.
|
||||
func (l NodeList) EncodeBinary(w *io.BinWriter) {
|
||||
stackitem.EncodeBinaryStackItem(l.toStackItem(), w)
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable.
|
||||
func (l *NodeList) DecodeBinary(r *io.BinReader) {
|
||||
item := stackitem.DecodeBinaryStackItem(r)
|
||||
if r.Err != nil || item == nil {
|
||||
return
|
||||
}
|
||||
r.Err = l.fromStackItem(item)
|
||||
}
|
||||
|
||||
func (l NodeList) toStackItem() stackitem.Item {
|
||||
arr := make([]stackitem.Item, len(l))
|
||||
for i := range l {
|
||||
arr[i] = stackitem.NewByteArray(l[i].Bytes())
|
||||
}
|
||||
return stackitem.NewArray(arr)
|
||||
}
|
||||
|
||||
func (l *NodeList) fromStackItem(it stackitem.Item) error {
|
||||
arr, ok := it.Value().([]stackitem.Item)
|
||||
if !ok {
|
||||
return errors.New("not an array")
|
||||
}
|
||||
*l = make(NodeList, len(arr))
|
||||
for i := range arr {
|
||||
bs, err := arr[i].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
(*l)[i] = pub
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bytes return o serizalized to a byte-slice.
|
||||
func (o *OracleRequest) Bytes() []byte {
|
||||
w := io.NewBufBinWriter()
|
||||
o.EncodeBinary(w.BinWriter)
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable.
|
||||
func (o *OracleRequest) EncodeBinary(w *io.BinWriter) {
|
||||
stackitem.EncodeBinaryStackItem(o.toStackItem(), w)
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable.
|
||||
func (o *OracleRequest) DecodeBinary(r *io.BinReader) {
|
||||
item := stackitem.DecodeBinaryStackItem(r)
|
||||
if r.Err != nil || item == nil {
|
||||
return
|
||||
}
|
||||
r.Err = o.fromStackItem(item)
|
||||
}
|
||||
|
||||
func (o *OracleRequest) toStackItem() stackitem.Item {
|
||||
filter := stackitem.Item(stackitem.Null{})
|
||||
if o.Filter != nil {
|
||||
filter = stackitem.Make(*o.Filter)
|
||||
}
|
||||
return stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewByteArray(o.OriginalTxID.BytesBE()),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(o.GasForResponse)),
|
||||
stackitem.Make(o.URL),
|
||||
filter,
|
||||
stackitem.NewByteArray(o.CallbackContract.BytesBE()),
|
||||
stackitem.Make(o.CallbackMethod),
|
||||
stackitem.NewByteArray(o.UserData),
|
||||
})
|
||||
}
|
||||
|
||||
func (o *OracleRequest) fromStackItem(it stackitem.Item) error {
|
||||
arr, ok := it.Value().([]stackitem.Item)
|
||||
if !ok || len(arr) < 7 {
|
||||
return errors.New("not an array of needed length")
|
||||
}
|
||||
bs, err := arr[0].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.OriginalTxID, err = util.Uint256DecodeBytesBE(bs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gas, err := arr[1].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.GasForResponse = gas.Uint64()
|
||||
|
||||
s, isNull, ok := itemToString(arr[2])
|
||||
if !ok || isNull {
|
||||
return errors.New("invalid URL")
|
||||
}
|
||||
o.URL = s
|
||||
|
||||
s, isNull, ok = itemToString(arr[3])
|
||||
if !ok {
|
||||
return errors.New("invalid filter")
|
||||
} else if !isNull {
|
||||
filter := s
|
||||
o.Filter = &filter
|
||||
}
|
||||
|
||||
bs, err = arr[4].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.CallbackContract, err = util.Uint160DecodeBytesBE(bs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.CallbackMethod, isNull, ok = itemToString(arr[5])
|
||||
if !ok || isNull {
|
||||
return errors.New("invalid callback method")
|
||||
}
|
||||
|
||||
o.UserData, err = arr[6].TryBytes()
|
||||
return err
|
||||
}
|
||||
|
||||
func itemToString(it stackitem.Item) (string, bool, bool) {
|
||||
_, ok := it.(stackitem.Null)
|
||||
if ok {
|
||||
return "", true, true
|
||||
}
|
||||
bs, err := it.TryBytes()
|
||||
if err != nil || !utf8.Valid(bs) {
|
||||
return "", false, false
|
||||
}
|
||||
return string(bs), false, true
|
||||
}
|
125
pkg/core/native/oracle_types_test.go
Normal file
125
pkg/core/native/oracle_types_test.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
package native
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func getInvalidTestFunc(actual io.Serializable, value interface{}) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
w := io.NewBufBinWriter()
|
||||
it := stackitem.Make(value)
|
||||
stackitem.EncodeBinaryStackItem(it, w.BinWriter)
|
||||
require.NoError(t, w.Err)
|
||||
require.Error(t, testserdes.DecodeBinary(w.Bytes(), actual))
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDList_EncodeBinary(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
l := &IDList{1, 4, 5}
|
||||
testserdes.EncodeDecodeBinary(t, l, new(IDList))
|
||||
})
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
t.Run("NotArray", getInvalidTestFunc(new(IDList), []byte{}))
|
||||
t.Run("InvalidElement", getInvalidTestFunc(new(IDList), []stackitem.Item{stackitem.Null{}}))
|
||||
t.Run("NotStackItem", func(t *testing.T) {
|
||||
require.Error(t, testserdes.DecodeBinary([]byte{0x77}, new(IDList)))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNodeList_EncodeBinary(t *testing.T) {
|
||||
priv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
pub := priv.PublicKey()
|
||||
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
l := &NodeList{pub}
|
||||
testserdes.EncodeDecodeBinary(t, l, new(NodeList))
|
||||
})
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
t.Run("NotArray", getInvalidTestFunc(new(NodeList), []byte{}))
|
||||
t.Run("InvalidElement", getInvalidTestFunc(new(NodeList), []stackitem.Item{stackitem.Null{}}))
|
||||
t.Run("InvalidKey", getInvalidTestFunc(new(NodeList),
|
||||
[]stackitem.Item{stackitem.NewByteArray([]byte{0x9})}))
|
||||
t.Run("NotStackItem", func(t *testing.T) {
|
||||
require.Error(t, testserdes.DecodeBinary([]byte{0x77}, new(NodeList)))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestOracleRequest_EncodeBinary(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
r := &OracleRequest{
|
||||
OriginalTxID: random.Uint256(),
|
||||
GasForResponse: 12345,
|
||||
URL: "https://get.value",
|
||||
CallbackContract: random.Uint160(),
|
||||
CallbackMethod: "method",
|
||||
UserData: []byte{1, 2, 3},
|
||||
}
|
||||
testserdes.EncodeDecodeBinary(t, r, new(OracleRequest))
|
||||
|
||||
t.Run("WithFilter", func(t *testing.T) {
|
||||
s := "filter"
|
||||
r.Filter = &s
|
||||
testserdes.EncodeDecodeBinary(t, r, new(OracleRequest))
|
||||
})
|
||||
})
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
w := io.NewBufBinWriter()
|
||||
t.Run("NotArray", func(t *testing.T) {
|
||||
w.Reset()
|
||||
it := stackitem.NewByteArray([]byte{})
|
||||
stackitem.EncodeBinaryStackItem(it, w.BinWriter)
|
||||
require.Error(t, testserdes.DecodeBinary(w.Bytes(), new(OracleRequest)))
|
||||
})
|
||||
t.Run("NotStackItem", func(t *testing.T) {
|
||||
w.Reset()
|
||||
require.Error(t, testserdes.DecodeBinary([]byte{0x77}, new(OracleRequest)))
|
||||
})
|
||||
|
||||
items := []stackitem.Item{
|
||||
stackitem.NewByteArray(random.Uint256().BytesBE()),
|
||||
stackitem.NewBigInteger(big.NewInt(123)),
|
||||
stackitem.Make("url"),
|
||||
stackitem.Null{},
|
||||
stackitem.NewByteArray(random.Uint160().BytesBE()),
|
||||
stackitem.Make("method"),
|
||||
stackitem.NewByteArray([]byte{1, 2, 3}),
|
||||
}
|
||||
arrItem := stackitem.NewArray(items)
|
||||
runInvalid := func(i int, elem stackitem.Item) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
w.Reset()
|
||||
before := items[i]
|
||||
items[i] = elem
|
||||
stackitem.EncodeBinaryStackItem(arrItem, w.BinWriter)
|
||||
items[i] = before
|
||||
require.Error(t, testserdes.DecodeBinary(w.Bytes(), new(OracleRequest)))
|
||||
}
|
||||
}
|
||||
t.Run("TxID", func(t *testing.T) {
|
||||
t.Run("Type", runInvalid(0, stackitem.NewMap()))
|
||||
t.Run("Length", runInvalid(0, stackitem.NewByteArray([]byte{0, 1, 2})))
|
||||
})
|
||||
t.Run("Gas", runInvalid(1, stackitem.NewMap()))
|
||||
t.Run("URL", runInvalid(2, stackitem.NewMap()))
|
||||
t.Run("Filter", runInvalid(3, stackitem.NewMap()))
|
||||
t.Run("Contract", func(t *testing.T) {
|
||||
t.Run("Type", runInvalid(4, stackitem.NewMap()))
|
||||
t.Run("Length", runInvalid(4, stackitem.NewByteArray([]byte{0, 1, 2})))
|
||||
})
|
||||
t.Run("Method", runInvalid(5, stackitem.NewMap()))
|
||||
t.Run("UserData", runInvalid(6, stackitem.NewMap()))
|
||||
})
|
||||
|
||||
}
|
Loading…
Reference in a new issue