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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
|
package common
import (
"github.com/dexon-foundation/dexon/common"
"github.com/dexon-foundation/dexon/core/vm/sqlvm/schema"
)
// Owner, writer operations of Storage.
// LoadOwner load the owner of a SQLVM contract from storage.
func (s *Storage) LoadOwner(contract common.Address) common.Address {
return hashToAddress(s.GetState(contract, s.getOwnerPathHash()))
}
// StoreOwner save the owner of a SQLVM contract to storage.
func (s *Storage) StoreOwner(contract, newOwner common.Address) {
s.SetState(contract, s.getOwnerPathHash(), addressToHash(newOwner))
}
type tableWriters struct {
Length uint64
// 3 unused uint64 in slot 1.
Writers []common.Address // Each address consumes one slot, right aligned.
}
type tableWriterRevIdx struct {
IndexToValuesOffset uint64
// 3 unused uint64 in the slot.
}
func (c *tableWriterRevIdx) Valid() bool {
return c.IndexToValuesOffset != 0
}
func (s *Storage) loadTableWriterRevIdx(
contract common.Address,
path common.Hash,
) *tableWriterRevIdx {
ret := &tableWriterRevIdx{}
data := s.GetState(contract, path)
ret.IndexToValuesOffset = bytesToUint64(data[:8])
return ret
}
func (s *Storage) storeTableWriterRevIdx(
contract common.Address,
path common.Hash,
rev *tableWriterRevIdx,
) {
var data common.Hash // One slot.
copy(data[:8], uint64ToBytes(rev.IndexToValuesOffset))
s.SetState(contract, path, data)
}
func (s *Storage) loadTableWriters(
contract common.Address,
pathHash common.Hash,
onlyHeader bool,
) *tableWriters {
ret := &tableWriters{}
header := s.GetState(contract, pathHash)
ret.Length = bytesToUint64(header[:8])
if onlyHeader {
return ret
}
ret.Writers = make([]common.Address, ret.Length)
for i := uint64(0); i < ret.Length; i++ {
ret.Writers[i] = s.loadSingleTableWriter(contract, pathHash, i)
}
return ret
}
func (s *Storage) storeTableWritersHeader(
contract common.Address,
pathHash common.Hash,
w *tableWriters,
) {
var header common.Hash
copy(header[:8], uint64ToBytes(w.Length))
s.SetState(contract, pathHash, header)
}
func (s *Storage) shiftTableWriterList(
base common.Hash,
idx uint64,
) common.Hash {
return s.ShiftHashListEntry(base, 1, 1, idx)
}
func (s *Storage) loadSingleTableWriter(
contract common.Address,
writersPathHash common.Hash,
idx uint64,
) common.Address {
slot := s.shiftTableWriterList(writersPathHash, idx)
acc := s.GetState(contract, slot)
return hashToAddress(acc)
}
func (s *Storage) storeSingleTableWriter(
contract common.Address,
writersPathHash common.Hash,
idx uint64,
acc common.Address,
) {
slot := s.shiftTableWriterList(writersPathHash, idx)
s.SetState(contract, slot, addressToHash(acc))
}
// IsTableWriter check if an account is writer to the table.
func (s *Storage) IsTableWriter(
contract common.Address,
tableRef schema.TableRef,
account common.Address,
) bool {
path := s.getTableWriterRevIdxPathHash(tableRef, account)
rev := s.loadTableWriterRevIdx(contract, path)
return rev.Valid()
}
// LoadTableWriters load writers of a table.
func (s *Storage) LoadTableWriters(
contract common.Address,
tableRef schema.TableRef,
) (ret []common.Address) {
path := s.getTableWritersPathHash(tableRef)
writers := s.loadTableWriters(contract, path, false)
return writers.Writers
}
// InsertTableWriter insert an account into writer list of the table.
func (s *Storage) InsertTableWriter(
contract common.Address,
tableRef schema.TableRef,
account common.Address,
) {
revPath := s.getTableWriterRevIdxPathHash(tableRef, account)
rev := s.loadTableWriterRevIdx(contract, revPath)
if rev.Valid() {
return
}
path := s.getTableWritersPathHash(tableRef)
writers := s.loadTableWriters(contract, path, true)
// Store modification.
s.storeSingleTableWriter(contract, path, writers.Length, account)
writers.Length++
s.storeTableWritersHeader(contract, path, writers)
// Notice: IndexToValuesOffset starts from 1.
s.storeTableWriterRevIdx(contract, revPath, &tableWriterRevIdx{
IndexToValuesOffset: writers.Length,
})
}
// DeleteTableWriter delete an account from writer list of the table.
func (s *Storage) DeleteTableWriter(
contract common.Address,
tableRef schema.TableRef,
account common.Address,
) {
revPath := s.getTableWriterRevIdxPathHash(tableRef, account)
rev := s.loadTableWriterRevIdx(contract, revPath)
if !rev.Valid() {
return
}
path := s.getTableWritersPathHash(tableRef)
writers := s.loadTableWriters(contract, path, true)
// Store modification.
if rev.IndexToValuesOffset != writers.Length {
// Move last to deleted slot.
lastAcc := s.loadSingleTableWriter(contract, path, writers.Length-1)
s.storeSingleTableWriter(contract, path, rev.IndexToValuesOffset-1,
lastAcc)
s.storeTableWriterRevIdx(contract, s.getTableWriterRevIdxPathHash(
tableRef, lastAcc), rev)
}
// Delete last.
writers.Length--
s.storeTableWritersHeader(contract, path, writers)
s.storeSingleTableWriter(contract, path, writers.Length, common.Address{})
s.storeTableWriterRevIdx(contract, revPath, &tableWriterRevIdx{})
}
|