core: move System.Binary.* interops to binary/ package

Also extend test suite.
This commit is contained in:
Evgenii Stratonikov 2020-12-02 12:29:14 +03:00
parent 2eb256014e
commit d136569ac8
5 changed files with 155 additions and 100 deletions

View file

@ -0,0 +1,57 @@
package binary
import (
"encoding/base64"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/vm"
)
// Serialize serializes top stack item into a ByteArray.
func Serialize(ic *interop.Context) error {
return vm.RuntimeSerialize(ic.VM)
}
// Deserialize deserializes ByteArray from a stack into an item.
func Deserialize(ic *interop.Context) error {
return vm.RuntimeDeserialize(ic.VM)
}
// EncodeBase64 encodes top stack item into a base64 string.
func EncodeBase64(ic *interop.Context) error {
src := ic.VM.Estack().Pop().Bytes()
result := base64.StdEncoding.EncodeToString(src)
ic.VM.Estack().PushVal([]byte(result))
return nil
}
// DecodeBase64 decodes top stack item from base64 string to byte array.
func DecodeBase64(ic *interop.Context) error {
src := ic.VM.Estack().Pop().String()
result, err := base64.StdEncoding.DecodeString(src)
if err != nil {
return err
}
ic.VM.Estack().PushVal(result)
return nil
}
// EncodeBase58 encodes top stack item into a base58 string.
func EncodeBase58(ic *interop.Context) error {
src := ic.VM.Estack().Pop().Bytes()
result := base58.Encode(src)
ic.VM.Estack().PushVal([]byte(result))
return nil
}
// DecodeBase58 decodes top stack item from base58 string to byte array.
func DecodeBase58(ic *interop.Context) error {
src := ic.VM.Estack().Pop().String()
result, err := base58.Decode(src)
if err != nil {
return err
}
ic.VM.Estack().PushVal(result)
return nil
}

View file

@ -0,0 +1,92 @@
package binary
import (
"encoding/base64"
"math/big"
"testing"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
func TestRuntimeSerialize(t *testing.T) {
t.Run("recursive", func(t *testing.T) {
arr := stackitem.NewArray(nil)
arr.Append(arr)
ic := &interop.Context{VM: vm.New()}
ic.VM.Estack().PushVal(arr)
require.Error(t, Serialize(ic))
})
t.Run("big item", func(t *testing.T) {
ic := &interop.Context{VM: vm.New()}
ic.VM.Estack().PushVal(make([]byte, stackitem.MaxSize))
require.Error(t, Serialize(ic))
})
t.Run("good", func(t *testing.T) {
ic := &interop.Context{VM: vm.New()}
ic.VM.Estack().PushVal(42)
require.NoError(t, Serialize(ic))
w := io.NewBufBinWriter()
stackitem.EncodeBinaryStackItem(stackitem.Make(42), w.BinWriter)
require.NoError(t, w.Err)
encoded := w.Bytes()
require.Equal(t, encoded, ic.VM.Estack().Pop().Bytes())
ic.VM.Estack().PushVal(encoded)
require.NoError(t, Deserialize(ic))
require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().BigInt())
t.Run("bad", func(t *testing.T) {
encoded[0] ^= 0xFF
ic.VM.Estack().PushVal(encoded)
require.Error(t, Deserialize(ic))
})
})
}
func TestRuntimeEncodeDecode(t *testing.T) {
original := []byte("my pretty string")
encoded64 := base64.StdEncoding.EncodeToString(original)
encoded58 := base58.Encode(original)
v := vm.New()
ic := &interop.Context{VM: v}
t.Run("Encode64", func(t *testing.T) {
v.Estack().PushVal(original)
require.NoError(t, EncodeBase64(ic))
actual := v.Estack().Pop().Bytes()
require.Equal(t, []byte(encoded64), actual)
})
t.Run("Encode58", func(t *testing.T) {
v.Estack().PushVal(original)
require.NoError(t, EncodeBase58(ic))
actual := v.Estack().Pop().Bytes()
require.Equal(t, []byte(encoded58), actual)
})
t.Run("Decode64/positive", func(t *testing.T) {
v.Estack().PushVal(encoded64)
require.NoError(t, DecodeBase64(ic))
actual := v.Estack().Pop().Bytes()
require.Equal(t, original, actual)
})
t.Run("Decode64/error", func(t *testing.T) {
v.Estack().PushVal(encoded64 + "%")
require.Error(t, DecodeBase64(ic))
})
t.Run("Decode58/positive", func(t *testing.T) {
v.Estack().PushVal(encoded58)
require.NoError(t, DecodeBase58(ic))
actual := v.Estack().Pop().Bytes()
require.Equal(t, original, actual)
})
t.Run("Decode58/error", func(t *testing.T) {
v.Estack().PushVal(encoded58 + "%")
require.Error(t, DecodeBase58(ic))
})
}

View file

@ -2,14 +2,12 @@ package core
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"math"
"sort"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/core/state"
@ -193,51 +191,3 @@ func callDeploy(ic *interop.Context, cs *state.Contract, isUpdate bool) error {
}
return nil
}
// runtimeSerialize serializes top stack item into a ByteArray.
func runtimeSerialize(ic *interop.Context) error {
return vm.RuntimeSerialize(ic.VM)
}
// runtimeDeserialize deserializes ByteArray from a stack into an item.
func runtimeDeserialize(ic *interop.Context) error {
return vm.RuntimeDeserialize(ic.VM)
}
// runtimeEncodeBase64 encodes top stack item into a base64 string.
func runtimeEncodeBase64(ic *interop.Context) error {
src := ic.VM.Estack().Pop().Bytes()
result := base64.StdEncoding.EncodeToString(src)
ic.VM.Estack().PushVal([]byte(result))
return nil
}
// runtimeDecodeBase64 decodes top stack item from base64 string to byte array.
func runtimeDecodeBase64(ic *interop.Context) error {
src := ic.VM.Estack().Pop().String()
result, err := base64.StdEncoding.DecodeString(src)
if err != nil {
return err
}
ic.VM.Estack().PushVal(result)
return nil
}
// runtimeEncodeBase58 encodes top stack item into a base58 string.
func runtimeEncodeBase58(ic *interop.Context) error {
src := ic.VM.Estack().Pop().Bytes()
result := base58.Encode(src)
ic.VM.Estack().PushVal([]byte(result))
return nil
}
// runtimeDecodeBase58 decodes top stack item from base58 string to byte array.
func runtimeDecodeBase58(ic *interop.Context) error {
src := ic.VM.Estack().Pop().String()
result, err := base58.Decode(src)
if err != nil {
return err
}
ic.VM.Estack().PushVal(result)
return nil
}

View file

@ -1,11 +1,9 @@
package core
import (
"encoding/base64"
"fmt"
"testing"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/dao"
@ -213,48 +211,6 @@ func TestECDSAVerify(t *testing.T) {
})
}
func TestRuntimeEncodeDecode(t *testing.T) {
original := []byte("my pretty string")
encoded64 := base64.StdEncoding.EncodeToString(original)
encoded58 := base58.Encode(original)
v, ic, bc := createVM(t)
defer bc.Close()
t.Run("Encode64", func(t *testing.T) {
v.Estack().PushVal(original)
require.NoError(t, runtimeEncodeBase64(ic))
actual := v.Estack().Pop().Bytes()
require.Equal(t, []byte(encoded64), actual)
})
t.Run("Encode58", func(t *testing.T) {
v.Estack().PushVal(original)
require.NoError(t, runtimeEncodeBase58(ic))
actual := v.Estack().Pop().Bytes()
require.Equal(t, []byte(encoded58), actual)
})
t.Run("Decode64/positive", func(t *testing.T) {
v.Estack().PushVal(encoded64)
require.NoError(t, runtimeDecodeBase64(ic))
actual := v.Estack().Pop().Bytes()
require.Equal(t, original, actual)
})
t.Run("Decode64/error", func(t *testing.T) {
v.Estack().PushVal(encoded64 + "%")
require.Error(t, runtimeDecodeBase64(ic))
})
t.Run("Decode58/positive", func(t *testing.T) {
v.Estack().PushVal(encoded58)
require.NoError(t, runtimeDecodeBase58(ic))
actual := v.Estack().Pop().Bytes()
require.Equal(t, original, actual)
})
t.Run("Decode58/error", func(t *testing.T) {
v.Estack().PushVal(encoded58 + "%")
require.Error(t, runtimeDecodeBase58(ic))
})
}
// Helper functions to create VM, InteropContext, TX, Account, Contract.
func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) {

View file

@ -34,13 +34,13 @@ func SpawnVM(ic *interop.Context) *vm.VM {
// All lists are sorted, keep 'em this way, please.
var systemInterops = []interop.Function{
{Name: interopnames.SystemBinaryAtoi, Func: binary.Atoi, Price: 100000, ParamCount: 2},
{Name: interopnames.SystemBinaryBase58Decode, Func: runtimeDecodeBase58, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBinaryBase58Encode, Func: runtimeEncodeBase58, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBinaryBase64Decode, Func: runtimeDecodeBase64, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBinaryBase64Encode, Func: runtimeEncodeBase64, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBinaryDeserialize, Func: runtimeDeserialize, Price: 500000, ParamCount: 1},
{Name: interopnames.SystemBinaryBase58Decode, Func: binary.DecodeBase58, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBinaryBase58Encode, Func: binary.EncodeBase58, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBinaryBase64Decode, Func: binary.DecodeBase64, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBinaryBase64Encode, Func: binary.EncodeBase64, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBinaryDeserialize, Func: binary.Deserialize, Price: 500000, ParamCount: 1},
{Name: interopnames.SystemBinaryItoa, Func: binary.Itoa, Price: 100000, ParamCount: 2},
{Name: interopnames.SystemBinarySerialize, Func: runtimeSerialize, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBinarySerialize, Func: binary.Serialize, Price: 100000, ParamCount: 1},
{Name: interopnames.SystemBlockchainGetBlock, Func: bcGetBlock, Price: 2500000,
RequiredFlags: smartcontract.AllowStates, ParamCount: 1},
{Name: interopnames.SystemBlockchainGetContract, Func: bcGetContract, Price: 1000000,