aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJhih-Ming Huang <jm.huang@cobinhood.com>2019-03-15 17:43:50 +0800
committerJhih-Ming Huang <jm.huang@cobinhood.com>2019-03-26 17:48:23 +0800
commit952d0af6cb266824ca4517cca9d5dc6f3e505a1a (patch)
treebad8419a76a3e0b4dde68c046afc73f7f42e68ac
parentec427716b8d8419f0f4bb2761d1465bf8fc68ab7 (diff)
downloaddexon-952d0af6cb266824ca4517cca9d5dc6f3e505a1a.tar
dexon-952d0af6cb266824ca4517cca9d5dc6f3e505a1a.tar.gz
dexon-952d0af6cb266824ca4517cca9d5dc6f3e505a1a.tar.bz2
dexon-952d0af6cb266824ca4517cca9d5dc6f3e505a1a.tar.lz
dexon-952d0af6cb266824ca4517cca9d5dc6f3e505a1a.tar.xz
dexon-952d0af6cb266824ca4517cca9d5dc6f3e505a1a.tar.zst
dexon-952d0af6cb266824ca4517cca9d5dc6f3e505a1a.zip
core: vm: sqlvm: common: storage: implement storage util functions
Implement some storage utility functions, including shift slot, get dynamic byte and get primary key hash.
-rw-r--r--core/vm/sqlvm/common/context.go2
-rw-r--r--core/vm/sqlvm/common/storage.go93
-rw-r--r--core/vm/sqlvm/common/storage_test.go107
3 files changed, 201 insertions, 1 deletions
diff --git a/core/vm/sqlvm/common/context.go b/core/vm/sqlvm/common/context.go
index 7c4305e3e..1985473fa 100644
--- a/core/vm/sqlvm/common/context.go
+++ b/core/vm/sqlvm/common/context.go
@@ -6,6 +6,6 @@ import "github.com/dexon-foundation/dexon/core/vm"
type Context struct {
vm.Context
- StateDB vm.StateDB
+ Storage Storage
Contract *vm.Contract
}
diff --git a/core/vm/sqlvm/common/storage.go b/core/vm/sqlvm/common/storage.go
new file mode 100644
index 000000000..4595a773b
--- /dev/null
+++ b/core/vm/sqlvm/common/storage.go
@@ -0,0 +1,93 @@
+package common
+
+import (
+ "math/big"
+
+ "github.com/shopspring/decimal"
+ "golang.org/x/crypto/sha3"
+
+ "github.com/dexon-foundation/dexon/common"
+ "github.com/dexon-foundation/dexon/core/state"
+ "github.com/dexon-foundation/dexon/core/vm/sqlvm/ast"
+ "github.com/dexon-foundation/dexon/core/vm/sqlvm/schema"
+ "github.com/dexon-foundation/dexon/crypto"
+ "github.com/dexon-foundation/dexon/rlp"
+)
+
+// Storage holds SQLVM required data and method.
+type Storage struct {
+ state.StateDB
+ Schema schema.Schema
+}
+
+// NewStorage return Storage instance.
+func NewStorage(state *state.StateDB) Storage {
+ s := Storage{*state, schema.Schema{}}
+ return s
+}
+
+func convertIDtoBytes(id uint64) []byte {
+ bigIntID := new(big.Int).SetUint64(id)
+ decimalID := decimal.NewFromBigInt(bigIntID, 0)
+ dt := ast.ComposeDataType(ast.DataTypeMajorUint, 7)
+ byteID, _ := ast.DecimalEncode(dt, decimalID)
+ return byteID
+}
+
+// GetPrimaryKeyHash return primary key hash.
+func (s Storage) GetPrimaryKeyHash(tableName []byte, id uint64) (h common.Hash) {
+ key := [][]byte{
+ []byte("tables"),
+ tableName,
+ []byte("primary"),
+ convertIDtoBytes(id),
+ }
+ hw := sha3.NewLegacyKeccak256()
+ rlp.Encode(hw, key)
+ // length of common.Hash is 256bit,
+ // so it can properly match the size of hw.Sum
+ hw.Sum(h[:0])
+ return
+}
+
+// ShiftHashUint64 shift hash in uint64.
+func (s Storage) ShiftHashUint64(hash common.Hash, shift uint64) common.Hash {
+ bigIntOffset := new(big.Int)
+ bigIntOffset.SetUint64(shift)
+ return s.ShiftHashBigInt(hash, bigIntOffset)
+}
+
+// ShiftHashBigInt shift hash in big.Int
+func (s Storage) ShiftHashBigInt(hash common.Hash, shift *big.Int) common.Hash {
+ head := hash.Big()
+ head.Add(head, shift)
+ return common.BytesToHash(head.Bytes())
+}
+
+func getDByteSize(data common.Hash) uint64 {
+ bytes := data.Bytes()
+ lastByte := bytes[len(bytes)-1]
+ if lastByte&0x1 == 0 {
+ return uint64(lastByte / 2)
+ }
+ return new(big.Int).Div(new(big.Int).Sub(
+ data.Big(), big.NewInt(1)), big.NewInt(2)).Uint64()
+}
+
+// DecodeDByteBySlot given contract address and slot return the dynamic bytes data.
+func (s Storage) DecodeDByteBySlot(address common.Address, slot common.Hash) []byte {
+ data := s.GetState(address, slot)
+ length := getDByteSize(data)
+ if length < 32 {
+ return data[:length]
+ }
+ ptr := crypto.Keccak256Hash(slot.Bytes())
+ slotNum := (length-1)/32 + 1
+ rVal := make([]byte, slotNum*32)
+ for i := uint64(0); i < slotNum; i++ {
+ start := i * 32
+ copy(rVal[start:start+32], s.GetState(address, ptr).Bytes())
+ ptr = s.ShiftHashUint64(ptr, 1)
+ }
+ return rVal[:length]
+}
diff --git a/core/vm/sqlvm/common/storage_test.go b/core/vm/sqlvm/common/storage_test.go
new file mode 100644
index 000000000..42b8c2298
--- /dev/null
+++ b/core/vm/sqlvm/common/storage_test.go
@@ -0,0 +1,107 @@
+package common
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+ "golang.org/x/crypto/sha3"
+
+ "github.com/dexon-foundation/dexon/common"
+ "github.com/dexon-foundation/dexon/core/state"
+ "github.com/dexon-foundation/dexon/crypto"
+ "github.com/dexon-foundation/dexon/ethdb"
+ "github.com/dexon-foundation/dexon/rlp"
+)
+
+type StorageTestSuite struct{ suite.Suite }
+
+func (s *StorageTestSuite) TestGetPrimaryKeyHash() {
+ id := uint64(555666)
+ table := []byte("TABLE_A")
+ key := [][]byte{
+ []byte("tables"),
+ table,
+ []byte("primary"),
+ convertIDtoBytes(id),
+ }
+ hw := sha3.NewLegacyKeccak256()
+ rlp.Encode(hw, key)
+ bytes := hw.Sum(nil)
+ storage := Storage{}
+ result := storage.GetPrimaryKeyHash(table, id)
+ s.Require().Equal(bytes, result[:])
+}
+
+type decodeTestCase struct {
+ name string
+ slotData common.Hash
+ result []byte
+}
+
+func (s *StorageTestSuite) TestDecodeDByte() {
+ db := ethdb.NewMemDatabase()
+ state, _ := state.New(common.Hash{}, state.NewDatabase(db))
+ storage := NewStorage(state)
+ address := common.BytesToAddress([]byte("123"))
+ head := common.HexToHash("0x5566")
+ testcase := []decodeTestCase{
+ {
+ name: "small size",
+ slotData: common.HexToHash("0x48656c6c6f2c20776f726c64210000000000000000000000000000000000001a"),
+ result: common.FromHex("0x48656c6c6f2c20776f726c6421"),
+ },
+ {
+ name: "32 byte case",
+ slotData: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000041"),
+ result: []byte("Hello world. Hello DEXON, SQLVM."),
+ },
+ {
+ name: "large size",
+ slotData: common.HexToHash("0x000000000000000000000000000000000000000000000000000000000000047D"),
+ result: []byte("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."),
+ },
+ {
+ name: "empty",
+ slotData: common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"),
+ result: []byte(""),
+ },
+ }
+ SetDataToStateDB(head, storage, address, testcase)
+ for i, t := range testcase {
+ slot := storage.ShiftHashUint64(head, uint64(i))
+ result := storage.DecodeDByteBySlot(address, slot)
+ s.Require().Truef(bytes.Equal(result, t.result), fmt.Sprintf("name %v", t.name))
+ }
+}
+
+func SetDataToStateDB(head common.Hash, storage Storage, addr common.Address,
+ testcase []decodeTestCase) {
+ for i, t := range testcase {
+ slot := storage.ShiftHashUint64(head, uint64(i))
+ storage.SetState(addr, slot, t.slotData)
+ b := t.slotData.Bytes()
+ if b[len(b)-1]&0x1 != 0 {
+ length := len(t.result)
+ slotNum := (length-1)/32 + 1
+ ptr := crypto.Keccak256Hash(slot.Bytes())
+ for s := 0; s < slotNum; s++ {
+ start := s * 32
+ end := (s + 1) * 32
+ if end > len(t.result) {
+ end = len(t.result)
+ }
+ hash := common.Hash{}
+ copy(hash[:], t.result[start:end])
+ storage.SetState(addr, ptr, hash)
+ ptr = storage.ShiftHashUint64(ptr, 1)
+ }
+ }
+ }
+ storage.Commit(false)
+}
+
+func TestStorage(t *testing.T) {
+ suite.Run(t, new(StorageTestSuite))
+}