native: move OracleRequest to state package

It is used by multiple modules outside native
and produces unneeded dependencies.
This commit is contained in:
Evgenii Stratonikov 2020-10-07 15:06:10 +03:00
parent 705d04eda6
commit 037cecf1ac
6 changed files with 209 additions and 190 deletions

View file

@ -453,7 +453,7 @@ func TestVerifyTx(t *testing.T) {
})
t.Run("Oracle", func(t *testing.T) {
orc := bc.contracts.Oracle
req := &native.OracleRequest{GasForResponse: 1000_0000}
req := &state.OracleRequest{GasForResponse: 1000_0000}
require.NoError(t, orc.PutRequestInternal(1, req, bc.dao))
oracleScript, err := smartcontract.CreateMajorityMultiSigRedeemScript(oraclePubs)

View file

@ -138,7 +138,7 @@ func (o *Oracle) PostPersist(ic *interop.Context) error {
continue
}
reqKey := makeRequestKey(resp.ID)
req := new(OracleRequest)
req := new(state.OracleRequest)
if err := o.getSerializableFromDAO(ic.DAO, reqKey, req); err != nil {
return err
}
@ -299,7 +299,7 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url, filter, cb string, us
return ErrBigArgument
}
req := &OracleRequest{
req := &state.OracleRequest{
OriginalTxID: o.getOriginalTxID(ic.DAO, ic.Tx),
GasForResponse: gas.Uint64(),
URL: url,
@ -312,7 +312,7 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url, filter, cb string, us
}
// PutRequestInternal puts oracle request with the specified id to d.
func (o *Oracle) PutRequestInternal(id uint64, req *OracleRequest, d dao.DAO) error {
func (o *Oracle) PutRequestInternal(id uint64, req *state.OracleRequest, d dao.DAO) error {
reqItem := &state.StorageItem{Value: req.Bytes()}
reqKey := makeRequestKey(id)
if err := d.PutStorageItem(o.ContractID, reqKey, reqItem); err != nil {
@ -345,9 +345,9 @@ func (o *Oracle) GetOracleNodes(d dao.DAO) (keys.PublicKeys, error) {
}
// GetRequestInternal returns request by ID and key under which it is stored.
func (o *Oracle) GetRequestInternal(d dao.DAO, id uint64) (*OracleRequest, error) {
func (o *Oracle) GetRequestInternal(d dao.DAO, id uint64) (*state.OracleRequest, error) {
key := makeRequestKey(id)
req := new(OracleRequest)
req := new(state.OracleRequest)
return req, o.getSerializableFromDAO(d, key, req)
}

View file

@ -4,11 +4,9 @@ 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"
)
@ -18,17 +16,6 @@ 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()
@ -136,104 +123,3 @@ func (l *NodeList) fromStackItem(it stackitem.Item) error {
}
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
}

View file

@ -1,11 +1,9 @@
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"
@ -71,71 +69,3 @@ func TestNodeList_EncodeBinary(t *testing.T) {
})
})
}
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()))
})
}

123
pkg/core/state/oracle.go Normal file
View file

@ -0,0 +1,123 @@
package state
import (
"errors"
"math/big"
"unicode/utf8"
"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"
)
// 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 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
}

View file

@ -0,0 +1,80 @@
package state
import (
"math/big"
"testing"
"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 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()))
})
}