diff options
-rw-r--r-- | core/vm/sqlvm/common/context.go | 11 | ||||
-rw-r--r-- | core/vm/sqlvm/errors/errors.go | 14 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions.go | 63 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/jumptable.go | 3 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/opcodes.go | 135 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/runtime.go | 19 |
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 +} |