aboutsummaryrefslogtreecommitdiffstats
path: root/core/vm/sqlvm/common/storage.go
blob: 674efbf17b72ff3ea5a735be555721c056a0e406 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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 < common.HashLength {
        return data[:length]
    }
    ptr := crypto.Keccak256Hash(slot.Bytes())
    slotNum := (length-1)/common.HashLength + 1
    rVal := make([]byte, slotNum*common.HashLength)
    for i := uint64(0); i < slotNum; i++ {
        start := i * common.HashLength
        copy(rVal[start:start+common.HashLength], s.GetState(address, ptr).Bytes())
        ptr = s.ShiftHashUint64(ptr, 1)
    }
    return rVal[:length]
}