aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMeng-Ying Yang <garfield@dexon.org>2019-04-15 12:18:22 +0800
committerJhih-Ming Huang <jm.huang@cobinhood.com>2019-05-06 10:44:05 +0800
commit3089040675fd0bccf2f2ef6f0a14d33a362ba27b (patch)
tree3bf6442cb84dc32240b049d65ae9bb804c5d4850
parentba6e85977e88195d8c5dccc512d70b066265930a (diff)
downloaddexon-3089040675fd0bccf2f2ef6f0a14d33a362ba27b.tar.gz
dexon-3089040675fd0bccf2f2ef6f0a14d33a362ba27b.tar.zst
dexon-3089040675fd0bccf2f2ef6f0a14d33a362ba27b.zip
core: vm: sqlvm: add built-in function RAND()
-rw-r--r--core/vm/sqlvm/runtime/functions.go32
-rw-r--r--core/vm/sqlvm/runtime/functions_test.go59
2 files changed, 91 insertions, 0 deletions
diff --git a/core/vm/sqlvm/runtime/functions.go b/core/vm/sqlvm/runtime/functions.go
index d72ee5bd8..d9c0f94f4 100644
--- a/core/vm/sqlvm/runtime/functions.go
+++ b/core/vm/sqlvm/runtime/functions.go
@@ -1,6 +1,7 @@
package runtime
import (
+ "encoding/binary"
"fmt"
"math"
@@ -10,6 +11,7 @@ import (
"github.com/dexon-foundation/dexon/core/vm/sqlvm/common"
dec "github.com/dexon-foundation/dexon/core/vm/sqlvm/common/decimal"
se "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors"
+ "github.com/dexon-foundation/dexon/crypto"
)
// function identifier
@@ -23,6 +25,7 @@ const (
MSGDATA = "MSG_DATA"
TXORIGIN = "TX_ORIGIN"
NOW = "NOW"
+ RAND = "RAND"
)
type fn func(*common.Context, []*Operand, uint64) (*Operand, error)
@@ -38,6 +41,7 @@ var (
MSGSENDER: fnMsgSender,
MSGDATA: fnMsgData,
TXORIGIN: fnTxOrigin,
+ RAND: fnRand,
}
)
@@ -165,3 +169,31 @@ func fnTxOrigin(ctx *common.Context, ops []*Operand, length uint64) (result *Ope
return
}
+func fnRand(ctx *common.Context, ops []*Operand, length uint64) (result *Operand, err error) {
+ binaryOriginNonce := make([]byte, binary.MaxVarintLen64)
+ binary.PutUvarint(binaryOriginNonce, ctx.Storage.GetNonce(ctx.Origin))
+
+ binaryUsedIndex := make([]byte, binary.MaxVarintLen64)
+ vType := ast.ComposeDataType(ast.DataTypeMajorUint, 31)
+
+ fn := func() *Raw {
+ binary.PutUvarint(binaryUsedIndex, ctx.RandCallIndex)
+ ctx.RandCallIndex++
+
+ hash := crypto.Keccak256(
+ ctx.Randomness,
+ ctx.Origin.Bytes(),
+ binaryOriginNonce,
+ binaryUsedIndex)
+
+ v, _ := ast.DecimalDecode(vType, hash)
+ return &Raw{Value: v}
+ }
+
+ result = assignFuncResult(
+ []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorUint, 31)},
+ fn, length,
+ )
+ return
+
+}
diff --git a/core/vm/sqlvm/runtime/functions_test.go b/core/vm/sqlvm/runtime/functions_test.go
index 54fa75816..828c43efa 100644
--- a/core/vm/sqlvm/runtime/functions_test.go
+++ b/core/vm/sqlvm/runtime/functions_test.go
@@ -463,3 +463,62 @@ func (s *FunctionSuite) TestFnTxOrigin() {
}
}
+func (s *FunctionSuite) TestFnRand() {
+ type blockRandCase struct {
+ Name string
+ Origin dexCommon.Address
+ Length uint64
+ Err error
+ }
+
+ res := []byte{
+ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
+ 0x01, 0x23, 0x45, 0x67}
+ address := dexCommon.BytesToAddress(res)
+
+ testcases := []blockRandCase{
+ {"address with length 100", address, 100, nil},
+ {"address with length 0", address, 0, nil},
+ }
+
+ callFn := func(c blockRandCase) (*Operand, error) {
+ return fnRand(
+ &common.Context{
+ Context: vm.Context{Origin: c.Origin, Randomness: res},
+ Storage: newStorage(),
+ },
+ nil,
+ c.Length)
+ }
+
+ meta := []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorUint, 31)}
+
+ for idx, tCase := range testcases {
+ r, err := callFn(tCase)
+ s.Require().Equal(
+ tCase.Err, err,
+ "Index: %v. Error not expected: %v != %v", idx, tCase.Err, err)
+ s.Require().Equal(
+ meta, r.Meta,
+ "Index: %v. Meta not equal: %v != %v", idx, meta, r.Meta)
+ s.Require().Equal(
+ uint64(len(r.Data)), tCase.Length,
+ "Index: %v. Length not equal: %v != %v", idx, len(r.Data), tCase.Length)
+
+ var (
+ fmap = make(map[string]struct{})
+ ok bool
+ key string
+ )
+
+ for i := 0; i < len(r.Data); i++ {
+ key = r.Data[i].String()
+ _, ok = fmap[key]
+ s.Require().False(
+ ok,
+ "Duplicate rand: %v\nmap: %v\ndata: %v", key, fmap, r.Data)
+ fmap[key] = struct{}{}
+ }
+ }
+}