diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index c88a0d928..276acb26b 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -9,8 +9,10 @@ type Syscall struct { // All lists are sorted, keep 'em this way, please. var syscalls = map[string]map[string]Syscall{ "binary": { - "Deserialize": {"System.Binary.Deserialize", false}, - "Serialize": {"System.Binary.Serialize", false}, + "Base64Decode": {"System.Binary.Base64Decode", false}, + "Base64Encode": {"System.Binary.Base64Encode", false}, + "Deserialize": {"System.Binary.Deserialize", false}, + "Serialize": {"System.Binary.Serialize", false}, }, "blockchain": { "GetBlock": {"System.Blockchain.GetBlock", true}, diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 721d8ddc0..cf41306c4 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -2,6 +2,7 @@ package core import ( "bytes" + "encoding/base64" "errors" "fmt" "sort" @@ -192,3 +193,22 @@ func runtimeSerialize(_ *interop.Context, v *vm.VM) error { func runtimeDeserialize(_ *interop.Context, v *vm.VM) error { return vm.RuntimeDeserialize(v) } + +// runtimeEncode encodes top stack item into a base64 string. +func runtimeEncode(_ *interop.Context, v *vm.VM) error { + src := v.Estack().Pop().Bytes() + result := base64.StdEncoding.EncodeToString(src) + v.Estack().PushVal([]byte(result)) + return nil +} + +// runtimeDecode decodes top stack item from base64 string to byte array. +func runtimeDecode(_ *interop.Context, v *vm.VM) error { + src := string(v.Estack().Pop().Bytes()) + result, err := base64.StdEncoding.DecodeString(src) + if err != nil { + return err + } + v.Estack().PushVal(result) + return nil +} diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 9d26b88b0..2336fed8a 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -1,6 +1,7 @@ package core import ( + "encoding/base64" "fmt" "testing" @@ -214,6 +215,39 @@ func TestECDSAVerify(t *testing.T) { }) } +func TestRuntimeEncode(t *testing.T) { + str := []byte("my pretty string") + v, ic, bc := createVM(t) + defer bc.Close() + + v.Estack().PushVal(str) + require.NoError(t, runtimeEncode(ic, v)) + + expected := []byte(base64.StdEncoding.EncodeToString(str)) + actual := v.Estack().Pop().Bytes() + require.Equal(t, expected, actual) +} + +func TestRuntimeDecode(t *testing.T) { + expected := []byte("my pretty string") + str := base64.StdEncoding.EncodeToString(expected) + v, ic, bc := createVM(t) + defer bc.Close() + + t.Run("positive", func(t *testing.T) { + v.Estack().PushVal(str) + require.NoError(t, runtimeDecode(ic, v)) + + actual := v.Estack().Pop().Bytes() + require.Equal(t, expected, actual) + }) + + t.Run("error", func(t *testing.T) { + v.Estack().PushVal(str + "%") + require.Error(t, runtimeDecode(ic, v)) + }) +} + // Helper functions to create VM, InteropContext, TX, Account, Contract. func createVM(t *testing.T) (*vm.VM, *interop.Context, *Blockchain) { diff --git a/pkg/core/interops.go b/pkg/core/interops.go index d2dcd6958..2e9ebed86 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -69,6 +69,8 @@ func getInteropFromSlice(ic *interop.Context, slice []interop.Function) func(uin // All lists are sorted, keep 'em this way, please. var systemInterops = []interop.Function{ + {Name: "System.Binary.Base64Decode", Func: runtimeDecode, Price: 100000}, + {Name: "System.Binary.Base64Encode", Func: runtimeEncode, Price: 100000}, {Name: "System.Binary.Deserialize", Func: runtimeDeserialize, Price: 500000}, {Name: "System.Binary.Serialize", Func: runtimeSerialize, Price: 100000}, {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 2500000, diff --git a/pkg/interop/binary/binary.go b/pkg/interop/binary/binary.go index 8a847e47c..647e4d19f 100644 --- a/pkg/interop/binary/binary.go +++ b/pkg/interop/binary/binary.go @@ -16,3 +16,15 @@ func Serialize(item interface{}) []byte { func Deserialize(b []byte) interface{} { return nil } + +// Base64Encode encodes given byte slice into a base64 string and returns byte +// representation of this string. It uses `System.Binary.Base64Encode` interop. +func Base64Encode(b []byte) []byte { + return nil +} + +// Base64Decode decodes given base64 string represented as a byte slice into +// byte slice. It uses `System.Binary.Base64Decode` interop. +func Base64Decode(b []byte) []byte { + return nil +}