forked from AplaProject/go-apla
-
Notifications
You must be signed in to change notification settings - Fork 35
/
limits.go
283 lines (250 loc) · 7.88 KB
/
limits.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
// Copyright 2016 The go-daylight Authors
// This file is part of the go-daylight library.
//
// The go-daylight library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-daylight library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-daylight library. If not, see <http://www.gnu.org/licenses/>.
package block
import (
"errors"
"fmt"
"time"
"github.com/GenesisCommunity/go-genesis/packages/conf/syspar"
"github.com/GenesisCommunity/go-genesis/packages/consts"
"github.com/GenesisCommunity/go-genesis/packages/converter"
"github.com/GenesisCommunity/go-genesis/packages/model"
"github.com/GenesisCommunity/go-genesis/packages/script"
"github.com/GenesisCommunity/go-genesis/packages/transaction"
log "github.com/sirupsen/logrus"
)
const (
letPreprocess = 0x0001 // checking before generating block
letGenBlock = 0x0002 // checking during generating block
letParsing = 0x0004 // common checking during parsing block
)
// Limits is used for saving current limit information
type Limits struct {
Mode int
Block *Block // it equals nil if checking before generatin block
Limiters []Limiter // the list of limiters
}
// Limiter describes interface functions for limits
type Limiter interface {
init(*Block)
check(*transaction.Transaction, int) error
}
type limiterModes struct {
limiter Limiter
modes int // combination of letPreprocess letGenBlock letParsing
}
var (
// ErrLimitSkip returns when tx should be skipped during generating block
ErrLimitSkip = errors.New(`skip tx`)
// ErrLimitStop returns when the generation of the block should be stopped
ErrLimitStop = errors.New(`stop generating block`)
// ErrLimitTime returns when the time limit exceeded
ErrLimitTime = errors.New(`Time limit exceeded`)
)
// NewLimits initializes Limits structure.
func NewLimits(b *Block) (limits *Limits) {
limits = &Limits{Block: b, Limiters: make([]Limiter, 0, 8)}
if b == nil {
limits.Mode = letPreprocess
} else if b.GenBlock {
limits.Mode = letGenBlock
} else {
limits.Mode = letParsing
}
allLimiters := []limiterModes{
{&txMaxSize{}, letPreprocess | letParsing},
{&txUserLimit{}, letPreprocess | letParsing},
{&txMaxLimit{}, letPreprocess | letParsing},
{&txUserEcosysLimit{}, letPreprocess | letParsing},
{&timeBlockLimit{}, letGenBlock},
{&txMaxFuel{}, letGenBlock | letParsing},
}
for _, limiter := range allLimiters {
if limiter.modes&limits.Mode == 0 {
continue
}
limiter.limiter.init(b)
limits.Limiters = append(limits.Limiters, limiter.limiter)
}
return
}
// CheckLimit calls each limiter
func (limits *Limits) CheckLimit(t *transaction.Transaction) error {
for _, limiter := range limits.Limiters {
if err := limiter.check(t, limits.Mode); err != nil {
return err
}
}
return nil
}
func limitError(limitName, msg string, args ...interface{}) error {
err := fmt.Errorf(msg, args...)
log.WithFields(log.Fields{"type": consts.BlockError, "error": err}).Error(limitName)
return script.SetVMError(`panic`, err)
}
// Checking the max tx in the block
type txMaxLimit struct {
Count int // the current count
Limit int // max count of tx in the block
}
func (bl *txMaxLimit) init(b *Block) {
bl.Limit = syspar.GetMaxTxCount()
}
func (bl *txMaxLimit) check(t *transaction.Transaction, mode int) error {
bl.Count++
if bl.Count > bl.Limit {
if mode == letPreprocess {
return ErrLimitStop
}
return limitError(`txMaxLimit`, `Max tx in the block`)
}
return nil
}
// Checking the time of the start of generating block
type timeBlockLimit struct {
Start time.Time // the time of the start of generating block
Limit time.Duration // the maximum time
}
func (bl *timeBlockLimit) init(b *Block) {
bl.Start = time.Now()
bl.Limit = time.Millisecond * time.Duration(syspar.GetMaxBlockGenerationTime())
}
func (bl *timeBlockLimit) check(t *transaction.Transaction, mode int) error {
if time.Since(bl.Start) < bl.Limit {
return nil
}
if mode == letGenBlock {
return ErrLimitStop
}
return limitError("txBlockTimeLimit", "Block generation time exceeded")
}
// Checking the max tx from one user in the block
type txUserLimit struct {
TxUsers map[int64]int // the counter of tx from one user
Limit int // the value of max tx from one user
}
func (bl *txUserLimit) init(b *Block) {
bl.TxUsers = make(map[int64]int)
bl.Limit = syspar.GetMaxBlockUserTx()
}
func (bl *txUserLimit) check(t *transaction.Transaction, mode int) error {
var (
count int
ok bool
)
keyID := t.TxSmart.KeyID
if count, ok = bl.TxUsers[keyID]; ok {
if count+1 > bl.Limit {
if mode == letPreprocess {
return ErrLimitSkip
}
return limitError(`txUserLimit`, `Max tx from one user %d`, keyID)
}
}
bl.TxUsers[keyID] = count + 1
return nil
}
// Checking the max tx from one user in the ecosystem contracts
type ecosysLimit struct {
TxUsers map[int64]int // the counter of tx from one user in the ecosystem
Limit int // the value of max tx from one user in the ecosystem
}
type txUserEcosysLimit struct {
TxEcosys map[int64]ecosysLimit // the counter of tx from one user in ecosystems
}
func (bl *txUserEcosysLimit) init(b *Block) {
bl.TxEcosys = make(map[int64]ecosysLimit)
}
func (bl *txUserEcosysLimit) check(t *transaction.Transaction, mode int) error {
keyID := t.TxSmart.KeyID
ecosystemID := t.TxSmart.EcosystemID
if val, ok := bl.TxEcosys[ecosystemID]; ok {
if user, ok := val.TxUsers[keyID]; ok {
if user+1 > val.Limit {
if mode == letPreprocess {
return ErrLimitSkip
}
return limitError(`txUserEcosysLimit`, `Max tx from one user %d in ecosystem %d`,
keyID, ecosystemID)
}
val.TxUsers[keyID] = user + 1
} else {
val.TxUsers[keyID] = 1
}
} else {
limit := syspar.GetMaxBlockUserTx()
sp := &model.StateParameter{}
sp.SetTablePrefix(converter.Int64ToStr(ecosystemID))
found, err := sp.Get(t.DbTransaction, `max_block_user_tx`)
if err != nil {
return limitError(`txUserEcosysLimit`, err.Error())
}
if found {
limit = converter.StrToInt(sp.Value)
}
bl.TxEcosys[ecosystemID] = ecosysLimit{TxUsers: make(map[int64]int), Limit: limit}
bl.TxEcosys[ecosystemID].TxUsers[keyID] = 1
}
return nil
}
// Checking the max tx & block size
type txMaxSize struct {
Size int64 // the current size of the block
LimitBlock int64 // max size of the block
LimitTx int64 // max size of tx
}
func (bl *txMaxSize) init(b *Block) {
bl.LimitBlock = syspar.GetMaxBlockSize()
bl.LimitTx = syspar.GetMaxTxSize()
}
func (bl *txMaxSize) check(t *transaction.Transaction, mode int) error {
size := int64(len(t.TxFullData))
if size > bl.LimitTx {
return limitError(`txMaxSize`, `Max size of tx`)
}
bl.Size += size
if bl.Size > bl.LimitBlock {
if mode == letPreprocess {
return ErrLimitStop
}
return limitError(`txMaxSize`, `Max size of the block`)
}
return nil
}
// Checking the max tx & block size
type txMaxFuel struct {
Fuel int64 // the current fuel of the block
LimitBlock int64 // max fuel of the block
LimitTx int64 // max fuel of tx
}
func (bl *txMaxFuel) init(b *Block) {
bl.LimitBlock = syspar.GetMaxBlockFuel()
bl.LimitTx = syspar.GetMaxTxFuel()
}
func (bl *txMaxFuel) check(t *transaction.Transaction, mode int) error {
fuel := t.TxFuel
if fuel > bl.LimitTx {
return limitError(`txMaxFuel`, `Max fuel of tx %d > %d`, fuel, bl.LimitTx)
}
bl.Fuel += fuel
if bl.Fuel > bl.LimitBlock {
if mode == letGenBlock {
return ErrLimitStop
}
return limitError(`txMaxFuel`, `Max fuel of the block`)
}
return nil
}