native/std: add memoryCompare method

This commit is contained in:
Evgeniy Stratonikov 2021-04-30 15:02:44 +03:00
parent 82a6c3266c
commit 978f4dfbc5
4 changed files with 57 additions and 0 deletions

View file

@ -228,6 +228,7 @@ func TestNativeHelpersCompile(t *testing.T) {
{"itoa10", []string{"4"}}, {"itoa10", []string{"4"}},
{"atoi", []string{`"4"`, "10"}}, {"atoi", []string{`"4"`, "10"}},
{"atoi10", []string{`"4"`}}, {"atoi10", []string{`"4"`}},
{"memoryCompare", []string{"[]byte{1}", "[]byte{2}"}},
}) })
} }

View file

@ -1,6 +1,7 @@
package native package native
import ( import (
"bytes"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"errors" "errors"
@ -104,6 +105,12 @@ func newStd() *Std {
md = newMethodAndPrice(s.base58Decode, 1<<10, callflag.NoneFlag) md = newMethodAndPrice(s.base58Decode, 1<<10, callflag.NoneFlag)
s.AddMethod(md, desc) s.AddMethod(md, desc)
desc = newDescriptor("memoryCompare", smartcontract.IntegerType,
manifest.NewParameter("str1", smartcontract.ByteArrayType),
manifest.NewParameter("str2", smartcontract.ByteArrayType))
md = newMethodAndPrice(s.memoryCompare, 1<<5, callflag.NoneFlag)
s.AddMethod(md, desc)
return s return s
} }
@ -278,6 +285,12 @@ func (s *Std) base58Decode(_ *interop.Context, args []stackitem.Item) stackitem.
return stackitem.NewByteArray(result) return stackitem.NewByteArray(result)
} }
func (s *Std) memoryCompare(_ *interop.Context, args []stackitem.Item) stackitem.Item {
s1 := s.toLimitedBytes(args[0])
s2 := s.toLimitedBytes(args[1])
return stackitem.NewBigInteger(big.NewInt(int64(bytes.Compare(s1, s2))))
}
// Metadata implements Contract interface. // Metadata implements Contract interface.
func (s *Std) Metadata() *interop.ContractMD { func (s *Std) Metadata() *interop.ContractMD {
return &s.ContractMD return &s.ContractMD

View file

@ -354,3 +354,38 @@ func TestStdLibSerializeDeserialize(t *testing.T) {
}) })
}) })
} }
func TestMemoryCompare(t *testing.T) {
s := newStd()
ic := &interop.Context{VM: vm.New()}
check := func(t *testing.T, result int64, s1, s2 string) {
actual := s.memoryCompare(ic, []stackitem.Item{stackitem.Make(s1), stackitem.Make(s2)})
require.Equal(t, big.NewInt(result), actual.Value())
}
check(t, -1, "a", "ab")
check(t, 1, "ab", "a")
check(t, 0, "ab", "ab")
check(t, -1, "", "a")
check(t, 0, "", "")
t.Run("C# compatibility", func(t *testing.T) {
// These tests are taken from C# node.
check(t, -1, "abc", "c")
check(t, -1, "abc", "d")
check(t, 0, "abc", "abc")
check(t, -1, "abc", "abcd")
})
t.Run("big arguments", func(t *testing.T) {
s1 := stackitem.Make(strings.Repeat("x", stdMaxInputLength+1))
s2 := stackitem.Make("xxx")
require.PanicsWithError(t, ErrTooBigInput.Error(),
func() { s.memoryCompare(ic, []stackitem.Item{s1, s2}) })
require.PanicsWithError(t, ErrTooBigInput.Error(),
func() { s.memoryCompare(ic, []stackitem.Item{s2, s1}) })
})
}

View file

@ -113,3 +113,11 @@ func Atoi10(s string) int {
return contract.Call(interop.Hash160(Hash), "atoi", contract.NoneFlag, return contract.Call(interop.Hash160(Hash), "atoi", contract.NoneFlag,
s).(int) s).(int)
} }
// MemoryCompare is similar to bytes.Compare:
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
// It uses `memoryCompare` method of StdLib native contract.
func MemoryCompare(s1, s2 []byte) int {
return contract.Call(interop.Hash160(Hash), "memoryCompare", contract.NoneFlag,
s1, s2).(int)
}