diff options
author | Meng-Ying Yang <garfield@dexon.org> | 2019-02-23 10:51:51 +0800 |
---|---|---|
committer | Jhih-Ming Huang <jm.huang@cobinhood.com> | 2019-05-06 10:44:04 +0800 |
commit | 8115f8c82f95e6581361fcd7e2d838406517ea4f (patch) | |
tree | 2efe439e555bf75761b084f7f5fe3634e3bbf260 | |
parent | 4c3bc281c9d8409e2ae030be63e82d2c27aa73aa (diff) | |
download | dexon-8115f8c82f95e6581361fcd7e2d838406517ea4f.tar.gz dexon-8115f8c82f95e6581361fcd7e2d838406517ea4f.tar.zst dexon-8115f8c82f95e6581361fcd7e2d838406517ea4f.zip |
core: vm: sqlvm: opcodes and basic structs
For runtime implementation, we define opcodes and basic structs for
runtime usage and concrete implementation.
-rw-r--r-- | core/vm/sqlvm/common/context.go | 7 | ||||
-rw-r--r-- | core/vm/sqlvm/common/decimal/decimal.go | 23 | ||||
-rw-r--r-- | core/vm/sqlvm/errors/errors.go | 8 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions.go | 1530 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/opcodes.go | 6 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/runtime.go | 13 |
6 files changed, 1570 insertions, 17 deletions
diff --git a/core/vm/sqlvm/common/context.go b/core/vm/sqlvm/common/context.go index 1985473fa..56ad89ded 100644 --- a/core/vm/sqlvm/common/context.go +++ b/core/vm/sqlvm/common/context.go @@ -2,10 +2,17 @@ package common import "github.com/dexon-foundation/dexon/core/vm" +// Option is collection of SQL options. +type Option struct { + SafeMath bool +} + // Context holds SQL VM required params. type Context struct { vm.Context Storage Storage Contract *vm.Contract + + Opt Option } diff --git a/core/vm/sqlvm/common/decimal/decimal.go b/core/vm/sqlvm/common/decimal/decimal.go index 445256cf5..96212ed27 100644 --- a/core/vm/sqlvm/common/decimal/decimal.go +++ b/core/vm/sqlvm/common/decimal/decimal.go @@ -6,4 +6,27 @@ import "github.com/dexon-foundation/decimal" var ( False = decimal.New(0, 0) True = decimal.New(1, 0) + + Int64Max = decimal.New(1, 63).Sub(decimal.One) + Int64Min = decimal.New(1, 63).Neg() + + UInt16Max = decimal.New(1, 16).Sub(decimal.One) ) + +// Val2Bool convert value to boolean definition. +func Val2Bool(v decimal.Decimal) decimal.Decimal { + if v.IsZero() { + return False + } + return True +} + +// IsTrue returns given value is decimal defined True in golang boolean. +func IsTrue(v decimal.Decimal) bool { + return v.Cmp(True) == 0 +} + +// IsFalse returns given value is decimal defined False in golang boolean. +func IsFalse(v decimal.Decimal) bool { + return v.Cmp(False) == 0 +} diff --git a/core/vm/sqlvm/errors/errors.go b/core/vm/sqlvm/errors/errors.go index 6f2b8ad93..b6c3909af 100644 --- a/core/vm/sqlvm/errors/errors.go +++ b/core/vm/sqlvm/errors/errors.go @@ -112,6 +112,7 @@ const ( ErrorCodeDecimalDecode // Runtime Error + ErrorCodeInvalidOperandNum ErrorCodeInvalidDataType ErrorCodeOverflow ErrorCodeUnderflow @@ -119,6 +120,9 @@ const ( ErrorCodeInvalidCastType ErrorCodeDividedByZero ErrorCodeNegDecimalToUint64 + ErrorCodeDataLengthNotMatch + ErrorCodeMultipleEscapeByte + ErrorCodePendingEscapeByte ) var errorCodeMap = [...]string{ @@ -142,6 +146,7 @@ var errorCodeMap = [...]string{ ErrorCodeDecimalEncode: "decimal encode failed", ErrorCodeDecimalDecode: "decimal decode failed", // Runtime Error + ErrorCodeInvalidOperandNum: "invalid operand number", ErrorCodeInvalidDataType: "invalid data type", ErrorCodeOverflow: "overflow", ErrorCodeUnderflow: "underflow", @@ -149,6 +154,9 @@ var errorCodeMap = [...]string{ ErrorCodeInvalidCastType: "invalid cast type", ErrorCodeDividedByZero: "divide by zero", ErrorCodeNegDecimalToUint64: "negative deciaml to uint64", + ErrorCodeDataLengthNotMatch: "data length not match", + ErrorCodeMultipleEscapeByte: "multiple escape byte", + ErrorCodePendingEscapeByte: "pending escape byte", } func (c ErrorCode) Error() string { diff --git a/core/vm/sqlvm/runtime/instructions.go b/core/vm/sqlvm/runtime/instructions.go index d1bfa8a17..4b179f4aa 100644 --- a/core/vm/sqlvm/runtime/instructions.go +++ b/core/vm/sqlvm/runtime/instructions.go @@ -1,7 +1,11 @@ package runtime import ( + "bytes" + "errors" "fmt" + "regexp" + "sort" "strings" "github.com/dexon-foundation/decimal" @@ -9,21 +13,31 @@ import ( dexCommon "github.com/dexon-foundation/dexon/common" "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast" "github.com/dexon-foundation/dexon/core/vm/sqlvm/common" - "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" + dec "github.com/dexon-foundation/dexon/core/vm/sqlvm/common/decimal" + se "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" ) -var tupleJoin = "|" +var ( + byteLikeP = byte('%') + byteLikeU = byte('_') + byteDot = byte('.') + bytesLikeReg = []byte{'.', '*', '?'} + bytesStart = []byte{'^'} + bytesEnd = []byte{'$'} + + 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 +type OpFunction func(ctx *common.Context, ops, registers []*Operand, output uint) error // Instruction represents single instruction with essential information // collection. type Instruction struct { Op OpCode Input []*Operand - Output int + Output uint Position uint32 // ast tree position } @@ -38,6 +52,9 @@ func (r *Raw) String() string { return fmt.Sprintf("Value: %v, Bytes: %v", r.Value, r.Bytes) } +func (r *Raw) isTrue() bool { return dec.IsTrue(r.Value) } +func (r *Raw) isFalse() bool { return dec.IsFalse(r.Value) } + // Tuple is collection of Raw. type Tuple []*Raw @@ -46,7 +63,7 @@ func (t Tuple) String() string { for i := 0; i < len(t); i++ { rawStr = append(rawStr, t[i].String()) } - return strings.Join(rawStr, tupleJoin) + return fmt.Sprintf("\n%v\n", strings.Join(rawStr, tupleJoin)) } // Operand would be array-based value associated with meta to describe type of @@ -58,9 +75,9 @@ type Operand struct { RegisterIndex uint } -func (o *Operand) toUint64() (result []uint64, err error) { - result = make([]uint64, len(o.Data)) - for i, tuple := range o.Data { +func (op *Operand) toUint64() (result []uint64, err error) { + result = make([]uint64, len(op.Data)) + for i, tuple := range op.Data { result[i], err = ast.DecimalToUint64(tuple[0].Value) if err != nil { return @@ -69,9 +86,9 @@ func (o *Operand) toUint64() (result []uint64, err error) { return } -func (o *Operand) toUint8() ([]uint8, error) { - result := make([]uint8, len(o.Data)) - for i, tuple := range o.Data { +func (op *Operand) toUint8() ([]uint8, error) { + result := make([]uint8, len(op.Data)) + for i, tuple := range op.Data { u, err := ast.DecimalToUint64(tuple[0].Value) if err != nil { return nil, err @@ -81,10 +98,10 @@ func (o *Operand) toUint8() ([]uint8, error) { return result, nil } -func opLoad(ctx *common.Context, input []*Operand, registers []*Operand, output int) error { +func opLoad(ctx *common.Context, input []*Operand, registers []*Operand, output uint) error { tableIdx := input[0].Data[0][0].Value.IntPart() if tableIdx >= int64(len(ctx.Storage.Schema)) { - return errors.ErrorCodeIndexOutOfRange + return se.ErrorCodeIndexOutOfRange } table := ctx.Storage.Schema[tableIdx] @@ -164,3 +181,1490 @@ func decode(ctx *common.Context, dt ast.DataType, slot dexCommon.Hash, bytes []b } return rVal, nil } + +func (op *Operand) clone(metaOnly bool) (op2 *Operand) { + op2 = &Operand{ + Meta: op.cloneMeta(), + // skip RegisterIndex since is only used when loading + // skip IsImmediate flag since is only set by codegen + } + + if metaOnly { + return + } + + op2.Data = make([]Tuple, len(op.Data)) + for i := 0; i < len(op.Data); i++ { + op2.Data[i] = append(Tuple{}, op.Data[i]...) + } + return +} + +func (op *Operand) cloneMeta() (meta []ast.DataType) { + meta = make([]ast.DataType, len(op.Meta)) + copy(meta, op.Meta) + return +} + +// Equal compares underlying data level-by-level. +func (op *Operand) Equal(op2 *Operand) (equal bool) { + if op2 == nil { + return + } + + equal = op.IsImmediate == op2.IsImmediate + if !equal { + return + } + + equal = metaAllEq(op, op2) + if !equal { + return + } + + equal = len(op.Data) == len(op2.Data) + if !equal { + return + } + + for i := 0; i < len(op.Data); i++ { + equal = op.Data[i].Equal(op2.Data[i], op.Meta) + if !equal { + return + } + } + return +} + +// Equal compares tuple values one by one. +func (t Tuple) Equal(t2 Tuple, meta []ast.DataType) (equal bool) { + equal = len(t) == len(t2) + if !equal { + return + } + + for i := 0; i < len(t); i++ { + equal = t[i].Equal(t2[i], meta[i]) + if !equal { + return + } + } + return +} + +// Equal compares raw by type. +func (r *Raw) Equal(r2 *Raw, dType ast.DataType) (equal bool) { + major, _ := ast.DecomposeDataType(dType) + switch major { + case ast.DataTypeMajorDynamicBytes, + ast.DataTypeMajorFixedBytes, + ast.DataTypeMajorAddress: + equal = bytes.Equal(r.Bytes, r2.Bytes) + default: + equal = r.Value.Cmp(r2.Value) == 0 + } + return +} + +var ( + rawFalse = &Raw{Value: dec.False} + rawTrue = &Raw{Value: dec.True} +) + +func metaAllEq(op1, op2 *Operand) bool { + if len(op1.Meta) != len(op2.Meta) { + return false + } + + for i := 0; i < len(op1.Meta); i++ { + if op1.Meta[i] != op2.Meta[i] { + return false + } + } + return true +} + +func metaAll(op *Operand, fn func(ast.DataType) bool) bool { + for i := 0; i < len(op.Meta); i++ { + if !fn(op.Meta[i]) { + return false + } + } + return true +} + +func metaBool(dType ast.DataType) bool { + dMajor, _ := ast.DecomposeDataType(dType) + return dMajor == ast.DataTypeMajorBool +} + +func metaAllBool(op *Operand) bool { return metaAll(op, metaBool) } + +func metaArith(dType ast.DataType) bool { + major, _ := ast.DecomposeDataType(dType) + if major == ast.DataTypeMajorInt || + major == ast.DataTypeMajorUint { + return true + } + return false +} + +func metaAllArith(op *Operand) bool { return metaAll(op, metaArith) } + +func findMaxDataLength(ops []*Operand) (l int, err error) { + l = -1 + + for i := 0; i < len(ops); i++ { + if ops[i].IsImmediate { + continue + } + + if l == -1 { + l = len(ops[i].Data) + continue + } + + if len(ops[i].Data) != l { + err = se.ErrorCodeDataLengthNotMatch + l = -1 + return + } + } + + if l == -1 { + l = 1 + } + return +} + +func bool2Raw(b bool) (r *Raw) { + if b { + r = rawTrue + } else { + r = rawFalse + } + return +} + +func value2ColIdx(v decimal.Decimal) (idx uint16) { + if v.GreaterThan(dec.UInt16Max) { + panic(errors.New("field index greater than uint16 max")) + } else if v.LessThan(decimal.Zero) { + panic(errors.New("field index less than 0")) + } + + idx = uint16(v.IntPart()) + return +} + +func flowCheck(ctx *common.Context, v decimal.Decimal, dType ast.DataType) (err error) { + if !ctx.Opt.SafeMath { + return + } + + min, max, ok := dType.GetMinMax() + if !ok { + err = se.ErrorCodeInvalidDataType + return + } + + if v.Cmp(max) > 0 { + err = se.ErrorCodeOverflow + } else if v.Cmp(min) < 0 { + err = se.ErrorCodeUnderflow + } + return +} + +func opAdd(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + + raw, iErr := op1.Data[j].add(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) add(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw := t[i].add(t2[i]) + err = flowCheck(ctx, raw.Value, meta[i]) + if err != nil { + return + } + t3[i] = raw + } + return +} + +func (r *Raw) add(r2 *Raw) (r3 *Raw) { + r3 = &Raw{Value: r.Value.Add(r2.Value)} + return +} + +func opMul(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + + raw, iErr := op1.Data[j].mul(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) mul(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw := t[i].mul(t2[i]) + err = flowCheck(ctx, raw.Value, meta[i]) + if err != nil { + return + } + + t3[i] = raw + } + return +} + +func (r *Raw) mul(r2 *Raw) (r3 *Raw) { + r3 = &Raw{Value: r.Value.Mul(r2.Value)} + return +} + +func opSub(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + + raw, iErr := op1.Data[j].sub(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) sub(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw := t[i].sub(t2[i]) + err = flowCheck(ctx, raw.Value, meta[i]) + if err != nil { + return + } + + t3[i] = raw + } + return +} + +func (r *Raw) sub(r2 *Raw) (r3 *Raw) { + r3 = &Raw{Value: r.Value.Sub(r2.Value)} + return +} + +func opDiv(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + raw, iErr := op1.Data[j].div(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) div(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw, iErr := t[i].div(t2[i]) + if iErr != nil { + err = iErr + return + } + + iErr = flowCheck(ctx, raw.Value, meta[i]) + if iErr != nil { + err = iErr + return + } + + t3[i] = raw + } + return +} + +func (r *Raw) div(r2 *Raw) (r3 *Raw, err error) { + if r2.Value.IsZero() { + err = se.ErrorCodeDividedByZero + return + } + + q, _ := r.Value.QuoRem(r2.Value, 0) + + r3 = &Raw{Value: q} + return +} + +func opMod(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllArith(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + raw, iErr := op1.Data[j].mod(ctx, op2.Data[k], op1.Meta) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + + registers[output] = &Operand{Meta: op1.cloneMeta(), Data: data} + return +} + +func (t Tuple) mod(ctx *common.Context, t2 Tuple, meta []ast.DataType) (t3 Tuple, err error) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + raw, iErr := t[i].mod(t2[i]) + if iErr != nil { + err = iErr + return + + } + t3[i] = raw + } + return +} + +func (r *Raw) mod(r2 *Raw) (r3 *Raw, err error) { + if r2.Value.IsZero() { + err = se.ErrorCodeDividedByZero + return + } + + _, qr := r.Value.QuoRem(r2.Value, 0) + + r3 = &Raw{Value: qr} + return +} + +func opLt(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].lt(op2.Data[k]) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, len(op1.Meta)) + for i := 0; i < len(op1.Meta); i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) lt(t2 Tuple) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = bool2Raw(t[i].lt(t2[i])) + } + return +} + +func (r *Raw) lt(r2 *Raw) (lt bool) { + if r.Bytes == nil { + lt = r.Value.Cmp(r2.Value) < 0 + } else { + lt = bytes.Compare(r.Bytes, r2.Bytes) < 0 + } + return +} + +func opGt(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].gt(op2.Data[k]) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, len(op1.Meta)) + for i := 0; i < len(op1.Meta); i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) gt(t2 Tuple) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = bool2Raw(t[i].gt(t2[i])) + } + return +} + +func (r *Raw) gt(r2 *Raw) (gt bool) { + if r.Bytes == nil { + gt = r.Value.Cmp(r2.Value) > 0 + } else { + gt = bytes.Compare(r.Bytes, r2.Bytes) > 0 + } + return +} + +func opEq(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].eq(op2.Data[k], op1.Meta) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, len(op1.Meta)) + for i := 0; i < len(op1.Meta); i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) eq(t2 Tuple, meta []ast.DataType) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = bool2Raw(t[i].Equal(t2[i], meta[i])) + } + return +} + +func opAnd(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + if !metaAllBool(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].and(op2.Data[k]) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, l) + for i := 0; i < l; i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) and(t2 Tuple) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = t[i].and(t2[i]) + } + return +} + +func (r *Raw) and(r2 *Raw) (r3 *Raw) { + r3 = bool2Raw(r.isTrue() && r2.isTrue()) + return +} + +func opOr(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + if !metaAllBool(op1) { + err = se.ErrorCodeInvalidDataType + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + data := make([]Tuple, l) + for i, j, k := 0, 0, 0; i < l; i, j, k = i+1, j+1, k+1 { + if op1.IsImmediate { + j = 0 + } + if op2.IsImmediate { + k = 0 + } + data[i] = op1.Data[j].or(op2.Data[k]) + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, l) + for i := 0; i < l; i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +func (t Tuple) or(t2 Tuple) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = t[i].or(t2[i]) + } + return +} + +func (r *Raw) or(r2 *Raw) (r3 *Raw) { + r3 = bool2Raw(r.Value.Equal(dec.True) || r2.Value.Equal(dec.True)) + return +} + +func opNot(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 1 { + err = se.ErrorCodeInvalidOperandNum + return + } + op := ops[0] + + if !metaAllBool(op) { + err = se.ErrorCodeInvalidDataType + return + } + + data := make([]Tuple, len(op.Data)) + for i := 0; i < len(op.Data); i++ { + data[i] = op.Data[i].not() + } + + registers[output] = &Operand{Meta: op.cloneMeta(), Data: data} + return +} + +func (t Tuple) not() (t2 Tuple) { + t2 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t2[i] = t[i].not() + } + return +} + +func (r *Raw) not() (r2 *Raw) { + r2 = bool2Raw(r.Value.IsZero()) + return +} + +func opUnion(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + if len(op1.Data) > len(op2.Data) { + op1, op2 = op2, op1 + } + + tmpMap := make(map[string]struct{}) + for i := 0; i < len(op1.Data); i++ { + tmpMap[op1.Data[i].String()] = struct{}{} + } + + op3 := op1.clone(false) + + for i := 0; i < len(op2.Data); i++ { + if _, ok := tmpMap[op2.Data[i].String()]; !ok { + op3.Data = append(op3.Data, append(Tuple{}, op2.Data[i]...)) + } + } + + orders := make([]sortOption, len(op3.Meta)) + for i := 0; i < len(orders); i++ { + orders[i] = sortOption{Asc: true, Field: uint(i)} + } + + sort.SliceStable( + op3.Data, + func(i, j int) bool { return op3.Data[i].less(op3.Data[j], orders) }, + ) + + registers[output] = op3 + return +} + +func opIntxn(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op1, op2 := ops[0], ops[1] + + if !metaAllEq(op1, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + if len(op1.Data) > len(op2.Data) { + op1, op2 = op2, op1 + } + + tmpMap := make(map[string]struct{}) + for i := 0; i < len(op1.Data); i++ { + tmpMap[op1.Data[i].String()] = struct{}{} + } + + op3 := &Operand{Meta: op1.cloneMeta(), Data: []Tuple{}} + + for i := 0; i < len(op2.Data); i++ { + if _, ok := tmpMap[op2.Data[i].String()]; ok { + op3.Data = append(op3.Data, append(Tuple{}, op2.Data[i]...)) + } + } + + orders := make([]sortOption, len(op3.Meta)) + for i := 0; i < len(orders); i++ { + orders[i] = sortOption{Asc: true, Field: uint(i)} + } + + sort.SliceStable( + op3.Data, + func(i, j int) bool { return op3.Data[i].less(op3.Data[j], orders) }, + ) + + registers[output] = op3 + return +} + +func opLike(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 && len(ops) != 3 { + err = se.ErrorCodeInvalidOperandNum + return + } + op, pattern := ops[0], ops[1] + + var escape *Operand + if len(ops) > 2 { + escape = ops[2] + } + + var cReg *regexp.Regexp + + matchWithI := pattern.IsImmediate && (escape == nil || escape.IsImmediate) + if matchWithI { + var escapeBytes []byte + if escape != nil && len(escape.Data) > 0 { + escapeBytes = escape.Data[0][0].Bytes + } + + if len(escapeBytes) > 1 { + err = se.ErrorCodeMultipleEscapeByte + return + } + + cReg, err = like2regexp(pattern.Data[0][0].Bytes, escapeBytes) + if err != nil { + return + } + + } + + data := make([]Tuple, len(op.Data)) + if matchWithI { + for i := 0; i < len(op.Data); i++ { + raw, iErr := op.Data[i].like(cReg) + if iErr != nil { + err = iErr + return + } + data[i] = raw + } + } else { + var ( + pat []byte + esc []byte + ) + + for i := 0; i < len(op.Data); i++ { + if pattern.IsImmediate { + pat = pattern.Data[0][0].Bytes + } else { + pat = pattern.Data[i][0].Bytes + } + + if escape != nil { + if escape.IsImmediate { + esc = escape.Data[0][0].Bytes + } else { + esc = escape.Data[i][0].Bytes + } + } else { + esc = []byte{} + } + + if len(esc) > 1 { + err = se.ErrorCodeMultipleEscapeByte + return + } + + reg, iErr := like2regexp(pat, esc) + if iErr != nil { + err = iErr + return + } + + raw, iErr := op.Data[i].like(reg) + if iErr != nil { + err = iErr + return + } + + data[i] = raw + } + } + + boolType := ast.ComposeDataType(ast.DataTypeMajorBool, 0) + meta := make([]ast.DataType, len(op.Meta)) + for i := 0; i < len(meta); i++ { + meta[i] = boolType + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +// check parser/parser.go comment for string encoding +func encB(b []byte) []byte { + encBuf := bytes.Buffer{} + for _, c := range b { + encBuf.WriteRune(rune(c)) + } + return encBuf.Bytes() +} + +func writeC2Buf(buf *bytes.Buffer, c byte) { + if c < 0x80 { + // quote for valid ascii + buf.WriteString(regexp.QuoteMeta(string(c))) + } else { + buf.WriteRune(rune(c)) + } +} + +func like2regexp(pattern []byte, escape []byte) (reg *regexp.Regexp, err error) { + var ( + buf = &bytes.Buffer{} + escMode = len(escape) > 0 + isEsc = false + c byte + ) + + for i := 0; i < len(pattern); i++ { + c = pattern[i] + + if escMode && !isEsc && c == escape[0] { + isEsc = true + continue + } + + if escMode && isEsc { + isEsc = false + writeC2Buf(buf, c) + continue + } + + switch c { + case byteLikeP: + buf.Write(bytesLikeReg) + case byteLikeU: + buf.WriteByte(byteDot) + default: + writeC2Buf(buf, c) + } + } + + if isEsc { + err = se.ErrorCodePendingEscapeByte + return + } + + rPattern := buf.Bytes() + + if !bytes.HasPrefix(rPattern, bytesLikeReg) { + rPattern = append(bytesStart, rPattern...) + } + + if !bytes.HasSuffix(rPattern, bytesLikeReg) { + rPattern = append(rPattern, bytesEnd...) + } + + reg, err = regexp.Compile(string(rPattern)) + return +} + +func (t Tuple) like(reg *regexp.Regexp) (t2 Tuple, err error) { + t2 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t2[i] = t[i].like(reg) + } + return +} + +func (r *Raw) like(reg *regexp.Regexp) (r2 *Raw) { + r2 = bool2Raw(reg.Match(encB(r.Bytes))) + return +} + +func opZip(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) == 0 { + err = se.ErrorCodeInvalidOperandNum + return + } + + l, err := findMaxDataLength(ops) + if err != nil { + return + } + + op3 := &Operand{Meta: make([]ast.DataType, 0), Data: make([]Tuple, l)} + + for i := 0; i < len(ops); i++ { + op3.Meta = append(op3.Meta, ops[i].Meta...) + } + + for i := 0; i < l; i++ { + if ops[0].IsImmediate { + op3.Data[i] = append(Tuple{}, ops[0].Data[0]...) + } else { + op3.Data[i] = append(Tuple{}, ops[0].Data[i]...) + } + + for j := 1; j < len(ops); j++ { + if ops[j].IsImmediate { + op3.Data[i] = append(op3.Data[i], ops[j].Data[0]...) + } else { + op3.Data[i] = append(op3.Data[i], ops[j].Data[i]...) + } + } + } + + registers[output] = op3 + return +} + +func opField(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op, fields := ops[0], ops[1].Data[0] + fLen := len(fields) + + var fieldIdx uint16 + meta, fieldIdxs := make([]ast.DataType, fLen), make([]uint16, fLen) + for i := 0; i < fLen; i++ { + fieldIdx = value2ColIdx(fields[i].Value) + if len(op.Meta) <= int(fieldIdx) { + err = se.ErrorCodeIndexOutOfRange + return + } + meta[i], fieldIdxs[i] = op.Meta[fieldIdx], fieldIdx + } + + data := make([]Tuple, len(op.Data)) + for i := 0; i < len(op.Data); i++ { + tuple := make(Tuple, fLen) + for j := 0; j < fLen; j++ { + tuple[j] = op.Data[i][fieldIdxs[j]] + } + data[i] = tuple + } + + registers[output] = &Operand{Meta: meta, Data: data} + return +} + +// in-place Op +func opPrune(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op, fields := ops[0], ops[1].Data[0] + fLen := len(fields) + + var fieldIdx uint16 + fieldIdxs := make([]int, fLen) + for i := 0; i < fLen; i++ { + fieldIdx = value2ColIdx(fields[i].Value) + if len(op.Meta) <= int(fieldIdx) { + err = se.ErrorCodeIndexOutOfRange + return + } + fieldIdxs[i] = int(fieldIdx) + } + + op.Meta = pruneMeta(op.Meta, fieldIdxs) + + for i := 0; i < len(op.Data); i++ { + op.Data[i] = pruneTuple(op.Data[i], fieldIdxs) + } + return +} + +func pruneMeta(meta []ast.DataType, prune []int) []ast.DataType { + for src, dst, pruneIdx := 0, 0, 0; src < len(meta); src++ { + if pruneIdx < len(prune) && src == prune[pruneIdx] { + pruneIdx++ + continue + } + meta[dst] = meta[src] + dst++ + } + return meta[:len(meta)-len(prune)] +} + +func pruneTuple(t Tuple, prune []int) Tuple { + for src, dst, pruneIdx := 0, 0, 0; src < len(t); src++ { + if pruneIdx < len(prune) && src == prune[pruneIdx] { + pruneIdx++ + continue + } + t[dst] = t[src] + dst++ + } + return t[:len(t)-len(prune)] +} + +// in-place Op +func opSort(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op := ops[0] + + // orders (ascending bool, field) + orders := make([]sortOption, len(ops[1].Data)) + for i, o := range ops[1].Data { + orders[i] = sortOption{ + Asc: o[0].isTrue(), + Field: uint(value2ColIdx(o[1].Value)), + } + } + + sort.SliceStable( + op.Data, + func(i, j int) bool { return op.Data[i].less(op.Data[j], orders) }, + ) + return +} + +type sortOption struct { + Asc bool + Field uint +} + +func (t Tuple) less(t2 Tuple, orders []sortOption) bool { + var r int + + for _, o := range orders { + r = 0 + + if o.Asc { + r = t[o.Field].cmp(t2[o.Field]) + } else { + r = t2[o.Field].cmp(t[o.Field]) + } + + switch r { + case -1: + return true + case 1: + return false + } + } + return false +} + +func (r *Raw) cmp(r2 *Raw) (v int) { + if r.Bytes != nil { + v = bytes.Compare(r.Bytes, r2.Bytes) + } else { + v = r.Value.Cmp(r2.Value) + } + return +} + +func opFilter(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op, filters := ops[0], ops[1] + + op2 := &Operand{Meta: op.cloneMeta(), Data: make([]Tuple, 0)} + + for i := 0; i < len(filters.Data); i++ { + if filters.Data[i][0].isTrue() { + op2.Data = append(op2.Data, append(Tuple{}, op.Data[i]...)) + } + } + + registers[output] = op2 + return +} + +// Type check will ensure all cast is valid +func opCast(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + op := ops[0] + dTypes := ops[1].Meta + + if len(dTypes) != len(op.Meta) { + err = se.ErrorCodeInvalidDataType + return + } + + op2 := &Operand{Meta: ops[1].cloneMeta(), Data: make([]Tuple, len(op.Data))} + for i := 0; i < len(op.Data); i++ { + op2.Data[i] = append(Tuple{}, op.Data[i]...) + + for j, dType := range dTypes { + if op.Meta[j] == dType { + continue + } + + err = op2.Data[i][j].cast(ctx, op.Meta[j], dType) + if err != nil { + return + } + } + } + + registers[output] = op2 + return +} + +func (r *Raw) cast(ctx *common.Context, origin, target ast.DataType) (err error) { + oMajor, _ := ast.DecomposeDataType(origin) + + // conversion table + switch oMajor { + case ast.DataTypeMajorInt, ast.DataTypeMajorUint: + err = r.castInt(ctx, origin, target) + case ast.DataTypeMajorFixedBytes: + err = r.castFixedBytes(ctx, origin, target) + case ast.DataTypeMajorAddress: + err = r.castAddress(ctx, origin, target) + case ast.DataTypeMajorBool: + err = r.castBool(origin, target) + case ast.DataTypeMajorDynamicBytes: + err = r.castDynBytes(origin, target) + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castValue( + ctx *common.Context, + origin, target ast.DataType, + l int, signed, rPadding bool) (err error) { + oBytes, err := ast.DecimalEncode(origin, r.Value) + if err != nil { + return + } + + bytes2 := r.shiftBytes(oBytes, l, signed, rPadding) + + r.Value, err = ast.DecimalDecode(target, bytes2) + if err != nil { + return + } + + err = flowCheck(ctx, r.Value, target) + return +} + +func (r *Raw) castInt(ctx *common.Context, origin, target ast.DataType) (err error) { + oMajor, oMinor := ast.DecomposeDataType(origin) + tMajor, tMinor := ast.DecomposeDataType(target) + signed := oMajor == ast.DataTypeMajorInt + + switch tMajor { + case ast.DataTypeMajorInt, ast.DataTypeMajorUint: + err = r.castValue(ctx, origin, target, int(tMinor)+1, signed, false) + case ast.DataTypeMajorAddress: + r.Bytes, err = ast.DecimalEncode(origin, r.Value) + if err != nil { + return + } + + if len(r.Bytes) > dexCommon.AddressLength { + if r.Bytes[0]&0x80 != 0 && signed { + err = se.ErrorCodeUnderflow + } else { + err = se.ErrorCodeOverflow + } + return + } + + r.Bytes = r.shiftBytes(r.Bytes, dexCommon.AddressLength, signed, false) + r.Value = decimal.Zero + case ast.DataTypeMajorFixedBytes: + if tMinor != oMinor { + err = se.ErrorCodeInvalidCastType + return + } + r.Bytes, err = ast.DecimalEncode(origin, r.Value) + if err != nil { + return + } + r.Value = decimal.Zero + case ast.DataTypeMajorBool: + r.Value = dec.Val2Bool(r.Value) + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castFixedBytes(ctx *common.Context, origin, target ast.DataType) (err error) { + _, oMinor := ast.DecomposeDataType(origin) + tMajor, tMinor := ast.DecomposeDataType(target) + switch tMajor { + case ast.DataTypeMajorDynamicBytes: + case ast.DataTypeMajorInt, ast.DataTypeMajorUint: + if tMinor != oMinor { + err = se.ErrorCodeInvalidCastType + return + } + r.Value, err = ast.DecimalDecode(target, r.Bytes) + if err != nil { + return + } + r.Bytes = nil + case ast.DataTypeMajorFixedBytes: + r.Bytes = r.shiftBytes(r.Bytes, int(tMinor)+1, false, true) + case ast.DataTypeMajorAddress: + if oMinor != (dexCommon.AddressLength - 1) { + err = se.ErrorCodeInvalidCastType + return + } + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castAddress(ctx *common.Context, origin, target ast.DataType) (err error) { + tMajor, tMinor := ast.DecomposeDataType(target) + + switch tMajor { + case ast.DataTypeMajorAddress: + case ast.DataTypeMajorInt, ast.DataTypeMajorUint: + r.Value, err = ast.DecimalDecode( + target, + r.shiftBytes(r.Bytes, int(tMinor)+1, false, false), + ) + if err != nil { + return + } + err = flowCheck(ctx, r.Value, target) + if err != nil { + return + } + r.Bytes = nil + case ast.DataTypeMajorFixedBytes: + if tMinor != (dexCommon.AddressLength - 1) { + err = se.ErrorCodeInvalidCastType + return + } + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castBool(origin, target ast.DataType) (err error) { + tMajor, _ := ast.DecomposeDataType(target) + switch tMajor { + case ast.DataTypeMajorBool, ast.DataTypeMajorInt, ast.DataTypeMajorUint: + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) castDynBytes(origin, target ast.DataType) (err error) { + tMajor, tMinor := ast.DecomposeDataType(target) + switch tMajor { + case ast.DataTypeMajorDynamicBytes: + case ast.DataTypeMajorFixedBytes: + r.Bytes = r.shiftBytes(r.Bytes, int(tMinor)+1, false, true) + default: + err = se.ErrorCodeInvalidCastType + } + return +} + +func (r *Raw) shiftBytes(src []byte, l int, signed, rPadding bool) (tgr []byte) { + if len(src) >= l { + if rPadding { + tgr = src[:l] + } else { + tgr = src[len(src)-l:] + } + return + } + + tgr = make([]byte, l) + + if rPadding { + copy(tgr, src) + return + } + + copy(tgr[l-len(src):], src) + + if signed && src[0]&0x80 != 0 { + for i := 0; i < l-len(src); i++ { + tgr[i] = 0xff + } + } + return +} diff --git a/core/vm/sqlvm/runtime/opcodes.go b/core/vm/sqlvm/runtime/opcodes.go index 5803c6982..88bc039e1 100644 --- a/core/vm/sqlvm/runtime/opcodes.go +++ b/core/vm/sqlvm/runtime/opcodes.go @@ -3,6 +3,11 @@ package runtime // OpCode type type OpCode byte +// 0x00 range - higher order ops. +const ( + NOP OpCode = iota +) + // 0x10 range - arithmetic ops. (array-based) const ( ADD OpCode = iota + 0x10 @@ -36,6 +41,7 @@ const ( const ( ZIP OpCode = iota + 0x40 FIELD + PRUNE SORT FILTER CAST diff --git a/core/vm/sqlvm/runtime/runtime.go b/core/vm/sqlvm/runtime/runtime.go index 8c3a105ac..e6a3a0006 100644 --- a/core/vm/sqlvm/runtime/runtime.go +++ b/core/vm/sqlvm/runtime/runtime.go @@ -3,20 +3,25 @@ package runtime import ( "github.com/dexon-foundation/dexon/core/vm" "github.com/dexon-foundation/dexon/core/vm/sqlvm/common" - "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" + se "github.com/dexon-foundation/dexon/core/vm/sqlvm/errors" ) // Run is runtime entrypoint. func Run(stateDB vm.StateDB, ins []Instruction, registers []*Operand) (ret []byte, err error) { for _, in := range ins { + for i := 0; i < len(in.Input); i++ { + if !in.Input[i].IsImmediate { + in.Input[i] = registers[in.Input[i].RegisterIndex] + } + } opFunc := jumpTable[in.Op] loadRegister(in.Input, registers) errCode := opFunc(&common.Context{}, in.Input, registers, in.Output) if errCode != nil { - err = errors.Error{ + err = se.Error{ Position: in.Position, - Code: errCode.(errors.ErrorCode), - Category: errors.ErrorCategoryRuntime, + Code: errCode.(se.ErrorCode), + Category: se.ErrorCategoryRuntime, } return nil, err } |