forked from hyperledger-archives/burrow
-
Notifications
You must be signed in to change notification settings - Fork 0
/
state.go
297 lines (270 loc) · 8.51 KB
/
state.go
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
// Copyright 2017 Monax Industries Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package state
import (
"crypto/sha256"
"fmt"
"sync"
"github.com/hyperledger/burrow/acm"
"github.com/hyperledger/burrow/acm/acmstate"
"github.com/hyperledger/burrow/acm/validator"
"github.com/hyperledger/burrow/binary"
"github.com/hyperledger/burrow/crypto"
"github.com/hyperledger/burrow/execution/exec"
"github.com/hyperledger/burrow/execution/names"
"github.com/hyperledger/burrow/execution/proposal"
"github.com/hyperledger/burrow/genesis"
"github.com/hyperledger/burrow/logging"
"github.com/hyperledger/burrow/permission"
"github.com/hyperledger/burrow/storage"
"github.com/hyperledger/burrow/txs"
dbm "github.com/tendermint/tendermint/libs/db"
)
const (
DefaultValidatorsWindowSize = 10
defaultCacheCapacity = 1024
uint64Length = 8
// Prefix under which the versioned merkle state tree resides - tracking previous versions of history
forestPrefix = "f"
)
// Implements account and blockchain state
var _ acmstate.IterableReader = &State{}
var _ names.IterableReader = &State{}
var _ Updatable = &writeState{}
type KeyFormatStore struct {
Account *storage.MustKeyFormat
Storage *storage.MustKeyFormat
Name *storage.MustKeyFormat
Proposal *storage.MustKeyFormat
Validator *storage.MustKeyFormat
Event *storage.MustKeyFormat
TxHash *storage.MustKeyFormat
}
var keys = KeyFormatStore{
// AccountAddress -> Account
Account: storage.NewMustKeyFormat("a", crypto.AddressLength),
// AccountAddress, Key -> Value
Storage: storage.NewMustKeyFormat("s", crypto.AddressLength, binary.Word256Length),
// Name -> Entry
Name: storage.NewMustKeyFormat("n", storage.VariadicSegmentLength),
// ProposalHash -> Proposal
Proposal: storage.NewMustKeyFormat("p", sha256.Size),
// ValidatorAddress -> Power
Validator: storage.NewMustKeyFormat("v", crypto.AddressLength),
// Height, EventIndex -> StreamEvent
Event: storage.NewMustKeyFormat("e", uint64Length, uint64Length),
// TxHash -> TxHeight, TxIndex
TxHash: storage.NewMustKeyFormat("th", txs.HashLength),
}
func init() {
err := storage.EnsureKeyFormatStore(keys)
if err != nil {
panic(fmt.Errorf("KeyFormatStore is invalid: %v", err))
}
}
type Updatable interface {
acmstate.Writer
names.Writer
proposal.Writer
validator.Writer
AddBlock(blockExecution *exec.BlockExecution) error
}
// Wraps state to give access to writer methods
type writeState struct {
forest *storage.MutableForest
accountStats acmstate.AccountStats
ring *validator.Ring
}
type ReadState struct {
Forest storage.ForestReader
validator.History
}
// Writers to state are responsible for calling State.Lock() before calling
type State struct {
sync.Mutex
db dbm.DB
ReadState
writeState writeState
logger *logging.Logger
}
// Create a new State object
func NewState(db dbm.DB) *State {
forest, err := storage.NewMutableForest(storage.NewPrefixDB(db, forestPrefix), defaultCacheCapacity)
if err != nil {
// This should only happen if we have negative cache capacity, which for us is a positive compile-time constant
panic(fmt.Errorf("could not create new state because error creating MutableForest"))
}
ring := validator.NewRing(nil, DefaultValidatorsWindowSize)
rs := ReadState{Forest: forest, History: ring}
ws := writeState{forest: forest, ring: ring}
return &State{
db: db,
ReadState: rs,
writeState: ws,
logger: logging.NewNoopLogger(),
}
}
// Make genesis state from GenesisDoc and save to DB
func MakeGenesisState(db dbm.DB, genesisDoc *genesis.GenesisDoc) (*State, error) {
s := NewState(db)
const errHeader = "MakeGenesisState():"
// Make accounts state tree
for _, genAcc := range genesisDoc.Accounts {
perm := genAcc.Permissions
acc := &acm.Account{
Address: genAcc.Address,
Balance: genAcc.Amount,
Permissions: perm,
}
err := s.writeState.UpdateAccount(acc)
if err != nil {
return nil, fmt.Errorf("%s %v", errHeader, err)
}
}
// Make genesis validators
err := s.writeState.MakeGenesisValidators(genesisDoc)
if err != nil {
return nil, fmt.Errorf("%s %v", errHeader, err)
}
// global permissions are saved as the 0 address
// so they are included in the accounts tree
globalPerms := permission.DefaultAccountPermissions
globalPerms = genesisDoc.GlobalPermissions
// XXX: make sure the set bits are all true
// Without it the HasPermission() functions will fail
globalPerms.Base.SetBit = permission.AllPermFlags
permsAcc := &acm.Account{
Address: acm.GlobalPermissionsAddress,
Balance: 1337,
Permissions: globalPerms,
}
err = s.writeState.UpdateAccount(permsAcc)
if err != nil {
return nil, fmt.Errorf("%s %v", errHeader, err)
}
return s, nil
}
func (s *State) InitialCommit() error {
_, version, err := s.commit()
if err != nil {
return fmt.Errorf("could not save initial state: %v", err)
}
if version != VersionOffset {
return fmt.Errorf("initial state got version %d after committing genesis state but version offset should be %d",
version, VersionOffset)
}
return nil
}
// Tries to load the execution state from DB, returns nil with no error if no state found
func LoadState(db dbm.DB, version int64) (*State, error) {
s := NewState(db)
err := s.writeState.forest.Load(version)
if err != nil {
return nil, fmt.Errorf("could not load MutableForest at version %d: %v", version, err)
}
// Populate stats. If this starts taking too long, store the value rather than the full scan at startup
err = s.IterateAccounts(func(acc *acm.Account) error {
if len(acc.Code) > 0 {
s.writeState.accountStats.AccountsWithCode++
} else {
s.writeState.accountStats.AccountsWithoutCode++
}
return nil
})
if err != nil {
return nil, err
}
// load the validator ring
ring, err := LoadValidatorRing(version, DefaultValidatorsWindowSize, s.writeState.forest.GetImmutable)
if err != nil {
return nil, err
}
s.writeState.ring = ring
s.ReadState.History = ring
return s, nil
}
func (s *State) Version() int64 {
return s.writeState.forest.Version()
}
func (s *State) Hash() []byte {
return s.writeState.forest.Hash()
}
func (s *State) LoadHeight(height uint64) (*ReadState, error) {
version := VersionAtHeight(height)
forest, err := s.writeState.forest.GetImmutable(version)
if err != nil {
return nil, err
}
ring, err := LoadValidatorRing(version, DefaultValidatorsWindowSize, s.writeState.forest.GetImmutable)
if err != nil {
return nil, err
}
return &ReadState{
Forest: forest,
History: ring,
}, nil
}
// Perform updates to state whilst holding the write lock, allows a commit to hold the write lock across multiple
// operations while preventing interlaced reads and writes
func (s *State) Update(updater func(up Updatable) error) ([]byte, int64, error) {
s.Lock()
defer s.Unlock()
err := updater(&s.writeState)
if err != nil {
return nil, 0, err
}
return s.commit()
}
func (s *State) commit() ([]byte, int64, error) {
// save state at a new version may still be orphaned before we save the version against the hash
hash, version, err := s.writeState.forest.Save()
if err != nil {
return nil, 0, err
}
totalPowerChange, totalFlow, err := s.writeState.ring.Rotate()
if err != nil {
return nil, 0, err
}
if totalFlow.Sign() != 0 {
//noinspection ALL
s.logger.InfoMsg("validator set changes", "total_power_change", totalPowerChange, "total_flow", totalFlow)
}
return hash, version, err
}
// Creates a copy of the database to the supplied db
func (s *State) Copy(db dbm.DB) (*State, error) {
stateCopy := NewState(db)
err := s.writeState.forest.IterateRWTree(nil, nil, true,
func(prefix []byte, tree *storage.RWTree) error {
treeCopy, err := stateCopy.writeState.forest.Writer(prefix)
if err != nil {
return err
}
return tree.IterateWriteTree(nil, nil, true, func(key []byte, value []byte) error {
treeCopy.Set(key, value)
return nil
})
})
if err != nil {
return nil, err
}
_, _, err = stateCopy.commit()
if err != nil {
return nil, err
}
return stateCopy, nil
}
func (s *State) SetLogger(logger *logging.Logger) {
s.logger = logger
}