aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/vm/sqlvm/common/context.go11
-rw-r--r--core/vm/sqlvm/errors/errors.go14
-rw-r--r--core/vm/sqlvm/runtime/instructions.go63
-rw-r--r--core/vm/sqlvm/runtime/jumptable.go3
-rw-r--r--core/vm/sqlvm/runtime/opcodes.go135
-rw-r--r--core/vm/sqlvm/runtime/runtime.go19
6 files changed, 245 insertions, 0 deletions
diff --git a/core/vm/sqlvm/common/context.go b/core/vm/sqlvm/common/context.go
new file mode 100644
index 000000000..7c4305e3e
--- /dev/null
+++ b/core/vm/sqlvm/common/context.go
@@ -0,0 +1,11 @@
+package common
+
+import "github.com/dexon-foundation/dexon/core/vm"
+
+// Context holds SQL VM required params.
+type Context struct {
+ vm.Context
+
+ StateDB vm.StateDB
+ Contract *vm.Contract
+}
diff --git a/core/vm/sqlvm/errors/errors.go b/core/vm/sqlvm/errors/errors.go
index 60dee909b..f6b91ebae 100644
--- a/core/vm/sqlvm/errors/errors.go
+++ b/core/vm/sqlvm/errors/errors.go
@@ -89,6 +89,13 @@ const (
ErrorCodeEscapeSequenceTooShort
ErrorCodeInvalidUnicodeCodePoint
ErrorCodeUnknownEscapeSequence
+ // Runtime Error
+ ErrorCodeInvalidDataType
+ ErrorCodeOverflow
+ ErrorCodeUnderflow
+ ErrorCodeIndexOutOfRange
+ ErrorCodeInvalidCastType
+ ErrorCodeDividedByZero
)
var errorCodeMap = [...]string{
@@ -101,6 +108,13 @@ var errorCodeMap = [...]string{
ErrorCodeEscapeSequenceTooShort: "escape sequence too short",
ErrorCodeInvalidUnicodeCodePoint: "invalid unicode code point",
ErrorCodeUnknownEscapeSequence: "unknown escape sequence",
+ // Runtime Error
+ ErrorCodeInvalidDataType: "invalid data type",
+ ErrorCodeOverflow: "overflow",
+ ErrorCodeUnderflow: "underflow",
+ ErrorCodeIndexOutOfRange: "index out of range",
+ ErrorCodeInvalidCastType: "invalid cast type",
+ ErrorCodeDividedByZero: "divide by zero",
}
func (c ErrorCode) Error() string {
diff --git a/core/vm/sqlvm/runtime/instructions.go b/core/vm/sqlvm/runtime/instructions.go
new file mode 100644
index 000000000..f642a2515
--- /dev/null
+++ b/core/vm/sqlvm/runtime/instructions.go
@@ -0,0 +1,63 @@
+package runtime
+
+import (
+ "fmt"
+ "math/big"
+ "strings"
+
+ "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast"
+ "github.com/dexon-foundation/dexon/core/vm/sqlvm/common"
+)
+
+var tupleJoin = "|"
+
+// OpFunction type
+// data could be fields Fields, pattern []byte, order Orders
+type OpFunction func(ctx *common.Context, ops []*Operand, registers []*Operand, output int) error
+
+// Instruction represents single instruction with essential information
+// collection.
+type Instruction struct {
+ op OpCode
+ input []*Operand
+ output int
+}
+
+// Raw with embedded big.Int value or byte slice which represents the real value
+// of basic operand unit.
+type Raw struct {
+ MajorType ast.DataTypeMajor
+ MinorType ast.DataTypeMinor
+
+ // for not bytes
+ Value *big.Int
+ Max, Min *big.Int
+ // for bytes
+ Bytes []byte
+}
+
+func (r *Raw) String() string {
+ return fmt.Sprintf(
+ "MajorType: %v, MinorType: %v, Value: %v, Bytes: %v",
+ r.MajorType, r.MinorType, r.Value, r.Bytes)
+}
+
+// Tuple is collection of Raw.
+type Tuple []*Raw
+
+func (t Tuple) String() string {
+ rawStr := []string{}
+ for i := 0; i < len(t); i++ {
+ rawStr = append(rawStr, t[i].String())
+ }
+ return strings.Join(rawStr, tupleJoin)
+}
+
+// Operand would be array-based value associated with meta to describe type of
+// array element.
+type Operand struct {
+ IsImmediate bool
+ Meta []ast.DataType
+ Data []Tuple
+ RegisterIndex *int
+}
diff --git a/core/vm/sqlvm/runtime/jumptable.go b/core/vm/sqlvm/runtime/jumptable.go
new file mode 100644
index 000000000..13ffef361
--- /dev/null
+++ b/core/vm/sqlvm/runtime/jumptable.go
@@ -0,0 +1,3 @@
+package runtime
+
+var jumpTable = map[OpCode]OpFunction{}
diff --git a/core/vm/sqlvm/runtime/opcodes.go b/core/vm/sqlvm/runtime/opcodes.go
new file mode 100644
index 000000000..22628b28e
--- /dev/null
+++ b/core/vm/sqlvm/runtime/opcodes.go
@@ -0,0 +1,135 @@
+package runtime
+
+// OpCode type
+type OpCode byte
+
+// 0x10 range - arithmetic ops. (array-based)
+const (
+ ADD OpCode = iota + 0x10
+ // ADD(t1, t2) res
+ // res = t1 + t2 = [1, 2] + [2, 3] = [3, 5]
+ MUL
+ // MUL(t1, t2) res
+ // res = t1 * t2 = [1, 2] * [2, 3] = [2, 6]
+ SUB
+ // SUB(t1, t2) res
+ // res = t1 - t2 = [1, 2] - [2, 3] = [-1, -1]
+ DIV
+ // DIV(t1, t2) res
+ // res = t1 / t2 = [1, 2] / [2, 3] = [0, 0]
+ MOD
+ // MOD(t1, t2) res
+ // res = t1 % t2 = [1, 2] % [2, 3] = [1, 2]
+
+)
+
+// 0x20 range - comparison ops.
+const (
+ LT OpCode = iota + 0x20
+ // LT(t1, t2) res
+ // res = t1 < t2 = [1, 2] < [2, 3] = [true, true]
+ GT
+ // GT(t1, t2) res
+ // res = t1 > t2 = [1, 2] > [2, 3] = [false, false]
+ EQ
+ // EQ(t1, t2) res
+ // res = t1 == t2 = [1, 2] == [2, 3] = [false, false]
+ AND
+ // AND(t1, t2) res
+ // res = t1 && t2 = [true, true] && [true, false] = [true, false]
+ OR
+ // OR(t1, t2) res
+ // res = t1 || t2 = [false, false] || [true, false] = [true, false]
+ NOT
+ // NOT(t1) res
+ // res = !t1 = ![false, true] = [true, false]
+ UNION
+ // UNION(t1, t2) res
+ // res = t1 ∪ t2 = [1, 2] ∪ [2, 3] = [1, 2, 3]
+ INTXN
+ // INTXN(t1, t2) res
+ // res = t1 ∩ t2 = [1, 2] ∩ [2, 3] = [2]
+ LIKE
+ // LIKE(t1, pattern) res
+ // res = t1 like '%abc%' =
+ // ['_abc_', '123'] like '%abc%' = [true, false]
+)
+
+// 0x30 range - pk/index/field meta ops
+const (
+ REPEATPK OpCode = iota + 0x30
+ // REPEATPK(pk) res res = [id1, id2, id3, ...]
+ // REPEATPK([tables, table_id, primary]) = [1, 2, 3, 5, 6, 7, ...]
+ REPEATIDX
+ // Scan given index value(s)
+ // REPEATIDX(base, idxv) res res = [id2, id4, id5, id6]
+ // REPEATIDX(
+ // [tables, table_id, indices, name_idx],
+ // [val1, val3]
+ // ) = [5, 6, 7, 10, 11, ... ]
+ REPEATIDXV
+ // Get possible values from index value meta
+ // REPEATIDXV(idx) res res = [val1, val2, val3, ...]
+ // REPEATIDXV(
+ // [tables, table_id, indices, name_idx]
+ // ) = ["alice", "bob", "foo", "bar", ... ]
+)
+
+// 0x40 range - format/output ops
+const (
+ ZIP OpCode = iota + 0x40
+ // ZIP(tgr, new) = res
+ // ZIP([f1, f2, f3], [c1, c2, c3]) = [(f1, c1), (f2, c2), (f3, c3)]
+ // ZIP(
+ // [(f1, c1), (f2, c2), (f3, c3)],
+ // [(x1, (y1)), (x2, (y2)), (x3, (y3))]
+ // ) = [(f1, c1, x1, (y1)), (f2, c2, x2, (y2)), (f3, c3, x2, (y2))]
+ FIELD
+ // (src, fields) = res
+ // (
+ // [(r1f1, r1f2, r1f3), (r2f1, r2f2, r2f3),...], [2,3]
+ // ) = [(r1f2, r1f3), (r2f2, r2f3), ...]
+ SORT
+ // SORT(src, [(field, order, null order)] ) = res
+ // SORT(
+ // [(a1, a2, a3), (b1, a2, null), (a1, b2, null), ...],
+ // [(1, asc, null_first), (2, desc, null_last), (3, asc, null_last)]
+ // ) = [(a1, a2, a3), (a1, b2, null), (b1, a2, null), ...]
+ FILTER
+ // FILTER(src, cond) = res
+ // FILTER([1, 2, 3, 4, 5], [true, false, true, false, false]) = [1, 3]
+ CAST
+ // CAST(t1, types) t2
+)
+
+// 0x60 range - storage ops
+const (
+ STOREPK OpCode = iota + 0x60
+ // STOREPK(pk, [uint256, uint256, ...])
+ LOADPK
+ // LOADPK(pk) array array = [uint256, uint256, ...]
+ STORE
+ // STORE(base, ids, values, fields, idxes)
+ // STORE(
+ // [tables, table_id, primary],
+ // [],
+ // [field1, field2, [field3.1, field3.2]],
+ // []
+ // )
+ // STORE(
+ // [tables, table_id, primary],
+ // [1, 2],
+ // [updated_field1, updated_field2],
+ // [1, 2]
+ // )
+ LOAD
+ // LOAD(base, ids, fields) array
+ // LOAD(
+ // [tables, table_id, primary], [1], []
+ // ) = [(field1, field2, [field3.1, field3.2])]
+ // LOAD(
+ // [tables, table_id, primary, [1], [1,3]
+ // ) =[(field1, [field3.1, field3.2])]
+ DELETE
+ // DELETE(base, ids, idxes)
+)
diff --git a/core/vm/sqlvm/runtime/runtime.go b/core/vm/sqlvm/runtime/runtime.go
new file mode 100644
index 000000000..ebb4d4579
--- /dev/null
+++ b/core/vm/sqlvm/runtime/runtime.go
@@ -0,0 +1,19 @@
+package runtime
+
+import (
+ "github.com/dexon-foundation/dexon/core/vm"
+ "github.com/dexon-foundation/dexon/core/vm/sqlvm/common"
+)
+
+// Run is runtime entrypoint.
+func Run(stateDB vm.StateDB, ins []Instruction, registers []*Operand) (ret []byte, err error) {
+ for _, in := range ins {
+ opFunc := jumpTable[in.op]
+ err = opFunc(&common.Context{}, in.input, registers, in.output)
+ if err != nil {
+ return nil, err
+ }
+ }
+ // TODO: ret = ABIEncode(ins[len(ins)-1].output)
+ return
+}