aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJhih-Ming Huang <jm.huang@cobinhood.com>2019-04-22 17:56:20 +0800
committerJhih-Ming Huang <jm@dexon.org>2019-05-08 21:45:52 +0800
commitbf103451425a0f7a986ff6dd1e2b7d1e478bd948 (patch)
tree7f7a1a91496be7403dbb73844e8379c2f0f5c541
parentd0f2c8fac94f6917e4e997fd0b236d4de36e41d1 (diff)
downloaddexon-bf103451425a0f7a986ff6dd1e2b7d1e478bd948.tar.gz
dexon-bf103451425a0f7a986ff6dd1e2b7d1e478bd948.tar.zst
dexon-bf103451425a0f7a986ff6dd1e2b7d1e478bd948.zip
core: vm: sqlvm: runtime: implement fillAutoInc and fillDefault
This commit implements fillAutoInc and fillDefault, and modifies Storage.IncSequence such that it can handle the numbers larger than maximum of uint64.
-rw-r--r--core/vm/sqlvm/common/storage.go20
-rw-r--r--core/vm/sqlvm/common/storage_test.go18
-rw-r--r--core/vm/sqlvm/runtime/instructions.go62
-rw-r--r--core/vm/sqlvm/runtime/instructions_test.go293
-rw-r--r--core/vm/sqlvm/schema/schema.go5
5 files changed, 372 insertions, 26 deletions
diff --git a/core/vm/sqlvm/common/storage.go b/core/vm/sqlvm/common/storage.go
index f56e7984d..7babc04dd 100644
--- a/core/vm/sqlvm/common/storage.go
+++ b/core/vm/sqlvm/common/storage.go
@@ -5,6 +5,7 @@ import (
"golang.org/x/crypto/sha3"
+ "github.com/dexon-foundation/decimal"
"github.com/dexon-foundation/dexon/common"
"github.com/dexon-foundation/dexon/core/vm"
"github.com/dexon-foundation/dexon/core/vm/sqlvm/schema"
@@ -114,8 +115,8 @@ func (s *Storage) GetPrimaryPathHash(tableRef schema.TableRef) (h common.Hash) {
return s.hashPathKey(key)
}
-// getSequencePathHash return the hash address of a sequence.
-func (s *Storage) getSequencePathHash(
+// GetSequencePathHash return the hash address of a sequence.
+func (s *Storage) GetSequencePathHash(
tableRef schema.TableRef, seqIdx uint8,
) common.Hash {
// PathKey(["tables", "{table_name}", "sequence", uint8(sequence_idx)])
@@ -225,11 +226,14 @@ func (s *Storage) IncSequence(
tableRef schema.TableRef,
seqIdx uint8,
inc uint64,
-) uint64 {
- seqPath := s.getSequencePathHash(tableRef, seqIdx)
+) decimal.Decimal {
+ seqPath := s.GetSequencePathHash(tableRef, seqIdx)
slot := s.GetState(contract, seqPath)
- val := bytesToUint64(slot.Bytes())
- // TODO(yenlin): Check overflow?
- s.SetState(contract, seqPath, common.BytesToHash(uint64ToBytes(val+inc)))
- return val
+ b := new(big.Int).SetBytes(slot.Bytes())
+ b.Add(b, new(big.Int).SetUint64(inc))
+ newHash := make([]byte, common.HashLength)
+ bs := b.Bytes()
+ copy(newHash[common.HashLength-len(bs):], bs)
+ s.SetState(contract, seqPath, common.BytesToHash(newHash))
+ return decimal.NewFromBigInt(b, 0)
}
diff --git a/core/vm/sqlvm/common/storage_test.go b/core/vm/sqlvm/common/storage_test.go
index b02705c11..2bc1354e1 100644
--- a/core/vm/sqlvm/common/storage_test.go
+++ b/core/vm/sqlvm/common/storage_test.go
@@ -192,17 +192,17 @@ func (s *StorageTestSuite) TestSequence() {
table2 := schema.TableRef(1)
contract := common.BytesToAddress([]byte("A"))
- s.Require().Equal(uint64(0), s.storage.IncSequence(contract, table1, 0, 2))
- s.Require().Equal(uint64(2), s.storage.IncSequence(contract, table1, 0, 1))
- s.Require().Equal(uint64(3), s.storage.IncSequence(contract, table1, 0, 1))
+ s.Require().Equal(uint64(2), s.storage.IncSequence(contract, table1, 0, 2).Rescale(0).Coefficient().Uint64())
+ s.Require().Equal(uint64(3), s.storage.IncSequence(contract, table1, 0, 1).Rescale(0).Coefficient().Uint64())
+ s.Require().Equal(uint64(4), s.storage.IncSequence(contract, table1, 0, 1).Rescale(0).Coefficient().Uint64())
// Repeat on another sequence.
- s.Require().Equal(uint64(0), s.storage.IncSequence(contract, table1, 1, 1))
- s.Require().Equal(uint64(1), s.storage.IncSequence(contract, table1, 1, 2))
- s.Require().Equal(uint64(3), s.storage.IncSequence(contract, table1, 1, 3))
+ s.Require().Equal(uint64(1), s.storage.IncSequence(contract, table1, 1, 1).Rescale(0).Coefficient().Uint64())
+ s.Require().Equal(uint64(3), s.storage.IncSequence(contract, table1, 1, 2).Rescale(0).Coefficient().Uint64())
+ s.Require().Equal(uint64(6), s.storage.IncSequence(contract, table1, 1, 3).Rescale(0).Coefficient().Uint64())
// Repeat on another table.
- s.Require().Equal(uint64(0), s.storage.IncSequence(contract, table2, 0, 3))
- s.Require().Equal(uint64(3), s.storage.IncSequence(contract, table2, 0, 4))
- s.Require().Equal(uint64(7), s.storage.IncSequence(contract, table2, 0, 5))
+ s.Require().Equal(uint64(3), s.storage.IncSequence(contract, table2, 0, 3).Rescale(0).Coefficient().Uint64())
+ s.Require().Equal(uint64(7), s.storage.IncSequence(contract, table2, 0, 4).Rescale(0).Coefficient().Uint64())
+ s.Require().Equal(uint64(12), s.storage.IncSequence(contract, table2, 0, 5).Rescale(0).Coefficient().Uint64())
}
func (s *StorageTestSuite) TestPKHeaderEncodeDecode() {
diff --git a/core/vm/sqlvm/runtime/instructions.go b/core/vm/sqlvm/runtime/instructions.go
index 35d3135b2..033ad3080 100644
--- a/core/vm/sqlvm/runtime/instructions.go
+++ b/core/vm/sqlvm/runtime/instructions.go
@@ -1921,3 +1921,65 @@ func opRepeatPK(ctx *common.Context, input []*Operand, registers []*Operand, out
registers[output], err = uint64ToOperands(IDs)
return
}
+
+// fillAutoInc returns the operand reference with incremented value.
+func fillAutoInc(
+ ctx *common.Context,
+ col schema.Column,
+ tableRef schema.TableRef,
+) (*Operand, error) {
+ dVal := ctx.Storage.IncSequence(ctx.Contract.Address(),
+ tableRef, uint8(col.Sequence), 1)
+ _, max, ok := col.Type.GetMinMax()
+ if !ok {
+ return nil, se.ErrorCodeInvalidDataType
+ }
+ if dVal.Cmp(max) > 0 {
+ return nil, se.ErrorCodeOverflow
+ }
+ op := &Operand{
+ Meta: []ast.DataType{col.Type},
+ Data: []Tuple{
+ {
+ &Raw{
+ Value: dVal,
+ Bytes: nil,
+ },
+ },
+ },
+ }
+ return op, nil
+}
+
+// fillDefault returns the operand reference with default value.
+func fillDefault(
+ ctx *common.Context,
+ col schema.Column,
+) (*Operand, error) {
+ var r Raw
+ major, _ := ast.DecomposeDataType(col.Type)
+ switch major {
+ case ast.DataTypeMajorDynamicBytes, ast.DataTypeMajorAddress:
+ r = Raw{
+ Bytes: col.Default.([]byte),
+ }
+ case ast.DataTypeMajorBool:
+ b := col.Default.(bool)
+ if b {
+ r = Raw{Value: dec.True}
+ } else {
+ r = Raw{Value: dec.False}
+ }
+ default:
+ r = Raw{Value: col.Default.(decimal.Decimal)}
+ }
+ op := &Operand{
+ Meta: []ast.DataType{col.Type},
+ Data: []Tuple{
+ {
+ &r,
+ },
+ },
+ }
+ return op, nil
+}
diff --git a/core/vm/sqlvm/runtime/instructions_test.go b/core/vm/sqlvm/runtime/instructions_test.go
index a00ac12dd..55dbab18d 100644
--- a/core/vm/sqlvm/runtime/instructions_test.go
+++ b/core/vm/sqlvm/runtime/instructions_test.go
@@ -55,7 +55,7 @@ func createSchema(storage *common.Storage, raws []*raw) {
storage.Schema[1].Columns[i] = schema.NewColumn(
[]byte{byte(i)},
ast.ComposeDataType(raws[i].major, raws[i].minor),
- 0, nil, 0,
+ 0, nil, 0, nil,
)
}
storage.Schema.SetupColumnOffset()
@@ -481,12 +481,6 @@ func (s opRepeatPKSuite) TestRepeatPK() {
}
}
-func TestInstructions(t *testing.T) {
- suite.Run(t, new(opLoadSuite))
- suite.Run(t, new(opRepeatPKSuite))
- suite.Run(t, new(instructionSuite))
-}
-
func makeOperand(im bool, meta []ast.DataType, pTuple []Tuple) (op *Operand) {
op = &Operand{IsImmediate: im, Meta: meta, Data: pTuple}
return
@@ -541,3 +535,288 @@ func (s *instructionSuite) run(testcases []opTestcase, opfunc OpFunction) {
)
}
}
+
+type autoIncSuite struct {
+ suite.Suite
+ ctx *common.Context
+}
+
+func (s *autoIncSuite) SetupTest() {
+ s.ctx = &common.Context{}
+ s.ctx.Storage = newStorage()
+ address := dexCommon.HexToAddress("0x6655")
+ s.ctx.Storage.CreateAccount(address)
+ s.ctx.Contract = vm.NewContract(vm.AccountRef(address),
+ vm.AccountRef(address), new(big.Int), 0)
+ s.ctx.Storage.Schema = schema.Schema{
+ schema.Table{
+ Name: []byte("normal_case"),
+ Columns: []schema.Column{
+ schema.NewColumn(
+ []byte("c1"),
+ ast.ComposeDataType(ast.DataTypeMajorInt, 0),
+ schema.ColumnAttrHasSequence,
+ nil,
+ 0,
+ nil,
+ ),
+ schema.NewColumn(
+ []byte("c2"),
+ ast.ComposeDataType(ast.DataTypeMajorUint, 0),
+ schema.ColumnAttrHasSequence,
+ nil,
+ 1,
+ nil,
+ ),
+ },
+ },
+ schema.Table{
+ Name: []byte("overflow_int_case"),
+ Columns: []schema.Column{
+ schema.NewColumn(
+ []byte("c1"),
+ ast.ComposeDataType(ast.DataTypeMajorInt, 0),
+ schema.ColumnAttrHasSequence,
+ nil,
+ 0,
+ nil,
+ ),
+ },
+ },
+ schema.Table{
+ Name: []byte("overflow_uint_case"),
+ Columns: []schema.Column{
+ schema.NewColumn(
+ []byte("c1"),
+ ast.ComposeDataType(ast.DataTypeMajorUint, 0),
+ schema.ColumnAttrHasSequence,
+ nil,
+ 0,
+ nil,
+ ),
+ },
+ },
+ }
+ s.SetOverflow(1, 0, ast.ComposeDataType(ast.DataTypeMajorInt, 0))
+ s.SetOverflow(2, 0, ast.ComposeDataType(ast.DataTypeMajorUint, 0))
+ s.ctx.Storage.Schema.SetupColumnOffset()
+}
+
+func (s *autoIncSuite) SetOverflow(tableRef schema.TableRef, seqIdx uint8, dt ast.DataType) {
+ storage := s.ctx.Storage
+ seqPath := storage.GetSequencePathHash(tableRef, seqIdx)
+ newHash := make([]byte, dexCommon.HashLength)
+ _, max, _ := dt.GetMinMax()
+ bs, _ := ast.DecimalEncode(dt, max)
+ copy(newHash[len(newHash)-len(bs):], bs)
+ storage.SetState(s.ctx.Contract.Address(), seqPath, dexCommon.BytesToHash(newHash))
+}
+
+func (s *autoIncSuite) TestFillAutoInc() {
+ type testcase struct {
+ name string
+ col schema.Column
+ tableRef schema.TableRef
+ result *Operand
+ err error
+ }
+ tt := []testcase{
+ {
+ name: "normal case 1",
+ col: s.ctx.Storage.Schema[0].Columns[0],
+ tableRef: schema.TableRef(0),
+ result: &Operand{
+ Meta: []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorInt, 0)},
+ Data: []Tuple{
+ {
+ &Raw{
+ Value: decimal.New(1, 0),
+ },
+ },
+ },
+ },
+ err: nil,
+ },
+ {
+ name: "normal case 2",
+ col: s.ctx.Storage.Schema[0].Columns[1],
+ tableRef: schema.TableRef(0),
+ result: &Operand{
+ Meta: []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorUint, 0)},
+ Data: []Tuple{
+ {
+ &Raw{
+ Value: decimal.New(1, 0),
+ },
+ },
+ },
+ },
+ err: nil,
+ },
+ {
+ name: "int overflow",
+ col: s.ctx.Storage.Schema[1].Columns[0],
+ tableRef: schema.TableRef(1),
+ result: nil,
+ err: errors.ErrorCodeOverflow,
+ },
+ {
+ name: "unt overflow",
+ col: s.ctx.Storage.Schema[2].Columns[0],
+ tableRef: schema.TableRef(2),
+ result: nil,
+ err: errors.ErrorCodeOverflow,
+ },
+ }
+
+ for _, t := range tt {
+ r, err := fillAutoInc(s.ctx, t.col, t.tableRef)
+ s.Require().Equalf(t.err, err, "testcase %v\n", t.name)
+ if t.err == nil {
+ s.Require().Truef(r.Equal(t.result),
+ "testcase: %v", t.name)
+ }
+ }
+}
+
+type setDefaultSuite struct {
+ suite.Suite
+ ctx *common.Context
+}
+
+func (s *setDefaultSuite) SetupTest() {
+ s.ctx = &common.Context{}
+ s.ctx.Storage = newStorage()
+ address := dexCommon.HexToAddress("0x6655")
+ s.ctx.Storage.CreateAccount(address)
+ s.ctx.Contract = vm.NewContract(vm.AccountRef(address),
+ vm.AccountRef(address), new(big.Int), 0)
+ s.ctx.Storage.Schema = schema.Schema{
+ schema.Table{
+ Name: []byte("table1"),
+ Columns: []schema.Column{
+ schema.NewColumn(
+ []byte("c1"),
+ ast.ComposeDataType(ast.DataTypeMajorInt, 0),
+ schema.ColumnAttrHasDefault,
+ nil,
+ 0,
+ decimal.New(127, 0),
+ ),
+ schema.NewColumn(
+ []byte("c2"),
+ ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0),
+ schema.ColumnAttrHasDefault,
+ nil,
+ 0,
+ []byte{1, 2, 3, 4},
+ ),
+ schema.NewColumn(
+ []byte("c3"),
+ ast.ComposeDataType(ast.DataTypeMajorUint, 0),
+ schema.ColumnAttrHasDefault,
+ nil,
+ 1,
+ decimal.New(255, 0),
+ ),
+ schema.NewColumn(
+ []byte("c4"),
+ ast.ComposeDataType(ast.DataTypeMajorAddress, 0),
+ schema.ColumnAttrHasDefault,
+ nil,
+ 1,
+ address[:],
+ ),
+ },
+ },
+ }
+ s.ctx.Storage.Schema.SetupColumnOffset()
+}
+
+func (s *setDefaultSuite) TestFillDefault() {
+ type testcase struct {
+ name string
+ col schema.Column
+ result *Operand
+ err error
+ }
+ tt := []testcase{
+ {
+ name: "int8",
+ col: s.ctx.Storage.Schema[0].Columns[0],
+ result: &Operand{
+ Meta: []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorInt, 0)},
+ Data: []Tuple{
+ {
+ &Raw{
+ Value: decimal.New(127, 0),
+ },
+ },
+ },
+ },
+ err: nil,
+ },
+ {
+ name: "dynamic byes",
+ col: s.ctx.Storage.Schema[0].Columns[1],
+ result: &Operand{
+ Meta: []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0)},
+ Data: []Tuple{
+ {
+ &Raw{
+ Bytes: []byte{1, 2, 3, 4},
+ },
+ },
+ },
+ },
+ err: nil,
+ },
+ {
+ name: "uint8",
+ col: s.ctx.Storage.Schema[0].Columns[2],
+ result: &Operand{
+ Meta: []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorUint, 0)},
+ Data: []Tuple{
+ {
+ &Raw{
+ Value: decimal.New(255, 0),
+ },
+ },
+ },
+ },
+ err: nil,
+ },
+ {
+ name: "address",
+ col: s.ctx.Storage.Schema[0].Columns[3],
+ result: &Operand{
+ Meta: []ast.DataType{ast.ComposeDataType(ast.DataTypeMajorAddress, 0)},
+ Data: []Tuple{
+ {
+ &Raw{
+ Bytes: dexCommon.HexToAddress("0x6655").Bytes(),
+ },
+ },
+ },
+ },
+ err: nil,
+ },
+ }
+
+ for _, t := range tt {
+ r, err := fillDefault(s.ctx, t.col)
+ s.Require().Equalf(t.err, err, "testcase %v\n", t.name)
+ if t.err == nil {
+ s.Require().Truef(r.Equal(t.result),
+ "testcase: %v", t.name)
+ }
+ }
+}
+
+func TestInstructions(t *testing.T) {
+ suite.Run(t, new(opLoadSuite))
+ suite.Run(t, new(opRepeatPKSuite))
+ suite.Run(t, new(instructionSuite))
+ suite.Run(t, new(autoIncSuite))
+ suite.Run(t, new(setDefaultSuite))
+}
diff --git a/core/vm/sqlvm/schema/schema.go b/core/vm/sqlvm/schema/schema.go
index 1e87d88cf..71122cc26 100644
--- a/core/vm/sqlvm/schema/schema.go
+++ b/core/vm/sqlvm/schema/schema.go
@@ -180,7 +180,7 @@ type Column struct {
// NewColumn return a Column instance.
func NewColumn(Name []byte, Type ast.DataType, Attr ColumnAttr,
- ForeignKeys []ColumnDescriptor, Sequence SequenceRef) Column {
+ ForeignKeys []ColumnDescriptor, Sequence SequenceRef, def interface{}) Column {
c := column{
Name: Name,
Type: Type,
@@ -190,7 +190,8 @@ func NewColumn(Name []byte, Type ast.DataType, Attr ColumnAttr,
}
return Column{
- column: c,
+ column: c,
+ Default: def,
}
}