diff options
author | Meng-Ying Yang <garfield@dexon.org> | 2019-04-15 12:18:22 +0800 |
---|---|---|
committer | Jhih-Ming Huang <jm.huang@cobinhood.com> | 2019-05-06 10:44:05 +0800 |
commit | 3089040675fd0bccf2f2ef6f0a14d33a362ba27b (patch) | |
tree | 3bf6442cb84dc32240b049d65ae9bb804c5d4850 | |
parent | ba6e85977e88195d8c5dccc512d70b066265930a (diff) | |
download | dexon-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.go | 32 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/functions_test.go | 59 |
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{}{} + } + } +} |