Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
whqwjb
go-ethereum
提交
a183ea29
G
go-ethereum
项目概览
whqwjb
/
go-ethereum
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
go-ethereum
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
a183ea29
编写于
8月 17, 2016
作者:
P
Péter Szilágyi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
core: add upper bound on the queued transctions
上级
affffb39
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
203 addition
and
33 deletion
+203
-33
core/tx_list.go
core/tx_list.go
+10
-10
core/tx_pool.go
core/tx_pool.go
+99
-9
core/tx_pool_test.go
core/tx_pool_test.go
+94
-14
未找到文件。
core/tx_list.go
浏览文件 @
a183ea29
...
@@ -52,11 +52,11 @@ func (h *nonceHeap) Pop() interface{} {
...
@@ -52,11 +52,11 @@ func (h *nonceHeap) Pop() interface{} {
type
txList
struct
{
type
txList
struct
{
strict
bool
// Whether nonces are strictly continuous or not
strict
bool
// Whether nonces are strictly continuous or not
items
map
[
uint64
]
*
types
.
Transaction
// Hash map storing the transaction data
items
map
[
uint64
]
*
types
.
Transaction
// Hash map storing the transaction data
cache
types
.
Transactions
//
c
ache of the transactions already sorted
cache
types
.
Transactions
//
C
ache of the transactions already sorted
first
uint64
// Nonce of the lowest stored transaction (strict mode)
first
uint64
// Nonce of the lowest stored transaction (strict mode)
last
uint64
// Nonce of the highest stored transaction (strict mode)
last
uint64
// Nonce of the highest stored transaction (strict mode)
index
*
nonceHeap
// Heap of nonces of all t
eh
stored transactions (non-strict mode)
index
*
nonceHeap
// Heap of nonces of all t
he
stored transactions (non-strict mode)
costcap
*
big
.
Int
// Price of the highest costing transaction (reset only if exceeds balance)
costcap
*
big
.
Int
// Price of the highest costing transaction (reset only if exceeds balance)
}
}
...
@@ -73,8 +73,8 @@ func newTxList(strict bool) *txList {
...
@@ -73,8 +73,8 @@ func newTxList(strict bool) *txList {
}
}
}
}
// Add tries to insert
s
a new transaction into the list, returning whether the
// Add tries to insert a new transaction into the list, returning whether the
// transaction was acceped, and if yes, any previous transaction it replaced.
// transaction was accep
t
ed, and if yes, any previous transaction it replaced.
//
//
// In case of strict lists (contiguous nonces) the nonce boundaries are updated
// In case of strict lists (contiguous nonces) the nonce boundaries are updated
// appropriately with the new transaction. Otherwise (gapped nonces) the heap of
// appropriately with the new transaction. Otherwise (gapped nonces) the heap of
...
@@ -146,10 +146,10 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
...
@@ -146,10 +146,10 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
//
//
// This method uses the cached costcap to quickly decide if there's even a point
// This method uses the cached costcap to quickly decide if there's even a point
// in calculating all the costs or if the balance covers all. If the threshold is
// in calculating all the costs or if the balance covers all. If the threshold is
// lo
ew
r than the costcap, the costcap will be reset to a new high after removing
// lo
we
r than the costcap, the costcap will be reset to a new high after removing
// expensive the too transactions.
// expensive the too transactions.
func
(
l
*
txList
)
Filter
(
threshold
*
big
.
Int
)
(
types
.
Transactions
,
types
.
Transactions
)
{
func
(
l
*
txList
)
Filter
(
threshold
*
big
.
Int
)
(
types
.
Transactions
,
types
.
Transactions
)
{
// If all transactions are blow the threshold, short circuit
// If all transactions are b
e
low the threshold, short circuit
if
l
.
costcap
.
Cmp
(
threshold
)
<=
0
{
if
l
.
costcap
.
Cmp
(
threshold
)
<=
0
{
return
nil
,
nil
return
nil
,
nil
}
}
...
@@ -195,7 +195,7 @@ func (l *txList) Filter(threshold *big.Int) (types.Transactions, types.Transacti
...
@@ -195,7 +195,7 @@ func (l *txList) Filter(threshold *big.Int) (types.Transactions, types.Transacti
}
}
// Cap places a hard limit on the number of items, returning all transactions
// Cap places a hard limit on the number of items, returning all transactions
// exceeding tht limit.
// exceeding th
a
t limit.
func
(
l
*
txList
)
Cap
(
threshold
int
)
types
.
Transactions
{
func
(
l
*
txList
)
Cap
(
threshold
int
)
types
.
Transactions
{
// Short circuit if the number of items is under the limit
// Short circuit if the number of items is under the limit
if
len
(
l
.
items
)
<
threshold
{
if
len
(
l
.
items
)
<
threshold
{
...
@@ -239,8 +239,9 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
...
@@ -239,8 +239,9 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
l
.
cache
=
nil
l
.
cache
=
nil
// Remove all invalidated transactions (strict mode only!)
// Remove all invalidated transactions (strict mode only!)
invalids
:=
make
(
types
.
Transactions
,
0
,
l
.
last
-
nonce
)
var
invalids
types
.
Transactions
if
l
.
strict
{
if
l
.
strict
{
invalids
=
make
(
types
.
Transactions
,
0
,
l
.
last
-
nonce
)
for
i
:=
nonce
+
1
;
i
<=
l
.
last
;
i
++
{
for
i
:=
nonce
+
1
;
i
<=
l
.
last
;
i
++
{
invalids
=
append
(
invalids
,
l
.
items
[
i
])
invalids
=
append
(
invalids
,
l
.
items
[
i
])
delete
(
l
.
items
,
i
)
delete
(
l
.
items
,
i
)
...
@@ -255,7 +256,6 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
...
@@ -255,7 +256,6 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
}
}
}
}
}
}
// Figure out the new highest nonce
return
true
,
invalids
return
true
,
invalids
}
}
return
false
,
nil
return
false
,
nil
...
@@ -265,7 +265,7 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
...
@@ -265,7 +265,7 @@ func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
// provided nonce that is ready for processing. The returned transactions will be
// provided nonce that is ready for processing. The returned transactions will be
// removed from the list.
// removed from the list.
//
//
// Note, all transactions with nonces lower tha
t
start will also be returned to
// Note, all transactions with nonces lower tha
n
start will also be returned to
// prevent getting into and invalid state. This is not something that should ever
// prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing!
// happen but better to be self correcting than failing!
func
(
l
*
txList
)
Ready
(
start
uint64
)
types
.
Transactions
{
func
(
l
*
txList
)
Ready
(
start
uint64
)
types
.
Transactions
{
...
...
core/tx_pool.go
浏览文件 @
a183ea29
...
@@ -20,6 +20,7 @@ import (
...
@@ -20,6 +20,7 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"math/big"
"math/big"
"sort"
"sync"
"sync"
"time"
"time"
...
@@ -44,8 +45,11 @@ var (
...
@@ -44,8 +45,11 @@ var (
ErrNegativeValue
=
errors
.
New
(
"Negative value"
)
ErrNegativeValue
=
errors
.
New
(
"Negative value"
)
)
)
const
(
var
(
maxQueued
=
64
// max limit of queued txs per address
maxQueuedPerAccount
=
uint64
(
64
)
// Max limit of queued transactions per address
maxQueuedInTotal
=
uint64
(
65536
)
// Max limit of queued transactions from all accounts
maxQueuedLifetime
=
3
*
time
.
Hour
// Max amount of time transactions from idle accounts are queued
evictionInterval
=
time
.
Minute
// Time interval to check for evictable transactions
)
)
type
stateFn
func
()
(
*
state
.
StateDB
,
error
)
type
stateFn
func
()
(
*
state
.
StateDB
,
error
)
...
@@ -71,8 +75,10 @@ type TxPool struct {
...
@@ -71,8 +75,10 @@ type TxPool struct {
pending
map
[
common
.
Address
]
*
txList
// All currently processable transactions
pending
map
[
common
.
Address
]
*
txList
// All currently processable transactions
queue
map
[
common
.
Address
]
*
txList
// Queued but non-processable transactions
queue
map
[
common
.
Address
]
*
txList
// Queued but non-processable transactions
all
map
[
common
.
Hash
]
*
types
.
Transaction
// All transactions to allow lookups
all
map
[
common
.
Hash
]
*
types
.
Transaction
// All transactions to allow lookups
beats
map
[
common
.
Address
]
time
.
Time
// Last heartbeat from each known account
wg
sync
.
WaitGroup
// for shutdown sync
wg
sync
.
WaitGroup
// for shutdown sync
quit
chan
struct
{}
homestead
bool
homestead
bool
}
}
...
@@ -83,6 +89,7 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat
...
@@ -83,6 +89,7 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat
pending
:
make
(
map
[
common
.
Address
]
*
txList
),
pending
:
make
(
map
[
common
.
Address
]
*
txList
),
queue
:
make
(
map
[
common
.
Address
]
*
txList
),
queue
:
make
(
map
[
common
.
Address
]
*
txList
),
all
:
make
(
map
[
common
.
Hash
]
*
types
.
Transaction
),
all
:
make
(
map
[
common
.
Hash
]
*
types
.
Transaction
),
beats
:
make
(
map
[
common
.
Address
]
time
.
Time
),
eventMux
:
eventMux
,
eventMux
:
eventMux
,
currentState
:
currentStateFn
,
currentState
:
currentStateFn
,
gasLimit
:
gasLimitFn
,
gasLimit
:
gasLimitFn
,
...
@@ -90,10 +97,12 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat
...
@@ -90,10 +97,12 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat
pendingState
:
nil
,
pendingState
:
nil
,
localTx
:
newTxSet
(),
localTx
:
newTxSet
(),
events
:
eventMux
.
Subscribe
(
ChainHeadEvent
{},
GasPriceChanged
{},
RemovedTransactionEvent
{}),
events
:
eventMux
.
Subscribe
(
ChainHeadEvent
{},
GasPriceChanged
{},
RemovedTransactionEvent
{}),
quit
:
make
(
chan
struct
{}),
}
}
pool
.
wg
.
Add
(
1
)
pool
.
wg
.
Add
(
2
)
go
pool
.
eventLoop
()
go
pool
.
eventLoop
()
go
pool
.
expirationLoop
()
return
pool
return
pool
}
}
...
@@ -154,6 +163,7 @@ func (pool *TxPool) resetState() {
...
@@ -154,6 +163,7 @@ func (pool *TxPool) resetState() {
func
(
pool
*
TxPool
)
Stop
()
{
func
(
pool
*
TxPool
)
Stop
()
{
pool
.
events
.
Unsubscribe
()
pool
.
events
.
Unsubscribe
()
close
(
pool
.
quit
)
pool
.
wg
.
Wait
()
pool
.
wg
.
Wait
()
glog
.
V
(
logger
.
Info
)
.
Infoln
(
"Transaction pool stopped"
)
glog
.
V
(
logger
.
Info
)
.
Infoln
(
"Transaction pool stopped"
)
}
}
...
@@ -290,7 +300,7 @@ func (pool *TxPool) add(tx *types.Transaction) error {
...
@@ -290,7 +300,7 @@ func (pool *TxPool) add(tx *types.Transaction) error {
if
pool
.
all
[
hash
]
!=
nil
{
if
pool
.
all
[
hash
]
!=
nil
{
return
fmt
.
Errorf
(
"Known transaction: %x"
,
hash
[
:
4
])
return
fmt
.
Errorf
(
"Known transaction: %x"
,
hash
[
:
4
])
}
}
// Otherwise ensure basic validation passes nd queue it up
// Otherwise ensure basic validation passes
a
nd queue it up
if
err
:=
pool
.
validateTx
(
tx
);
err
!=
nil
{
if
err
:=
pool
.
validateTx
(
tx
);
err
!=
nil
{
return
err
return
err
}
}
...
@@ -308,7 +318,7 @@ func (pool *TxPool) add(tx *types.Transaction) error {
...
@@ -308,7 +318,7 @@ func (pool *TxPool) add(tx *types.Transaction) error {
return
nil
return
nil
}
}
// enqueueTx inserts a new transction into the non-executable transaction queue.
// enqueueTx inserts a new trans
a
ction into the non-executable transaction queue.
//
//
// Note, this method assumes the pool lock is held!
// Note, this method assumes the pool lock is held!
func
(
pool
*
TxPool
)
enqueueTx
(
hash
common
.
Hash
,
tx
*
types
.
Transaction
)
{
func
(
pool
*
TxPool
)
enqueueTx
(
hash
common
.
Hash
,
tx
*
types
.
Transaction
)
{
...
@@ -355,6 +365,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
...
@@ -355,6 +365,7 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
pool
.
all
[
hash
]
=
tx
// Failsafe to work around direct pending inserts (tests)
pool
.
all
[
hash
]
=
tx
// Failsafe to work around direct pending inserts (tests)
// Set the potentially new pending nonce and notify any subsystems of the new tx
// Set the potentially new pending nonce and notify any subsystems of the new tx
pool
.
beats
[
addr
]
=
time
.
Now
()
pool
.
pendingState
.
SetNonce
(
addr
,
list
.
last
+
1
)
pool
.
pendingState
.
SetNonce
(
addr
,
list
.
last
+
1
)
go
pool
.
eventMux
.
Post
(
TxPreEvent
{
tx
})
go
pool
.
eventMux
.
Post
(
TxPreEvent
{
tx
})
}
}
...
@@ -412,8 +423,8 @@ func (pool *TxPool) RemoveBatch(txs types.Transactions) {
...
@@ -412,8 +423,8 @@ func (pool *TxPool) RemoveBatch(txs types.Transactions) {
}
}
}
}
// removeTx
iterates removes a single transaction from the queue, moving all
// removeTx
removes a single transaction from the queue, moving all subsequent
//
subsequent
transactions back to the future queue.
// transactions back to the future queue.
func
(
pool
*
TxPool
)
removeTx
(
hash
common
.
Hash
)
{
func
(
pool
*
TxPool
)
removeTx
(
hash
common
.
Hash
)
{
// Fetch the transaction we wish to delete
// Fetch the transaction we wish to delete
tx
,
ok
:=
pool
.
all
[
hash
]
tx
,
ok
:=
pool
.
all
[
hash
]
...
@@ -431,6 +442,8 @@ func (pool *TxPool) removeTx(hash common.Hash) {
...
@@ -431,6 +442,8 @@ func (pool *TxPool) removeTx(hash common.Hash) {
// If no more transactions are left, remove the list and reset the nonce
// If no more transactions are left, remove the list and reset the nonce
if
pending
.
Empty
()
{
if
pending
.
Empty
()
{
delete
(
pool
.
pending
,
addr
)
delete
(
pool
.
pending
,
addr
)
delete
(
pool
.
beats
,
addr
)
pool
.
pendingState
.
SetNonce
(
addr
,
tx
.
Nonce
())
pool
.
pendingState
.
SetNonce
(
addr
,
tx
.
Nonce
())
}
else
{
}
else
{
// Otherwise update the nonce and postpone any invalidated transactions
// Otherwise update the nonce and postpone any invalidated transactions
...
@@ -465,6 +478,8 @@ func (pool *TxPool) promoteExecutables() {
...
@@ -465,6 +478,8 @@ func (pool *TxPool) promoteExecutables() {
return
return
}
}
// Iterate over all accounts and promote any executable transactions
// Iterate over all accounts and promote any executable transactions
queued
:=
uint64
(
0
)
for
addr
,
list
:=
range
pool
.
queue
{
for
addr
,
list
:=
range
pool
.
queue
{
// Drop all transactions that are deemed too old (low nonce)
// Drop all transactions that are deemed too old (low nonce)
for
_
,
tx
:=
range
list
.
Forward
(
state
.
GetNonce
(
addr
))
{
for
_
,
tx
:=
range
list
.
Forward
(
state
.
GetNonce
(
addr
))
{
...
@@ -489,17 +504,51 @@ func (pool *TxPool) promoteExecutables() {
...
@@ -489,17 +504,51 @@ func (pool *TxPool) promoteExecutables() {
pool
.
promoteTx
(
addr
,
tx
.
Hash
(),
tx
)
pool
.
promoteTx
(
addr
,
tx
.
Hash
(),
tx
)
}
}
// Drop all transactions over the allowed limit
// Drop all transactions over the allowed limit
for
_
,
tx
:=
range
list
.
Cap
(
maxQueued
)
{
for
_
,
tx
:=
range
list
.
Cap
(
int
(
maxQueuedPerAccount
)
)
{
if
glog
.
V
(
logger
.
Core
)
{
if
glog
.
V
(
logger
.
Core
)
{
glog
.
Infof
(
"Removed cap-exceeding queued transaction: %v"
,
tx
)
glog
.
Infof
(
"Removed cap-exceeding queued transaction: %v"
,
tx
)
}
}
delete
(
pool
.
all
,
tx
.
Hash
())
delete
(
pool
.
all
,
tx
.
Hash
())
}
}
queued
+=
uint64
(
list
.
Len
())
// Delete the entire queue entry if it became empty.
// Delete the entire queue entry if it became empty.
if
list
.
Empty
()
{
if
list
.
Empty
()
{
delete
(
pool
.
queue
,
addr
)
delete
(
pool
.
queue
,
addr
)
}
}
}
}
// If we've queued more transactions than the hard limit, drop oldest ones
if
queued
>
maxQueuedInTotal
{
// Sort all accounts with queued transactions by heartbeat
addresses
:=
make
(
addresssByHeartbeat
,
0
,
len
(
pool
.
queue
))
for
addr
,
_
:=
range
pool
.
queue
{
addresses
=
append
(
addresses
,
addressByHeartbeat
{
addr
,
pool
.
beats
[
addr
]})
}
sort
.
Sort
(
addresses
)
// Drop transactions until the total is below the limit
for
drop
:=
queued
-
maxQueuedInTotal
;
drop
>
0
;
{
addr
:=
addresses
[
len
(
addresses
)
-
1
]
list
:=
pool
.
queue
[
addr
.
address
]
addresses
=
addresses
[
:
len
(
addresses
)
-
1
]
// Drop all transactions if they are less than the overflow
if
size
:=
uint64
(
list
.
Len
());
size
<=
drop
{
for
_
,
tx
:=
range
list
.
Flatten
()
{
pool
.
removeTx
(
tx
.
Hash
())
}
drop
-=
size
continue
}
// Otherwise drop only last few transactions
txs
:=
list
.
Flatten
()
for
i
:=
len
(
txs
)
-
1
;
i
>=
0
&&
drop
>
0
;
i
--
{
pool
.
removeTx
(
txs
[
i
]
.
Hash
())
drop
--
}
}
}
}
}
// demoteUnexecutables removes invalid and processed transactions from the pools
// demoteUnexecutables removes invalid and processed transactions from the pools
...
@@ -540,10 +589,51 @@ func (pool *TxPool) demoteUnexecutables() {
...
@@ -540,10 +589,51 @@ func (pool *TxPool) demoteUnexecutables() {
// Delete the entire queue entry if it became empty.
// Delete the entire queue entry if it became empty.
if
list
.
Empty
()
{
if
list
.
Empty
()
{
delete
(
pool
.
pending
,
addr
)
delete
(
pool
.
pending
,
addr
)
delete
(
pool
.
beats
,
addr
)
}
}
}
}
}
}
// expirationLoop is a loop that periodically iterates over all accounts with
// queued transactions and drop all that have been inactive for a prolonged amount
// of time.
func
(
pool
*
TxPool
)
expirationLoop
()
{
defer
pool
.
wg
.
Done
()
evict
:=
time
.
NewTicker
(
evictionInterval
)
defer
evict
.
Stop
()
for
{
select
{
case
<-
evict
.
C
:
pool
.
mu
.
Lock
()
for
addr
:=
range
pool
.
queue
{
if
time
.
Since
(
pool
.
beats
[
addr
])
>
maxQueuedLifetime
{
for
_
,
tx
:=
range
pool
.
queue
[
addr
]
.
Flatten
()
{
pool
.
removeTx
(
tx
.
Hash
())
}
}
}
pool
.
mu
.
Unlock
()
case
<-
pool
.
quit
:
return
}
}
}
// addressByHeartbeat is an account address tagged with its last activity timestamp.
type
addressByHeartbeat
struct
{
address
common
.
Address
heartbeat
time
.
Time
}
type
addresssByHeartbeat
[]
addressByHeartbeat
func
(
a
addresssByHeartbeat
)
Len
()
int
{
return
len
(
a
)
}
func
(
a
addresssByHeartbeat
)
Less
(
i
,
j
int
)
bool
{
return
a
[
i
]
.
heartbeat
.
Before
(
a
[
j
]
.
heartbeat
)
}
func
(
a
addresssByHeartbeat
)
Swap
(
i
,
j
int
)
{
a
[
i
],
a
[
j
]
=
a
[
j
],
a
[
i
]
}
// txSet represents a set of transaction hashes in which entries
// txSet represents a set of transaction hashes in which entries
// are automatically dropped after txSetDuration time
// are automatically dropped after txSetDuration time
type
txSet
struct
{
type
txSet
struct
{
...
...
core/tx_pool_test.go
浏览文件 @
a183ea29
...
@@ -19,7 +19,9 @@ package core
...
@@ -19,7 +19,9 @@ package core
import
(
import
(
"crypto/ecdsa"
"crypto/ecdsa"
"math/big"
"math/big"
"math/rand"
"testing"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state"
...
@@ -38,10 +40,10 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
...
@@ -38,10 +40,10 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
db
,
_
:=
ethdb
.
NewMemDatabase
()
db
,
_
:=
ethdb
.
NewMemDatabase
()
statedb
,
_
:=
state
.
New
(
common
.
Hash
{},
db
)
statedb
,
_
:=
state
.
New
(
common
.
Hash
{},
db
)
var
m
event
.
TypeMux
key
,
_
:=
crypto
.
GenerateKey
()
key
,
_
:=
crypto
.
GenerateKey
()
newPool
:=
NewTxPool
(
testChainConfig
(),
&
m
,
func
()
(
*
state
.
StateDB
,
error
)
{
return
statedb
,
nil
},
func
()
*
big
.
Int
{
return
big
.
NewInt
(
1000000
)
})
newPool
:=
NewTxPool
(
testChainConfig
(),
new
(
event
.
TypeMux
)
,
func
()
(
*
state
.
StateDB
,
error
)
{
return
statedb
,
nil
},
func
()
*
big
.
Int
{
return
big
.
NewInt
(
1000000
)
})
newPool
.
resetState
()
newPool
.
resetState
()
return
newPool
,
key
return
newPool
,
key
}
}
...
@@ -438,7 +440,7 @@ func TestTransactionPostponing(t *testing.T) {
...
@@ -438,7 +440,7 @@ func TestTransactionPostponing(t *testing.T) {
// Tests that if the transaction count belonging to a single account goes above
// Tests that if the transaction count belonging to a single account goes above
// some threshold, the higher transactions are dropped to prevent DOS attacks.
// some threshold, the higher transactions are dropped to prevent DOS attacks.
func
TestTransactionQueueLimiting
(
t
*
testing
.
T
)
{
func
TestTransactionQueue
Account
Limiting
(
t
*
testing
.
T
)
{
// Create a test account and fund it
// Create a test account and fund it
pool
,
key
:=
setupTxPool
()
pool
,
key
:=
setupTxPool
()
account
,
_
:=
transaction
(
0
,
big
.
NewInt
(
0
),
key
)
.
From
()
account
,
_
:=
transaction
(
0
,
big
.
NewInt
(
0
),
key
)
.
From
()
...
@@ -447,25 +449,103 @@ func TestTransactionQueueLimiting(t *testing.T) {
...
@@ -447,25 +449,103 @@ func TestTransactionQueueLimiting(t *testing.T) {
state
.
AddBalance
(
account
,
big
.
NewInt
(
1000000
))
state
.
AddBalance
(
account
,
big
.
NewInt
(
1000000
))
// Keep queuing up transactions and make sure all above a limit are dropped
// Keep queuing up transactions and make sure all above a limit are dropped
for
i
:=
uint64
(
1
);
i
<=
maxQueued
+
5
;
i
++
{
for
i
:=
uint64
(
1
);
i
<=
maxQueued
PerAccount
+
5
;
i
++
{
if
err
:=
pool
.
Add
(
transaction
(
i
,
big
.
NewInt
(
100000
),
key
));
err
!=
nil
{
if
err
:=
pool
.
Add
(
transaction
(
i
,
big
.
NewInt
(
100000
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"tx %d: failed to add transaction: %v"
,
i
,
err
)
t
.
Fatalf
(
"tx %d: failed to add transaction: %v"
,
i
,
err
)
}
}
if
len
(
pool
.
pending
)
!=
0
{
if
len
(
pool
.
pending
)
!=
0
{
t
.
Errorf
(
"tx %d: pending pool size mismatch: have %d, want %d"
,
i
,
len
(
pool
.
pending
),
0
)
t
.
Errorf
(
"tx %d: pending pool size mismatch: have %d, want %d"
,
i
,
len
(
pool
.
pending
),
0
)
}
}
if
i
<=
maxQueued
{
if
i
<=
maxQueued
PerAccount
{
if
pool
.
queue
[
account
]
.
Len
()
!=
int
(
i
)
{
if
pool
.
queue
[
account
]
.
Len
()
!=
int
(
i
)
{
t
.
Errorf
(
"tx %d: queue size mismatch: have %d, want %d"
,
i
,
pool
.
queue
[
account
]
.
Len
(),
i
)
t
.
Errorf
(
"tx %d: queue size mismatch: have %d, want %d"
,
i
,
pool
.
queue
[
account
]
.
Len
(),
i
)
}
}
}
else
{
}
else
{
if
pool
.
queue
[
account
]
.
Len
()
!=
maxQueued
{
if
pool
.
queue
[
account
]
.
Len
()
!=
int
(
maxQueuedPerAccount
)
{
t
.
Errorf
(
"tx %d: queue limit mismatch: have %d, want %d"
,
i
,
pool
.
queue
[
account
]
.
Len
(),
maxQueued
)
t
.
Errorf
(
"tx %d: queue limit mismatch: have %d, want %d"
,
i
,
pool
.
queue
[
account
]
.
Len
(),
maxQueued
PerAccount
)
}
}
}
}
}
}
if
len
(
pool
.
all
)
!=
maxQueued
{
if
len
(
pool
.
all
)
!=
int
(
maxQueuedPerAccount
)
{
t
.
Errorf
(
"total transaction mismatch: have %d, want %d"
,
len
(
pool
.
all
),
maxQueued
)
t
.
Errorf
(
"total transaction mismatch: have %d, want %d"
,
len
(
pool
.
all
),
maxQueuedPerAccount
)
}
}
// Tests that if the transaction count belonging to multiple accounts go above
// some threshold, the higher transactions are dropped to prevent DOS attacks.
func
TestTransactionQueueGlobalLimiting
(
t
*
testing
.
T
)
{
// Reduce the queue limits to shorten test time
defer
func
(
old
uint64
)
{
maxQueuedInTotal
=
old
}(
maxQueuedInTotal
)
maxQueuedInTotal
=
maxQueuedPerAccount
*
3
// Create the pool to test the limit enforcement with
db
,
_
:=
ethdb
.
NewMemDatabase
()
statedb
,
_
:=
state
.
New
(
common
.
Hash
{},
db
)
pool
:=
NewTxPool
(
testChainConfig
(),
new
(
event
.
TypeMux
),
func
()
(
*
state
.
StateDB
,
error
)
{
return
statedb
,
nil
},
func
()
*
big
.
Int
{
return
big
.
NewInt
(
1000000
)
})
pool
.
resetState
()
// Create a number of test accounts and fund them
state
,
_
:=
pool
.
currentState
()
keys
:=
make
([]
*
ecdsa
.
PrivateKey
,
5
)
for
i
:=
0
;
i
<
len
(
keys
);
i
++
{
keys
[
i
],
_
=
crypto
.
GenerateKey
()
state
.
AddBalance
(
crypto
.
PubkeyToAddress
(
keys
[
i
]
.
PublicKey
),
big
.
NewInt
(
1000000
))
}
// Generate and queue a batch of transactions
nonces
:=
make
(
map
[
common
.
Address
]
uint64
)
txs
:=
make
(
types
.
Transactions
,
0
,
3
*
maxQueuedInTotal
)
for
len
(
txs
)
<
cap
(
txs
)
{
key
:=
keys
[
rand
.
Intn
(
len
(
keys
))]
addr
:=
crypto
.
PubkeyToAddress
(
key
.
PublicKey
)
txs
=
append
(
txs
,
transaction
(
nonces
[
addr
]
+
1
,
big
.
NewInt
(
100000
),
key
))
nonces
[
addr
]
++
}
// Import the batch and verify that limits have been enforced
pool
.
AddBatch
(
txs
)
queued
:=
0
for
addr
,
list
:=
range
pool
.
queue
{
if
list
.
Len
()
>
int
(
maxQueuedPerAccount
)
{
t
.
Errorf
(
"addr %x: queued accounts overflown allowance: %d > %d"
,
addr
,
list
.
Len
(),
maxQueuedPerAccount
)
}
queued
+=
list
.
Len
()
}
if
queued
>
int
(
maxQueuedInTotal
)
{
t
.
Fatalf
(
"total transactions overflow allowance: %d > %d"
,
queued
,
maxQueuedInTotal
)
}
}
// Tests that if an account remains idle for a prolonged amount of time, any
// non-executable transactions queued up are dropped to prevent wasting resources
// on shuffling them around.
func
TestTransactionQueueTimeLimiting
(
t
*
testing
.
T
)
{
// Reduce the queue limits to shorten test time
defer
func
(
old
time
.
Duration
)
{
maxQueuedLifetime
=
old
}(
maxQueuedLifetime
)
defer
func
(
old
time
.
Duration
)
{
evictionInterval
=
old
}(
evictionInterval
)
maxQueuedLifetime
=
time
.
Second
evictionInterval
=
time
.
Second
// Create a test account and fund it
pool
,
key
:=
setupTxPool
()
account
,
_
:=
transaction
(
0
,
big
.
NewInt
(
0
),
key
)
.
From
()
state
,
_
:=
pool
.
currentState
()
state
.
AddBalance
(
account
,
big
.
NewInt
(
1000000
))
// Queue up a batch of transactions
for
i
:=
uint64
(
1
);
i
<=
maxQueuedPerAccount
;
i
++
{
if
err
:=
pool
.
Add
(
transaction
(
i
,
big
.
NewInt
(
100000
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"tx %d: failed to add transaction: %v"
,
i
,
err
)
}
}
// Wait until at least two expiration cycles hit and make sure the transactions are gone
time
.
Sleep
(
2
*
evictionInterval
)
if
len
(
pool
.
queue
)
>
0
{
t
.
Fatalf
(
"old transactions remained after eviction"
)
}
}
}
}
...
@@ -481,7 +561,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
...
@@ -481,7 +561,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
state
.
AddBalance
(
account
,
big
.
NewInt
(
1000000
))
state
.
AddBalance
(
account
,
big
.
NewInt
(
1000000
))
// Keep queuing up transactions and make sure all above a limit are dropped
// Keep queuing up transactions and make sure all above a limit are dropped
for
i
:=
uint64
(
0
);
i
<
maxQueued
+
5
;
i
++
{
for
i
:=
uint64
(
0
);
i
<
maxQueued
PerAccount
+
5
;
i
++
{
if
err
:=
pool
.
Add
(
transaction
(
i
,
big
.
NewInt
(
100000
),
key
));
err
!=
nil
{
if
err
:=
pool
.
Add
(
transaction
(
i
,
big
.
NewInt
(
100000
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"tx %d: failed to add transaction: %v"
,
i
,
err
)
t
.
Fatalf
(
"tx %d: failed to add transaction: %v"
,
i
,
err
)
}
}
...
@@ -492,8 +572,8 @@ func TestTransactionPendingLimiting(t *testing.T) {
...
@@ -492,8 +572,8 @@ func TestTransactionPendingLimiting(t *testing.T) {
t
.
Errorf
(
"tx %d: queue size mismatch: have %d, want %d"
,
i
,
pool
.
queue
[
account
]
.
Len
(),
0
)
t
.
Errorf
(
"tx %d: queue size mismatch: have %d, want %d"
,
i
,
pool
.
queue
[
account
]
.
Len
(),
0
)
}
}
}
}
if
len
(
pool
.
all
)
!=
maxQueued
+
5
{
if
len
(
pool
.
all
)
!=
int
(
maxQueuedPerAccount
+
5
)
{
t
.
Errorf
(
"total transaction mismatch: have %d, want %d"
,
len
(
pool
.
all
),
maxQueued
+
5
)
t
.
Errorf
(
"total transaction mismatch: have %d, want %d"
,
len
(
pool
.
all
),
maxQueued
PerAccount
+
5
)
}
}
}
}
...
@@ -509,7 +589,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
...
@@ -509,7 +589,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
state1
,
_
:=
pool1
.
currentState
()
state1
,
_
:=
pool1
.
currentState
()
state1
.
AddBalance
(
account1
,
big
.
NewInt
(
1000000
))
state1
.
AddBalance
(
account1
,
big
.
NewInt
(
1000000
))
for
i
:=
uint64
(
0
);
i
<
maxQueued
+
5
;
i
++
{
for
i
:=
uint64
(
0
);
i
<
maxQueued
PerAccount
+
5
;
i
++
{
if
err
:=
pool1
.
Add
(
transaction
(
origin
+
i
,
big
.
NewInt
(
100000
),
key1
));
err
!=
nil
{
if
err
:=
pool1
.
Add
(
transaction
(
origin
+
i
,
big
.
NewInt
(
100000
),
key1
));
err
!=
nil
{
t
.
Fatalf
(
"tx %d: failed to add transaction: %v"
,
i
,
err
)
t
.
Fatalf
(
"tx %d: failed to add transaction: %v"
,
i
,
err
)
}
}
...
@@ -521,7 +601,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
...
@@ -521,7 +601,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
state2
.
AddBalance
(
account2
,
big
.
NewInt
(
1000000
))
state2
.
AddBalance
(
account2
,
big
.
NewInt
(
1000000
))
txns
:=
[]
*
types
.
Transaction
{}
txns
:=
[]
*
types
.
Transaction
{}
for
i
:=
uint64
(
0
);
i
<
maxQueued
+
5
;
i
++
{
for
i
:=
uint64
(
0
);
i
<
maxQueued
PerAccount
+
5
;
i
++
{
txns
=
append
(
txns
,
transaction
(
origin
+
i
,
big
.
NewInt
(
100000
),
key2
))
txns
=
append
(
txns
,
transaction
(
origin
+
i
,
big
.
NewInt
(
100000
),
key2
))
}
}
pool2
.
AddBatch
(
txns
)
pool2
.
AddBatch
(
txns
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录