aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorMeng-Ying Yang <garfield@dexon.org>2019-04-15 11:33:15 +0800
committerJhih-Ming Huang <jm.huang@cobinhood.com>2019-05-06 10:44:05 +0800
commitbb29b970cf211535cf76eee0217dc8de7dcf88bc (patch)
treeae95f2c1ccc8173bd2be9a039c69610eb6c5c72f /core
parent8ec4ef32d026f1b6b043b3268eccf68293266014 (diff)
downloaddexon-bb29b970cf211535cf76eee0217dc8de7dcf88bc.tar
dexon-bb29b970cf211535cf76eee0217dc8de7dcf88bc.tar.gz
dexon-bb29b970cf211535cf76eee0217dc8de7dcf88bc.tar.bz2
dexon-bb29b970cf211535cf76eee0217dc8de7dcf88bc.tar.lz
dexon-bb29b970cf211535cf76eee0217dc8de7dcf88bc.tar.xz
dexon-bb29b970cf211535cf76eee0217dc8de7dcf88bc.tar.zst
dexon-bb29b970cf211535cf76eee0217dc8de7dcf88bc.zip
core: vm: sqlvm: add built-in function BLOCK_HASH()
Diffstat (limited to 'core')
-rw-r--r--core/vm/sqlvm/common/decimal/decimal.go2
-rw-r--r--core/vm/sqlvm/runtime/functions.go67
-rw-r--r--core/vm/sqlvm/runtime/functions_test.go94
-rw-r--r--core/vm/sqlvm/runtime/instructions.go11
4 files changed, 173 insertions, 1 deletions
diff --git a/core/vm/sqlvm/common/decimal/decimal.go b/core/vm/sqlvm/common/decimal/decimal.go
index 390f440e3..ff45ec327 100644
--- a/core/vm/sqlvm/common/decimal/decimal.go
+++ b/core/vm/sqlvm/common/decimal/decimal.go
@@ -17,6 +17,8 @@ var (
MaxUint16 = decimal.New(math.MaxUint16, 0)
MaxUint64 = decimal.RequireFromString(fmt.Sprint(uint64(math.MaxUint64)))
+
+ Dec257 = decimal.New(257, 0)
)
// Val2Bool convert value to boolean definition.
diff --git a/core/vm/sqlvm/runtime/functions.go b/core/vm/sqlvm/runtime/functions.go
index 798f5a573..74fa50fc1 100644
--- a/core/vm/sqlvm/runtime/functions.go
+++ b/core/vm/sqlvm/runtime/functions.go
@@ -1,11 +1,76 @@
package runtime
import (
+ "github.com/dexon-foundation/decimal"
+
+ "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast"
"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"
+)
+
+// function identifier
+const (
+ BLOCKHASH = "BLOCK_HASH"
)
type fn func(*common.Context, []*Operand, uint64) (*Operand, error)
var (
- fnTable = map[string]fn{}
+ fnTable = map[string]fn{
+ BLOCKHASH: fnBlockHash,
+ }
)
+
+func assignFuncResult(meta []ast.DataType, fn func() *Raw, length uint64) (result *Operand) {
+ result = &Operand{Meta: meta, Data: make([]Tuple, length)}
+ for i := uint64(0); i < length; i++ {
+ result.Data[i] = Tuple{fn()}
+ }
+ return
+}
+
+func evalBlockHash(ctx *common.Context, num, cur decimal.Decimal) (r *Raw, err error) {
+ r = &Raw{Bytes: make([]byte, 32)}
+
+ cNum := cur.Sub(dec.Dec257)
+ if num.Cmp(cNum) > 0 && num.Cmp(cur) < 0 {
+ var num64 uint64
+ num64, err = ast.DecimalToUint64(num)
+ if err != nil {
+ return
+ }
+ r.Bytes = ctx.GetHash(num64).Bytes()
+ }
+ return
+}
+
+func fnBlockHash(ctx *common.Context, ops []*Operand, length uint64) (result *Operand, err error) {
+ if len(ops) != 1 {
+ err = se.ErrorCodeInvalidOperandNum
+ return
+ }
+
+ meta := []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorFixedBytes, 3)}
+ cNum := decimal.NewFromBigInt(ctx.BlockNumber, 0)
+
+ if ops[0].IsImmediate {
+ var r *Raw
+ r, err = evalBlockHash(ctx, ops[0].Data[0][0].Value, cNum)
+ if err != nil {
+ return
+ }
+ result = assignFuncResult(meta, r.clone, length)
+ } else {
+ result = &Operand{Meta: meta, Data: make([]Tuple, length)}
+ for i := uint64(0); i < length; i++ {
+ var r *Raw
+ r, err = evalBlockHash(ctx, ops[0].Data[i][0].Value, cNum)
+ if err != nil {
+ return
+ }
+ result.Data[i] = Tuple{r}
+ }
+ }
+ return
+}
diff --git a/core/vm/sqlvm/runtime/functions_test.go b/core/vm/sqlvm/runtime/functions_test.go
new file mode 100644
index 000000000..fe8fa0791
--- /dev/null
+++ b/core/vm/sqlvm/runtime/functions_test.go
@@ -0,0 +1,94 @@
+package runtime
+
+import (
+ "math/big"
+ "testing"
+
+ "github.com/dexon-foundation/decimal"
+ "github.com/stretchr/testify/suite"
+
+ dexCommon "github.com/dexon-foundation/dexon/common"
+ "github.com/dexon-foundation/dexon/core/vm"
+ "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast"
+ "github.com/dexon-foundation/dexon/core/vm/sqlvm/common"
+)
+
+func TestFunction(t *testing.T) {
+ suite.Run(t, new(FunctionSuite))
+}
+
+type FunctionSuite struct {
+ suite.Suite
+}
+
+var (
+ hash1 = dexCommon.BigToHash(big.NewInt(1))
+ hash255 = dexCommon.BigToHash(big.NewInt(255))
+)
+
+var mockNumberHashTable = map[uint64]dexCommon.Hash{1: hash1, 255: hash255}
+
+func mockGetHashFunc(u uint64) dexCommon.Hash { return mockNumberHashTable[u] }
+
+func (s *FunctionSuite) TestFnBlockHash() {
+ type blockHashCase struct {
+ Name string
+ Ops []*Operand
+ Length uint64
+ Res [][]byte
+ Cur *big.Int
+ Err error
+ }
+
+ testcases := []blockHashCase{
+ {"Immediate OP", []*Operand{
+ {IsImmediate: true, Meta: nil, Data: []Tuple{{&Raw{Value: decimal.New(1, 0)}}}},
+ }, 2, [][]byte{hash1.Bytes(), hash1.Bytes()}, big.NewInt(255), nil},
+ {"OP", []*Operand{
+ {IsImmediate: false, Meta: nil, Data: []Tuple{
+ {&Raw{Value: decimal.New(255, 0)}},
+ {&Raw{Value: decimal.New(515, 0)}},
+ }},
+ }, 2, [][]byte{hash255.Bytes(), make([]byte, 32)}, big.NewInt(256), nil},
+ {"Older than 257 block", []*Operand{
+ {IsImmediate: false, Meta: nil, Data: []Tuple{
+ {&Raw{Value: decimal.New(1, 0)}},
+ }},
+ }, 1, [][]byte{make([]byte, 32)}, big.NewInt(512), nil},
+ }
+
+ callFn := func(c blockHashCase) (*Operand, error) {
+ return fnBlockHash(
+ &common.Context{
+ Context: vm.Context{
+ GetHash: mockGetHashFunc,
+ BlockNumber: c.Cur,
+ },
+ },
+ c.Ops,
+ c.Length,
+ )
+ }
+
+ meta := []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorFixedBytes, 3)}
+
+ 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)
+
+ for i := 0; i < len(r.Data); i++ {
+ s.Require().Equal(
+ tCase.Res[i], r.Data[i][0].Bytes,
+ "TestCase Index: %v. Data Index: %v. Value not equal: %v != %v",
+ idx, i, tCase.Res[i], r.Data[i][0].Bytes)
+ }
+ }
+}
diff --git a/core/vm/sqlvm/runtime/instructions.go b/core/vm/sqlvm/runtime/instructions.go
index cd034955a..013c700f8 100644
--- a/core/vm/sqlvm/runtime/instructions.go
+++ b/core/vm/sqlvm/runtime/instructions.go
@@ -56,6 +56,17 @@ func (r *Raw) String() string {
func (r *Raw) isTrue() bool { return dec.IsTrue(r.Value) }
func (r *Raw) isFalse() bool { return dec.IsFalse(r.Value) }
+func (r *Raw) clone() *Raw {
+ r2 := &Raw{}
+ if len(r.Bytes) != 0 {
+ r2.Bytes = make([]byte, len(r.Bytes))
+ copy(r2.Bytes, r.Bytes)
+ } else {
+ r2.Value = r.Value
+ }
+ return r2
+}
+
// Tuple is collection of Raw.
type Tuple []*Raw