Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
whqwjb
go-ethereum
提交
0ef327bb
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 搜索 >>
提交
0ef327bb
编写于
7月 01, 2016
作者:
P
Péter Szilágyi
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
core, eth, internal, miner: optimize txpool for quick ops
上级
795b7042
变更
14
展开全部
隐藏空白更改
内联
并排
Showing
14 changed file
with
788 addition
and
426 deletion
+788
-426
core/tx_list.go
core/tx_list.go
+331
-0
core/tx_list_test.go
core/tx_list_test.go
+58
-0
core/tx_pool.go
core/tx_pool.go
+215
-288
core/tx_pool_test.go
core/tx_pool_test.go
+134
-92
core/types/transaction.go
core/types/transaction.go
+9
-18
core/types/transaction_test.go
core/types/transaction_test.go
+4
-3
eth/api_backend.go
eth/api_backend.go
+9
-5
eth/handler.go
eth/handler.go
+1
-1
eth/helper_test.go
eth/helper_test.go
+14
-8
eth/protocol.go
eth/protocol.go
+4
-4
eth/protocol_test.go
eth/protocol_test.go
+1
-1
eth/sync.go
eth/sync.go
+4
-1
internal/ethapi/backend.go
internal/ethapi/backend.go
+1
-1
miner/worker.go
miner/worker.go
+3
-4
未找到文件。
core/tx_list.go
0 → 100644
浏览文件 @
0ef327bb
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
core
import
(
"container/heap"
"math"
"math/big"
"sort"
"github.com/ethereum/go-ethereum/core/types"
)
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
// retrieving sorted transactions from the possibly gapped future queue.
type
nonceHeap
[]
uint64
func
(
h
nonceHeap
)
Len
()
int
{
return
len
(
h
)
}
func
(
h
nonceHeap
)
Less
(
i
,
j
int
)
bool
{
return
h
[
i
]
<
h
[
j
]
}
func
(
h
nonceHeap
)
Swap
(
i
,
j
int
)
{
h
[
i
],
h
[
j
]
=
h
[
j
],
h
[
i
]
}
func
(
h
*
nonceHeap
)
Push
(
x
interface
{})
{
*
h
=
append
(
*
h
,
x
.
(
uint64
))
}
func
(
h
*
nonceHeap
)
Pop
()
interface
{}
{
old
:=
*
h
n
:=
len
(
old
)
x
:=
old
[
n
-
1
]
*
h
=
old
[
0
:
n
-
1
]
return
x
}
// txList is a "list" of transactions belonging to an account, sorted by account
// nonce. The same type can be used both for storing contiguous transactions for
// the executable/pending queue; and for storing gapped transactions for the non-
// executable/future queue, with minor behavoiral changes.
type
txList
struct
{
strict
bool
// Whether nonces are strictly continuous or not
items
map
[
uint64
]
*
types
.
Transaction
// Hash map storing the transaction data
cache
types
.
Transactions
// cache of the transactions already sorted
first
uint64
// Nonce of the lowest stored transaction (strict mode)
last
uint64
// Nonce of the highest stored transaction (strict mode)
index
*
nonceHeap
// Heap of nonces of all teh stored transactions (non-strict mode)
costcap
*
big
.
Int
// Price of the highest costing transaction (reset only if exceeds balance)
}
// newTxList create a new transaction list for maintaining nonce-indexable fast,
// gapped, sortable transaction lists.
func
newTxList
(
strict
bool
)
*
txList
{
return
&
txList
{
strict
:
strict
,
items
:
make
(
map
[
uint64
]
*
types
.
Transaction
),
first
:
math
.
MaxUint64
,
index
:
&
nonceHeap
{},
costcap
:
new
(
big
.
Int
),
}
}
// Add tries to inserts a new transaction into the list, returning whether the
// transaction was acceped, and if yes, any previous transaction it replaced.
//
// In case of strict lists (contiguous nonces) the nonce boundaries are updated
// appropriately with the new transaction. Otherwise (gapped nonces) the heap of
// nonces is expanded with the new transaction.
func
(
l
*
txList
)
Add
(
tx
*
types
.
Transaction
)
(
bool
,
*
types
.
Transaction
)
{
// If an existing transaction is better, discard new one
nonce
:=
tx
.
Nonce
()
old
,
ok
:=
l
.
items
[
nonce
]
if
ok
&&
old
.
GasPrice
()
.
Cmp
(
tx
.
GasPrice
())
>=
0
{
return
false
,
nil
}
// Otherwise insert the transaction and replace any previous one
l
.
items
[
nonce
]
=
tx
if
cost
:=
tx
.
Cost
();
l
.
costcap
.
Cmp
(
cost
)
<
0
{
l
.
costcap
=
cost
}
if
l
.
strict
{
// In strict mode, maintain the nonce sequence boundaries
if
nonce
<
l
.
first
{
l
.
first
=
nonce
}
if
nonce
>
l
.
last
{
l
.
last
=
nonce
}
}
else
{
// In gapped mode, maintain the nonce heap
heap
.
Push
(
l
.
index
,
nonce
)
}
l
.
cache
=
nil
return
true
,
old
}
// Forward removes all transactions from the list with a nonce lower than the
// provided threshold. Every removed transaction is returned for any post-removal
// maintenance.
func
(
l
*
txList
)
Forward
(
threshold
uint64
)
types
.
Transactions
{
var
removed
types
.
Transactions
if
l
.
strict
{
// In strict mode, push the lowest nonce forward to the threshold
for
l
.
first
<
threshold
{
if
tx
,
ok
:=
l
.
items
[
l
.
first
];
ok
{
removed
=
append
(
removed
,
tx
)
}
delete
(
l
.
items
,
l
.
first
)
l
.
first
++
}
if
l
.
first
>
l
.
last
{
l
.
last
=
l
.
first
}
}
else
{
// In gapped mode, pop off heap items until the threshold is reached
for
l
.
index
.
Len
()
>
0
&&
(
*
l
.
index
)[
0
]
<
threshold
{
nonce
:=
heap
.
Pop
(
l
.
index
)
.
(
uint64
)
removed
=
append
(
removed
,
l
.
items
[
nonce
])
delete
(
l
.
items
,
nonce
)
}
}
l
.
cache
=
nil
return
removed
}
// Filter removes all transactions from the list with a cost higher than the
// provided threshold. Every removed transaction is returned for any post-removal
// maintenance. Strict-mode invalidated transactions are also returned.
//
// 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
// loewr than the costcap, the costcap will be reset to a new high after removing
// expensive the too transactions.
func
(
l
*
txList
)
Filter
(
threshold
*
big
.
Int
)
(
types
.
Transactions
,
types
.
Transactions
)
{
// If all transactions are blow the threshold, short circuit
if
l
.
costcap
.
Cmp
(
threshold
)
<=
0
{
return
nil
,
nil
}
l
.
costcap
=
new
(
big
.
Int
)
.
Set
(
threshold
)
// Lower the cap to the threshold
// Gather all the transactions needing deletion
var
removed
types
.
Transactions
for
_
,
tx
:=
range
l
.
items
{
if
cost
:=
tx
.
Cost
();
cost
.
Cmp
(
threshold
)
>
0
{
removed
=
append
(
removed
,
tx
)
delete
(
l
.
items
,
tx
.
Nonce
())
}
}
// Readjust the nonce boundaries/indexes and gather invalidate tranactions
var
invalids
types
.
Transactions
if
l
.
strict
{
// In strict mode iterate find the first gap and invalidate everything after it
for
i
:=
l
.
first
;
i
<=
l
.
last
;
i
++
{
if
_
,
ok
:=
l
.
items
[
i
];
!
ok
{
// Gap found, invalidate all subsequent transactions
for
j
:=
i
+
1
;
j
<=
l
.
last
;
j
++
{
if
tx
,
ok
:=
l
.
items
[
j
];
ok
{
invalids
=
append
(
invalids
,
tx
)
delete
(
l
.
items
,
j
)
}
}
// Reduce the highest transaction nonce and return
l
.
last
=
i
-
1
break
}
}
}
else
{
// In gapped mode no transactions are invalid, but the heap is ruined
l
.
index
=
&
nonceHeap
{}
for
nonce
,
_
:=
range
l
.
items
{
*
l
.
index
=
append
(
*
l
.
index
,
nonce
)
}
heap
.
Init
(
l
.
index
)
}
l
.
cache
=
nil
return
removed
,
invalids
}
// Cap places a hard limit on the number of items, returning all transactions
// exceeding tht limit.
func
(
l
*
txList
)
Cap
(
threshold
int
)
types
.
Transactions
{
// Short circuit if the number of items is under the limit
if
len
(
l
.
items
)
<
threshold
{
return
nil
}
// Otherwise gather and drop the highest nonce'd transactions
var
drops
types
.
Transactions
if
l
.
strict
{
// In strict mode, just gather top down from last to first
for
len
(
l
.
items
)
>
threshold
{
if
tx
,
ok
:=
l
.
items
[
l
.
last
];
ok
{
drops
=
append
(
drops
,
tx
)
delete
(
l
.
items
,
l
.
last
)
l
.
last
--
}
}
}
else
{
// In gapped mode it's expensive: we need to sort and drop like that
sort
.
Sort
(
*
l
.
index
)
for
size
:=
len
(
l
.
items
);
size
>
threshold
;
size
--
{
drops
=
append
(
drops
,
l
.
items
[(
*
l
.
index
)[
size
-
1
]])
delete
(
l
.
items
,
(
*
l
.
index
)[
size
-
1
])
*
l
.
index
=
(
*
l
.
index
)[
:
size
-
1
]
}
heap
.
Init
(
l
.
index
)
}
l
.
cache
=
nil
return
drops
}
// Remove deletes a transaction from the maintained list, returning whether the
// transaction was found, and also returning any transaction invalidated due to
// the deletion (strict mode only).
func
(
l
*
txList
)
Remove
(
tx
*
types
.
Transaction
)
(
bool
,
types
.
Transactions
)
{
nonce
:=
tx
.
Nonce
()
if
_
,
ok
:=
l
.
items
[
nonce
];
ok
{
// Remove the item and invalidate the sorted cache
delete
(
l
.
items
,
nonce
)
l
.
cache
=
nil
// Remove all invalidated transactions (strict mode only!)
invalids
:=
make
(
types
.
Transactions
,
0
,
l
.
last
-
nonce
)
if
l
.
strict
{
for
i
:=
nonce
+
1
;
i
<=
l
.
last
;
i
++
{
invalids
=
append
(
invalids
,
l
.
items
[
i
])
delete
(
l
.
items
,
i
)
}
l
.
last
=
nonce
-
1
}
else
{
// In gapped mode, remove the nonce from the index but honour the heap
for
i
:=
0
;
i
<
l
.
index
.
Len
();
i
++
{
if
(
*
l
.
index
)[
i
]
==
nonce
{
heap
.
Remove
(
l
.
index
,
i
)
break
}
}
}
// Figure out the new highest nonce
return
true
,
invalids
}
return
false
,
nil
}
// Ready retrieves a sequentially increasing list of transactions starting at the
// provided nonce that is ready for processing. The returned transactions will be
// removed from the list.
//
// Note, all transactions with nonces lower that start will also be returned to
// prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing!
func
(
l
*
txList
)
Ready
(
start
uint64
)
types
.
Transactions
{
var
txs
types
.
Transactions
if
l
.
strict
{
// In strict mode make sure we have valid transaction, return all contiguous
if
l
.
first
>
start
{
return
nil
}
for
{
if
tx
,
ok
:=
l
.
items
[
l
.
first
];
ok
{
txs
=
append
(
txs
,
tx
)
delete
(
l
.
items
,
l
.
first
)
l
.
first
++
continue
}
break
}
}
else
{
// In gapped mode, check the heap start and return all contiguous
if
l
.
index
.
Len
()
==
0
||
(
*
l
.
index
)[
0
]
>
start
{
return
nil
}
next
:=
(
*
l
.
index
)[
0
]
for
l
.
index
.
Len
()
>
0
&&
(
*
l
.
index
)[
0
]
==
next
{
txs
=
append
(
txs
,
l
.
items
[
next
])
delete
(
l
.
items
,
next
)
heap
.
Pop
(
l
.
index
)
next
++
}
}
l
.
cache
=
nil
return
txs
}
// Len returns the length of the transaction list.
func
(
l
*
txList
)
Len
()
int
{
return
len
(
l
.
items
)
}
// Empty returns whether the list of transactions is empty or not.
func
(
l
*
txList
)
Empty
()
bool
{
return
len
(
l
.
items
)
==
0
}
// Flatten creates a nonce-sorted slice of transactions based on the loosely
// sorted internal representation. The result of the sorting is cached in case
// it's requested again before any modifications are made to the contents.
func
(
l
*
txList
)
Flatten
()
types
.
Transactions
{
// If the sorting was not cached yet, create and cache it
if
l
.
cache
==
nil
{
l
.
cache
=
make
(
types
.
Transactions
,
0
,
len
(
l
.
items
))
for
_
,
tx
:=
range
l
.
items
{
l
.
cache
=
append
(
l
.
cache
,
tx
)
}
sort
.
Sort
(
types
.
TxByNonce
(
l
.
cache
))
}
// Copy the cache to prevent accidental modifications
txs
:=
make
(
types
.
Transactions
,
len
(
l
.
cache
))
copy
(
txs
,
l
.
cache
)
return
txs
}
core/tx_list_test.go
0 → 100644
浏览文件 @
0ef327bb
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package
core
import
(
"math/big"
"math/rand"
"testing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
)
// Tests that transactions can be added to strict lists and list contents and
// nonce boundaries are correctly maintained.
func
TestStrictTxListAdd
(
t
*
testing
.
T
)
{
// Generate a list of transactions to insert
key
,
_
:=
crypto
.
GenerateKey
()
txs
:=
make
(
types
.
Transactions
,
1024
)
for
i
:=
0
;
i
<
len
(
txs
);
i
++
{
txs
[
i
]
=
transaction
(
uint64
(
i
),
new
(
big
.
Int
),
key
)
}
// Insert the transactions in a random order
list
:=
newTxList
(
true
)
for
_
,
v
:=
range
rand
.
Perm
(
len
(
txs
))
{
list
.
Add
(
txs
[
v
])
}
// Verify internal state
if
list
.
first
!=
0
{
t
.
Errorf
(
"lowest nonce mismatch: have %d, want %d"
,
list
.
first
,
0
)
}
if
int
(
list
.
last
)
!=
len
(
txs
)
-
1
{
t
.
Errorf
(
"highest nonce mismatch: have %d, want %d"
,
list
.
last
,
len
(
txs
)
-
1
)
}
if
len
(
list
.
items
)
!=
len
(
txs
)
{
t
.
Errorf
(
"transaction count mismatch: have %d, want %d"
,
len
(
list
.
items
),
len
(
txs
))
}
for
i
,
tx
:=
range
txs
{
if
list
.
items
[
tx
.
Nonce
()]
!=
tx
{
t
.
Errorf
(
"item %d: transaction mismatch: have %v, want %v"
,
i
,
list
.
items
[
tx
.
Nonce
()],
tx
)
}
}
}
core/tx_pool.go
浏览文件 @
0ef327bb
此差异已折叠。
点击以展开。
core/tx_pool_test.go
浏览文件 @
0ef327bb
...
...
@@ -91,9 +91,9 @@ func TestTransactionQueue(t *testing.T) {
from
,
_
:=
tx
.
From
()
currentState
,
_
:=
pool
.
currentState
()
currentState
.
AddBalance
(
from
,
big
.
NewInt
(
1000
))
pool
.
queueTx
(
tx
.
Hash
(),
tx
)
pool
.
en
queueTx
(
tx
.
Hash
(),
tx
)
pool
.
checkQueue
()
pool
.
promoteExecutables
()
if
len
(
pool
.
pending
)
!=
1
{
t
.
Error
(
"expected valid txs to be 1 is"
,
len
(
pool
.
pending
))
}
...
...
@@ -101,14 +101,14 @@ func TestTransactionQueue(t *testing.T) {
tx
=
transaction
(
1
,
big
.
NewInt
(
100
),
key
)
from
,
_
=
tx
.
From
()
currentState
.
SetNonce
(
from
,
2
)
pool
.
queueTx
(
tx
.
Hash
(),
tx
)
pool
.
checkQueue
()
if
_
,
ok
:=
pool
.
pending
[
from
][
tx
.
Nonce
()];
ok
{
pool
.
en
queueTx
(
tx
.
Hash
(),
tx
)
pool
.
promoteExecutables
()
if
_
,
ok
:=
pool
.
pending
[
from
]
.
items
[
tx
.
Nonce
()];
ok
{
t
.
Error
(
"expected transaction to be in tx pool"
)
}
if
len
(
pool
.
queue
[
from
]
)
>
0
{
t
.
Error
(
"expected transaction queue to be empty. is"
,
len
(
pool
.
queue
[
from
]
))
if
len
(
pool
.
queue
)
>
0
{
t
.
Error
(
"expected transaction queue to be empty. is"
,
len
(
pool
.
queue
))
}
pool
,
key
=
setupTxPool
()
...
...
@@ -118,17 +118,17 @@ func TestTransactionQueue(t *testing.T) {
from
,
_
=
tx1
.
From
()
currentState
,
_
=
pool
.
currentState
()
currentState
.
AddBalance
(
from
,
big
.
NewInt
(
1000
))
pool
.
queueTx
(
tx1
.
Hash
(),
tx1
)
pool
.
queueTx
(
tx2
.
Hash
(),
tx2
)
pool
.
queueTx
(
tx3
.
Hash
(),
tx3
)
pool
.
en
queueTx
(
tx1
.
Hash
(),
tx1
)
pool
.
en
queueTx
(
tx2
.
Hash
(),
tx2
)
pool
.
en
queueTx
(
tx3
.
Hash
(),
tx3
)
pool
.
checkQueue
()
pool
.
promoteExecutables
()
if
len
(
pool
.
pending
)
!=
1
{
t
.
Error
(
"expected tx pool to be 1, got"
,
len
(
pool
.
pending
))
}
if
len
(
pool
.
queue
[
from
]
)
!=
2
{
t
.
Error
(
"expected len(queue) == 2, got"
,
len
(
pool
.
queue
[
from
]
))
if
pool
.
queue
[
from
]
.
Len
(
)
!=
2
{
t
.
Error
(
"expected len(queue) == 2, got"
,
pool
.
queue
[
from
]
.
Len
(
))
}
}
...
...
@@ -138,24 +138,21 @@ func TestRemoveTx(t *testing.T) {
from
,
_
:=
tx
.
From
()
currentState
,
_
:=
pool
.
currentState
()
currentState
.
AddBalance
(
from
,
big
.
NewInt
(
1
))
pool
.
queueTx
(
tx
.
Hash
(),
tx
)
pool
.
addTx
(
from
,
tx
)
pool
.
enqueueTx
(
tx
.
Hash
(),
tx
)
pool
.
promoteTx
(
from
,
tx
.
Hash
(),
tx
)
if
len
(
pool
.
queue
)
!=
1
{
t
.
Error
(
"expected queue to be 1, got"
,
len
(
pool
.
queue
))
}
if
len
(
pool
.
pending
)
!=
1
{
t
.
Error
(
"expected
txs
to be 1, got"
,
len
(
pool
.
pending
))
t
.
Error
(
"expected
pending
to be 1, got"
,
len
(
pool
.
pending
))
}
pool
.
RemoveTx
(
tx
.
Hash
())
pool
.
Remove
(
tx
.
Hash
())
if
len
(
pool
.
queue
)
>
0
{
t
.
Error
(
"expected queue to be 0, got"
,
len
(
pool
.
queue
))
}
if
len
(
pool
.
pending
)
>
0
{
t
.
Error
(
"expected
txs
to be 0, got"
,
len
(
pool
.
pending
))
t
.
Error
(
"expected
pending
to be 0, got"
,
len
(
pool
.
pending
))
}
}
...
...
@@ -188,7 +185,7 @@ func TestTransactionChainFork(t *testing.T) {
if
err
:=
pool
.
add
(
tx
);
err
!=
nil
{
t
.
Error
(
"didn't expect error"
,
err
)
}
pool
.
Remove
Transactions
([]
*
types
.
Transaction
{
tx
})
pool
.
Remove
Batch
([]
*
types
.
Transaction
{
tx
})
// reset the pool's internal state
resetState
()
...
...
@@ -221,22 +218,22 @@ func TestTransactionDoubleNonce(t *testing.T) {
if
err
:=
pool
.
add
(
tx2
);
err
!=
nil
{
t
.
Error
(
"didn't expect error"
,
err
)
}
pool
.
checkQueue
()
if
len
(
pool
.
pending
[
addr
]
)
!=
1
{
t
.
Error
(
"expected 1 pending transactions, got"
,
len
(
pool
.
pending
))
pool
.
promoteExecutables
()
if
pool
.
pending
[
addr
]
.
Len
(
)
!=
1
{
t
.
Error
(
"expected 1 pending transactions, got"
,
pool
.
pending
[
addr
]
.
Len
(
))
}
if
tx
:=
pool
.
pending
[
addr
][
0
];
tx
.
Hash
()
!=
tx2
.
Hash
()
{
if
tx
:=
pool
.
pending
[
addr
]
.
items
[
0
];
tx
.
Hash
()
!=
tx2
.
Hash
()
{
t
.
Errorf
(
"transaction mismatch: have %x, want %x"
,
tx
.
Hash
(),
tx2
.
Hash
())
}
// Add the thid transaction and ensure it's not saved (smaller price)
if
err
:=
pool
.
add
(
tx3
);
err
!=
nil
{
t
.
Error
(
"didn't expect error"
,
err
)
}
pool
.
checkQueue
()
if
len
(
pool
.
pending
[
addr
]
)
!=
1
{
t
.
Error
(
"expected 1 pending transactions, got"
,
len
(
pool
.
pending
))
pool
.
promoteExecutables
()
if
pool
.
pending
[
addr
]
.
Len
(
)
!=
1
{
t
.
Error
(
"expected 1 pending transactions, got"
,
pool
.
pending
[
addr
]
.
Len
(
))
}
if
tx
:=
pool
.
pending
[
addr
][
0
];
tx
.
Hash
()
!=
tx2
.
Hash
()
{
if
tx
:=
pool
.
pending
[
addr
]
.
items
[
0
];
tx
.
Hash
()
!=
tx2
.
Hash
()
{
t
.
Errorf
(
"transaction mismatch: have %x, want %x"
,
tx
.
Hash
(),
tx2
.
Hash
())
}
// Ensure the total transaction count is correct
...
...
@@ -254,11 +251,11 @@ func TestMissingNonce(t *testing.T) {
if
err
:=
pool
.
add
(
tx
);
err
!=
nil
{
t
.
Error
(
"didn't expect error"
,
err
)
}
if
len
(
pool
.
pending
[
addr
]
)
!=
0
{
t
.
Error
(
"expected 0 pending transactions, got"
,
len
(
pool
.
pending
[
addr
]
))
if
len
(
pool
.
pending
)
!=
0
{
t
.
Error
(
"expected 0 pending transactions, got"
,
len
(
pool
.
pending
))
}
if
len
(
pool
.
queue
[
addr
]
)
!=
1
{
t
.
Error
(
"expected 1 queued transaction, got"
,
len
(
pool
.
queue
[
addr
]
))
if
pool
.
queue
[
addr
]
.
Len
(
)
!=
1
{
t
.
Error
(
"expected 1 queued transaction, got"
,
pool
.
queue
[
addr
]
.
Len
(
))
}
if
len
(
pool
.
all
)
!=
1
{
t
.
Error
(
"expected 1 total transactions, got"
,
len
(
pool
.
all
))
...
...
@@ -293,8 +290,8 @@ func TestRemovedTxEvent(t *testing.T) {
currentState
.
AddBalance
(
from
,
big
.
NewInt
(
1000000000000
))
pool
.
eventMux
.
Post
(
RemovedTransactionEvent
{
types
.
Transactions
{
tx
}})
pool
.
eventMux
.
Post
(
ChainHeadEvent
{
nil
})
if
len
(
pool
.
pending
[
from
]
)
!=
1
{
t
.
Error
(
"expected 1 pending tx, got"
,
len
(
pool
.
pending
[
from
]
))
if
pool
.
pending
[
from
]
.
Len
(
)
!=
1
{
t
.
Error
(
"expected 1 pending tx, got"
,
pool
.
pending
[
from
]
.
Len
(
))
}
if
len
(
pool
.
all
)
!=
1
{
t
.
Error
(
"expected 1 total transactions, got"
,
len
(
pool
.
all
))
...
...
@@ -318,27 +315,27 @@ func TestTransactionDropping(t *testing.T) {
tx10
=
transaction
(
10
,
big
.
NewInt
(
100
),
key
)
tx11
=
transaction
(
11
,
big
.
NewInt
(
200
),
key
)
)
pool
.
addTx
(
account
,
tx0
)
pool
.
addTx
(
account
,
tx1
)
pool
.
queueTx
(
tx10
.
Hash
(),
tx10
)
pool
.
queueTx
(
tx11
.
Hash
(),
tx11
)
pool
.
promoteTx
(
account
,
tx0
.
Hash
()
,
tx0
)
pool
.
promoteTx
(
account
,
tx1
.
Hash
()
,
tx1
)
pool
.
en
queueTx
(
tx10
.
Hash
(),
tx10
)
pool
.
en
queueTx
(
tx11
.
Hash
(),
tx11
)
// Check that pre and post validations leave the pool as is
if
len
(
pool
.
pending
[
account
]
)
!=
2
{
t
.
Errorf
(
"pending transaction mismatch: have %d, want %d"
,
len
(
pool
.
pending
[
account
]
),
2
)
if
pool
.
pending
[
account
]
.
Len
(
)
!=
2
{
t
.
Errorf
(
"pending transaction mismatch: have %d, want %d"
,
pool
.
pending
[
account
]
.
Len
(
),
2
)
}
if
len
(
pool
.
queue
[
account
]
)
!=
2
{
t
.
Errorf
(
"queued transaction mismatch: have %d, want %d"
,
len
(
pool
.
queue
[
account
]
),
2
)
if
pool
.
queue
[
account
]
.
Len
(
)
!=
2
{
t
.
Errorf
(
"queued transaction mismatch: have %d, want %d"
,
pool
.
queue
[
account
]
.
Len
(
),
2
)
}
if
len
(
pool
.
all
)
!=
4
{
t
.
Errorf
(
"total transaction mismatch: have %d, want %d"
,
len
(
pool
.
all
),
4
)
}
pool
.
resetState
()
if
len
(
pool
.
pending
[
account
]
)
!=
2
{
t
.
Errorf
(
"pending transaction mismatch: have %d, want %d"
,
len
(
pool
.
pending
[
account
]
),
2
)
if
pool
.
pending
[
account
]
.
Len
(
)
!=
2
{
t
.
Errorf
(
"pending transaction mismatch: have %d, want %d"
,
pool
.
pending
[
account
]
.
Len
(
),
2
)
}
if
len
(
pool
.
queue
[
account
]
)
!=
2
{
t
.
Errorf
(
"queued transaction mismatch: have %d, want %d"
,
len
(
pool
.
queue
[
account
]
),
2
)
if
pool
.
queue
[
account
]
.
Len
(
)
!=
2
{
t
.
Errorf
(
"queued transaction mismatch: have %d, want %d"
,
pool
.
queue
[
account
]
.
Len
(
),
2
)
}
if
len
(
pool
.
all
)
!=
4
{
t
.
Errorf
(
"total transaction mismatch: have %d, want %d"
,
len
(
pool
.
all
),
4
)
...
...
@@ -347,16 +344,16 @@ func TestTransactionDropping(t *testing.T) {
state
.
AddBalance
(
account
,
big
.
NewInt
(
-
750
))
pool
.
resetState
()
if
_
,
ok
:=
pool
.
pending
[
account
][
tx0
.
Nonce
()];
!
ok
{
if
_
,
ok
:=
pool
.
pending
[
account
]
.
items
[
tx0
.
Nonce
()];
!
ok
{
t
.
Errorf
(
"funded pending transaction missing: %v"
,
tx0
)
}
if
_
,
ok
:=
pool
.
pending
[
account
][
tx1
.
Nonce
()];
ok
{
if
_
,
ok
:=
pool
.
pending
[
account
]
.
items
[
tx1
.
Nonce
()];
ok
{
t
.
Errorf
(
"out-of-fund pending transaction present: %v"
,
tx1
)
}
if
_
,
ok
:=
pool
.
queue
[
account
][
tx10
.
Nonce
()];
!
ok
{
if
_
,
ok
:=
pool
.
queue
[
account
]
.
items
[
tx10
.
Nonce
()];
!
ok
{
t
.
Errorf
(
"funded queued transaction missing: %v"
,
tx10
)
}
if
_
,
ok
:=
pool
.
queue
[
account
][
tx11
.
Nonce
()];
ok
{
if
_
,
ok
:=
pool
.
queue
[
account
]
.
items
[
tx11
.
Nonce
()];
ok
{
t
.
Errorf
(
"out-of-fund queued transaction present: %v"
,
tx11
)
}
if
len
(
pool
.
all
)
!=
2
{
...
...
@@ -384,25 +381,25 @@ func TestTransactionPostponing(t *testing.T) {
}
else
{
tx
=
transaction
(
uint64
(
i
),
big
.
NewInt
(
500
),
key
)
}
pool
.
addTx
(
account
,
tx
)
pool
.
promoteTx
(
account
,
tx
.
Hash
()
,
tx
)
txns
=
append
(
txns
,
tx
)
}
// Check that pre and post validations leave the pool as is
if
len
(
pool
.
pending
[
account
]
)
!=
len
(
txns
)
{
t
.
Errorf
(
"pending transaction mismatch: have %d, want %d"
,
len
(
pool
.
pending
[
account
]
),
len
(
txns
))
if
pool
.
pending
[
account
]
.
Len
(
)
!=
len
(
txns
)
{
t
.
Errorf
(
"pending transaction mismatch: have %d, want %d"
,
pool
.
pending
[
account
]
.
Len
(
),
len
(
txns
))
}
if
len
(
pool
.
queue
[
account
]
)
!=
0
{
t
.
Errorf
(
"queued transaction mismatch: have %d, want %d"
,
len
(
pool
.
queue
[
account
]
),
0
)
if
len
(
pool
.
queue
)
!=
0
{
t
.
Errorf
(
"queued transaction mismatch: have %d, want %d"
,
pool
.
queue
[
account
]
.
Len
(
),
0
)
}
if
len
(
pool
.
all
)
!=
len
(
txns
)
{
t
.
Errorf
(
"total transaction mismatch: have %d, want %d"
,
len
(
pool
.
all
),
len
(
txns
))
}
pool
.
resetState
()
if
len
(
pool
.
pending
[
account
]
)
!=
len
(
txns
)
{
t
.
Errorf
(
"pending transaction mismatch: have %d, want %d"
,
len
(
pool
.
pending
[
account
]
),
len
(
txns
))
if
pool
.
pending
[
account
]
.
Len
(
)
!=
len
(
txns
)
{
t
.
Errorf
(
"pending transaction mismatch: have %d, want %d"
,
pool
.
pending
[
account
]
.
Len
(
),
len
(
txns
))
}
if
len
(
pool
.
queue
[
account
]
)
!=
0
{
t
.
Errorf
(
"queued transaction mismatch: have %d, want %d"
,
len
(
pool
.
queue
[
account
]
),
0
)
if
len
(
pool
.
queue
)
!=
0
{
t
.
Errorf
(
"queued transaction mismatch: have %d, want %d"
,
pool
.
queue
[
account
]
.
Len
(
),
0
)
}
if
len
(
pool
.
all
)
!=
len
(
txns
)
{
t
.
Errorf
(
"total transaction mismatch: have %d, want %d"
,
len
(
pool
.
all
),
len
(
txns
))
...
...
@@ -411,25 +408,25 @@ func TestTransactionPostponing(t *testing.T) {
state
.
AddBalance
(
account
,
big
.
NewInt
(
-
750
))
pool
.
resetState
()
if
_
,
ok
:=
pool
.
pending
[
account
][
txns
[
0
]
.
Nonce
()];
!
ok
{
if
_
,
ok
:=
pool
.
pending
[
account
]
.
items
[
txns
[
0
]
.
Nonce
()];
!
ok
{
t
.
Errorf
(
"tx %d: valid and funded transaction missing from pending pool: %v"
,
0
,
txns
[
0
])
}
if
_
,
ok
:=
pool
.
queue
[
account
][
txns
[
0
]
.
Nonce
()];
ok
{
if
_
,
ok
:=
pool
.
queue
[
account
]
.
items
[
txns
[
0
]
.
Nonce
()];
ok
{
t
.
Errorf
(
"tx %d: valid and funded transaction present in future queue: %v"
,
0
,
txns
[
0
])
}
for
i
,
tx
:=
range
txns
[
1
:
]
{
if
i
%
2
==
1
{
if
_
,
ok
:=
pool
.
pending
[
account
][
tx
.
Nonce
()];
ok
{
if
_
,
ok
:=
pool
.
pending
[
account
]
.
items
[
tx
.
Nonce
()];
ok
{
t
.
Errorf
(
"tx %d: valid but future transaction present in pending pool: %v"
,
i
+
1
,
tx
)
}
if
_
,
ok
:=
pool
.
queue
[
account
][
tx
.
Nonce
()];
!
ok
{
if
_
,
ok
:=
pool
.
queue
[
account
]
.
items
[
tx
.
Nonce
()];
!
ok
{
t
.
Errorf
(
"tx %d: valid but future transaction missing from future queue: %v"
,
i
+
1
,
tx
)
}
}
else
{
if
_
,
ok
:=
pool
.
pending
[
account
][
tx
.
Nonce
()];
ok
{
if
_
,
ok
:=
pool
.
pending
[
account
]
.
items
[
tx
.
Nonce
()];
ok
{
t
.
Errorf
(
"tx %d: out-of-fund transaction present in pending pool: %v"
,
i
+
1
,
tx
)
}
if
_
,
ok
:=
pool
.
queue
[
account
][
tx
.
Nonce
()];
ok
{
if
_
,
ok
:=
pool
.
queue
[
account
]
.
items
[
tx
.
Nonce
()];
ok
{
t
.
Errorf
(
"tx %d: out-of-fund transaction present in future queue: %v"
,
i
+
1
,
tx
)
}
}
...
...
@@ -458,12 +455,12 @@ func TestTransactionQueueLimiting(t *testing.T) {
t
.
Errorf
(
"tx %d: pending pool size mismatch: have %d, want %d"
,
i
,
len
(
pool
.
pending
),
0
)
}
if
i
<=
maxQueued
{
if
len
(
pool
.
queue
[
account
]
)
!=
int
(
i
)
{
t
.
Errorf
(
"tx %d: queue size mismatch: have %d, want %d"
,
i
,
len
(
pool
.
queue
[
account
]
),
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
)
}
}
else
{
if
len
(
pool
.
queue
[
account
]
)
!=
maxQueued
{
t
.
Errorf
(
"tx %d: queue limit mismatch: have %d, want %d"
,
i
,
len
(
pool
.
queue
[
account
]
),
maxQueued
)
if
pool
.
queue
[
account
]
.
Len
(
)
!=
maxQueued
{
t
.
Errorf
(
"tx %d: queue limit mismatch: have %d, want %d"
,
i
,
pool
.
queue
[
account
]
.
Len
(
),
maxQueued
)
}
}
}
...
...
@@ -488,11 +485,11 @@ func TestTransactionPendingLimiting(t *testing.T) {
if
err
:=
pool
.
Add
(
transaction
(
i
,
big
.
NewInt
(
100000
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"tx %d: failed to add transaction: %v"
,
i
,
err
)
}
if
len
(
pool
.
pending
[
account
]
)
!=
int
(
i
)
+
1
{
t
.
Errorf
(
"tx %d: pending pool size mismatch: have %d, want %d"
,
i
,
len
(
pool
.
pending
[
account
]
),
i
+
1
)
if
pool
.
pending
[
account
]
.
Len
(
)
!=
int
(
i
)
+
1
{
t
.
Errorf
(
"tx %d: pending pool size mismatch: have %d, want %d"
,
i
,
pool
.
pending
[
account
]
.
Len
(
),
i
+
1
)
}
if
len
(
pool
.
queue
[
account
]
)
!=
0
{
t
.
Errorf
(
"tx %d: queue size mismatch: have %d, want %d"
,
i
,
len
(
pool
.
queue
[
account
]
),
0
)
if
len
(
pool
.
queue
)
!=
0
{
t
.
Errorf
(
"tx %d: queue size mismatch: have %d, want %d"
,
i
,
pool
.
queue
[
account
]
.
Len
(
),
0
)
}
}
if
len
(
pool
.
all
)
!=
maxQueued
+
5
{
...
...
@@ -517,7 +514,7 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
t
.
Fatalf
(
"tx %d: failed to add transaction: %v"
,
i
,
err
)
}
}
// Add a batch of transactions to a pool in one bi
t
batch
// Add a batch of transactions to a pool in one bi
g
batch
pool2
,
key2
:=
setupTxPool
()
account2
,
_
:=
transaction
(
0
,
big
.
NewInt
(
0
),
key2
)
.
From
()
state2
,
_
:=
pool2
.
currentState
()
...
...
@@ -527,14 +524,14 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
for
i
:=
uint64
(
0
);
i
<
maxQueued
+
5
;
i
++
{
txns
=
append
(
txns
,
transaction
(
origin
+
i
,
big
.
NewInt
(
100000
),
key2
))
}
pool2
.
Add
Transactions
(
txns
)
pool2
.
Add
Batch
(
txns
)
// Ensure the batch optimization honors the same pool mechanics
if
len
(
pool1
.
pending
)
!=
len
(
pool2
.
pending
)
{
t
.
Errorf
(
"pending transaction count mismatch: one-by-one algo: %d, batch algo: %d"
,
len
(
pool1
.
pending
),
len
(
pool2
.
pending
))
}
if
len
(
pool1
.
queue
[
account1
])
!=
len
(
pool2
.
queue
[
account2
]
)
{
t
.
Errorf
(
"queued transaction count mismatch: one-by-one algo: %d, batch algo: %d"
,
len
(
pool1
.
queue
[
account1
]),
len
(
pool2
.
queue
[
account2
]
))
if
len
(
pool1
.
queue
)
!=
len
(
pool2
.
queue
)
{
t
.
Errorf
(
"queued transaction count mismatch: one-by-one algo: %d, batch algo: %d"
,
len
(
pool1
.
queue
),
len
(
pool2
.
queue
))
}
if
len
(
pool1
.
all
)
!=
len
(
pool2
.
all
)
{
t
.
Errorf
(
"total transaction count mismatch: one-by-one algo %d, batch algo %d"
,
len
(
pool1
.
all
),
len
(
pool2
.
all
))
...
...
@@ -543,11 +540,11 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
// Benchmarks the speed of validating the contents of the pending queue of the
// transaction pool.
func
Benchmark
ValidatePool100
(
b
*
testing
.
B
)
{
benchmarkValidatePool
(
b
,
100
)
}
func
Benchmark
ValidatePool1000
(
b
*
testing
.
B
)
{
benchmarkValidatePool
(
b
,
1000
)
}
func
Benchmark
ValidatePool10000
(
b
*
testing
.
B
)
{
benchmarkValidatePool
(
b
,
10000
)
}
func
Benchmark
PendingDemotion100
(
b
*
testing
.
B
)
{
benchmarkPendingDemotion
(
b
,
100
)
}
func
Benchmark
PendingDemotion1000
(
b
*
testing
.
B
)
{
benchmarkPendingDemotion
(
b
,
1000
)
}
func
Benchmark
PendingDemotion10000
(
b
*
testing
.
B
)
{
benchmarkPendingDemotion
(
b
,
10000
)
}
func
benchmark
ValidatePool
(
b
*
testing
.
B
,
size
int
)
{
func
benchmark
PendingDemotion
(
b
*
testing
.
B
,
size
int
)
{
// Add a batch of transactions to a pool one by one
pool
,
key
:=
setupTxPool
()
account
,
_
:=
transaction
(
0
,
big
.
NewInt
(
0
),
key
)
.
From
()
...
...
@@ -556,22 +553,22 @@ func benchmarkValidatePool(b *testing.B, size int) {
for
i
:=
0
;
i
<
size
;
i
++
{
tx
:=
transaction
(
uint64
(
i
),
big
.
NewInt
(
100000
),
key
)
pool
.
addTx
(
account
,
tx
)
pool
.
promoteTx
(
account
,
tx
.
Hash
()
,
tx
)
}
// Benchmark the speed of pool validation
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
pool
.
validatePool
()
pool
.
demoteUnexecutables
()
}
}
// Benchmarks the speed of scheduling the contents of the future queue of the
// transaction pool.
func
Benchmark
CheckQueue100
(
b
*
testing
.
B
)
{
benchmarkCheckQueue
(
b
,
100
)
}
func
Benchmark
CheckQueue1000
(
b
*
testing
.
B
)
{
benchmarkCheckQueue
(
b
,
1000
)
}
func
Benchmark
CheckQueue10000
(
b
*
testing
.
B
)
{
benchmarkCheckQueue
(
b
,
10000
)
}
func
Benchmark
FuturePromotion100
(
b
*
testing
.
B
)
{
benchmarkFuturePromotion
(
b
,
100
)
}
func
Benchmark
FuturePromotion1000
(
b
*
testing
.
B
)
{
benchmarkFuturePromotion
(
b
,
1000
)
}
func
Benchmark
FuturePromotion10000
(
b
*
testing
.
B
)
{
benchmarkFuturePromotion
(
b
,
10000
)
}
func
benchmark
CheckQueue
(
b
*
testing
.
B
,
size
int
)
{
func
benchmark
FuturePromotion
(
b
*
testing
.
B
,
size
int
)
{
// Add a batch of transactions to a pool one by one
pool
,
key
:=
setupTxPool
()
account
,
_
:=
transaction
(
0
,
big
.
NewInt
(
0
),
key
)
.
From
()
...
...
@@ -580,11 +577,56 @@ func benchmarkCheckQueue(b *testing.B, size int) {
for
i
:=
0
;
i
<
size
;
i
++
{
tx
:=
transaction
(
uint64
(
1
+
i
),
big
.
NewInt
(
100000
),
key
)
pool
.
queueTx
(
tx
.
Hash
(),
tx
)
pool
.
en
queueTx
(
tx
.
Hash
(),
tx
)
}
// Benchmark the speed of pool validation
b
.
ResetTimer
()
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
pool
.
checkQueue
()
pool
.
promoteExecutables
()
}
}
// Benchmarks the speed of iterative transaction insertion.
func
BenchmarkPoolInsert
(
b
*
testing
.
B
)
{
// Generate a batch of transactions to enqueue into the pool
pool
,
key
:=
setupTxPool
()
account
,
_
:=
transaction
(
0
,
big
.
NewInt
(
0
),
key
)
.
From
()
state
,
_
:=
pool
.
currentState
()
state
.
AddBalance
(
account
,
big
.
NewInt
(
1000000
))
txs
:=
make
(
types
.
Transactions
,
b
.
N
)
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
txs
[
i
]
=
transaction
(
uint64
(
i
),
big
.
NewInt
(
100000
),
key
)
}
// Benchmark importing the transactions into the queue
b
.
ResetTimer
()
for
_
,
tx
:=
range
txs
{
pool
.
Add
(
tx
)
}
}
// Benchmarks the speed of batched transaction insertion.
func
BenchmarkPoolBatchInsert100
(
b
*
testing
.
B
)
{
benchmarkPoolBatchInsert
(
b
,
100
)
}
func
BenchmarkPoolBatchInsert1000
(
b
*
testing
.
B
)
{
benchmarkPoolBatchInsert
(
b
,
1000
)
}
func
BenchmarkPoolBatchInsert10000
(
b
*
testing
.
B
)
{
benchmarkPoolBatchInsert
(
b
,
10000
)
}
func
benchmarkPoolBatchInsert
(
b
*
testing
.
B
,
size
int
)
{
// Generate a batch of transactions to enqueue into the pool
pool
,
key
:=
setupTxPool
()
account
,
_
:=
transaction
(
0
,
big
.
NewInt
(
0
),
key
)
.
From
()
state
,
_
:=
pool
.
currentState
()
state
.
AddBalance
(
account
,
big
.
NewInt
(
1000000
))
batches
:=
make
([]
types
.
Transactions
,
b
.
N
)
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
batches
[
i
]
=
make
(
types
.
Transactions
,
size
)
for
j
:=
0
;
j
<
size
;
j
++
{
batches
[
i
][
j
]
=
transaction
(
uint64
(
size
*
i
+
j
),
big
.
NewInt
(
100000
),
key
)
}
}
// Benchmark importing the transactions into the queue
b
.
ResetTimer
()
for
_
,
batch
:=
range
batches
{
pool
.
AddBatch
(
batch
)
}
}
core/types/transaction.go
浏览文件 @
0ef327bb
...
...
@@ -24,7 +24,6 @@ import (
"fmt"
"io"
"math/big"
"sort"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
...
...
@@ -439,37 +438,29 @@ func (s *TxByPrice) Pop() interface{} {
// sender accounts and sorts them by nonce. After the account nonce ordering is
// satisfied, the results are merged back together by price, always comparing only
// the head transaction from each account. This is done via a heap to keep it fast.
func
SortByPriceAndNonce
(
txs
[]
*
Transaction
)
{
// Separate the transactions by account and sort by nonce
byNonce
:=
make
(
map
[
common
.
Address
][]
*
Transaction
)
for
_
,
tx
:=
range
txs
{
acc
,
_
:=
tx
.
From
()
// we only sort valid txs so this cannot fail
byNonce
[
acc
]
=
append
(
byNonce
[
acc
],
tx
)
}
for
_
,
accTxs
:=
range
byNonce
{
sort
.
Sort
(
TxByNonce
(
accTxs
))
}
func
SortByPriceAndNonce
(
txs
map
[
common
.
Address
]
Transactions
)
Transactions
{
// Initialize a price based heap with the head transactions
byPrice
:=
make
(
TxByPrice
,
0
,
len
(
byNonce
))
for
acc
,
accTxs
:=
range
byNonce
{
byPrice
:=
make
(
TxByPrice
,
0
,
len
(
txs
))
for
acc
,
accTxs
:=
range
txs
{
byPrice
=
append
(
byPrice
,
accTxs
[
0
])
byNonce
[
acc
]
=
accTxs
[
1
:
]
txs
[
acc
]
=
accTxs
[
1
:
]
}
heap
.
Init
(
&
byPrice
)
// Merge by replacing the best with the next from the same account
txs
=
txs
[
:
0
]
var
sorted
Transactions
for
len
(
byPrice
)
>
0
{
// Retrieve the next best transaction by price
best
:=
heap
.
Pop
(
&
byPrice
)
.
(
*
Transaction
)
// Push in its place the next transaction from the same account
acc
,
_
:=
best
.
From
()
// we only sort valid txs so this cannot fail
if
accTxs
,
ok
:=
byNonce
[
acc
];
ok
&&
len
(
accTxs
)
>
0
{
if
accTxs
,
ok
:=
txs
[
acc
];
ok
&&
len
(
accTxs
)
>
0
{
heap
.
Push
(
&
byPrice
,
accTxs
[
0
])
byNonce
[
acc
]
=
accTxs
[
1
:
]
txs
[
acc
]
=
accTxs
[
1
:
]
}
// Accumulate the best priced transaction
txs
=
append
(
txs
,
best
)
sorted
=
append
(
sorted
,
best
)
}
return
sorted
}
core/types/transaction_test.go
浏览文件 @
0ef327bb
...
...
@@ -128,15 +128,16 @@ func TestTransactionPriceNonceSort(t *testing.T) {
keys
[
i
],
_
=
crypto
.
GenerateKey
()
}
// Generate a batch of transactions with overlapping values, but shifted nonces
txs
:=
[]
*
Transaction
{}
groups
:=
map
[
common
.
Address
]
Transactions
{}
for
start
,
key
:=
range
keys
{
addr
:=
crypto
.
PubkeyToAddress
(
key
.
PublicKey
)
for
i
:=
0
;
i
<
25
;
i
++
{
tx
,
_
:=
NewTransaction
(
uint64
(
start
+
i
),
common
.
Address
{},
big
.
NewInt
(
100
),
big
.
NewInt
(
100
),
big
.
NewInt
(
int64
(
start
+
i
)),
nil
)
.
SignECDSA
(
key
)
txs
=
append
(
txs
,
tx
)
groups
[
addr
]
=
append
(
groups
[
addr
]
,
tx
)
}
}
// Sort the transactions and cross check the nonce ordering
SortByPriceAndNonce
(
tx
s
)
txs
:=
SortByPriceAndNonce
(
group
s
)
for
i
,
txi
:=
range
txs
{
fromi
,
_
:=
txi
.
From
()
...
...
eth/api_backend.go
浏览文件 @
0ef327bb
...
...
@@ -118,21 +118,25 @@ func (b *EthApiBackend) RemoveTx(txHash common.Hash) {
b
.
eth
.
txMu
.
Lock
()
defer
b
.
eth
.
txMu
.
Unlock
()
b
.
eth
.
txPool
.
Remove
Tx
(
txHash
)
b
.
eth
.
txPool
.
Remove
(
txHash
)
}
func
(
b
*
EthApiBackend
)
GetPoolTransactions
()
types
.
Transactions
{
b
.
eth
.
txMu
.
Lock
()
defer
b
.
eth
.
txMu
.
Unlock
()
return
b
.
eth
.
txPool
.
GetTransactions
()
var
txs
types
.
Transactions
for
_
,
batch
:=
range
b
.
eth
.
txPool
.
Pending
()
{
txs
=
append
(
txs
,
batch
...
)
}
return
txs
}
func
(
b
*
EthApiBackend
)
GetPoolTransaction
(
txH
ash
common
.
Hash
)
*
types
.
Transaction
{
func
(
b
*
EthApiBackend
)
GetPoolTransaction
(
h
ash
common
.
Hash
)
*
types
.
Transaction
{
b
.
eth
.
txMu
.
Lock
()
defer
b
.
eth
.
txMu
.
Unlock
()
return
b
.
eth
.
txPool
.
Get
Transaction
(
txH
ash
)
return
b
.
eth
.
txPool
.
Get
(
h
ash
)
}
func
(
b
*
EthApiBackend
)
GetPoolNonce
(
ctx
context
.
Context
,
addr
common
.
Address
)
(
uint64
,
error
)
{
...
...
@@ -149,7 +153,7 @@ func (b *EthApiBackend) Stats() (pending int, queued int) {
return
b
.
eth
.
txPool
.
Stats
()
}
func
(
b
*
EthApiBackend
)
TxPoolContent
()
(
map
[
common
.
Address
]
core
.
TxList
,
map
[
common
.
Address
]
core
.
TxList
)
{
func
(
b
*
EthApiBackend
)
TxPoolContent
()
(
map
[
common
.
Address
]
types
.
Transactions
,
map
[
common
.
Address
]
types
.
Transactions
)
{
b
.
eth
.
txMu
.
Lock
()
defer
b
.
eth
.
txMu
.
Unlock
()
...
...
eth/handler.go
浏览文件 @
0ef327bb
...
...
@@ -677,7 +677,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
}
p
.
MarkTransaction
(
tx
.
Hash
())
}
pm
.
txpool
.
Add
Transactions
(
txs
)
pm
.
txpool
.
Add
Batch
(
txs
)
default
:
return
errResp
(
ErrInvalidMsgCode
,
"%v"
,
msg
.
Code
)
...
...
eth/helper_test.go
浏览文件 @
0ef327bb
...
...
@@ -23,6 +23,7 @@ import (
"crypto/ecdsa"
"crypto/rand"
"math/big"
"sort"
"sync"
"testing"
...
...
@@ -89,9 +90,9 @@ type testTxPool struct {
lock
sync
.
RWMutex
// Protects the transaction pool
}
// Add
Transactions
appends a batch of transactions to the pool, and notifies any
// Add
Batch
appends a batch of transactions to the pool, and notifies any
// listeners if the addition channel is non nil
func
(
p
*
testTxPool
)
Add
Transactions
(
txs
[]
*
types
.
Transaction
)
{
func
(
p
*
testTxPool
)
Add
Batch
(
txs
[]
*
types
.
Transaction
)
{
p
.
lock
.
Lock
()
defer
p
.
lock
.
Unlock
()
...
...
@@ -101,15 +102,20 @@ func (p *testTxPool) AddTransactions(txs []*types.Transaction) {
}
}
//
GetTransactions
returns all the transactions known to the pool
func
(
p
*
testTxPool
)
GetTransactions
()
types
.
Transactions
{
//
Pending
returns all the transactions known to the pool
func
(
p
*
testTxPool
)
Pending
()
map
[
common
.
Address
]
types
.
Transactions
{
p
.
lock
.
RLock
()
defer
p
.
lock
.
RUnlock
()
txs
:=
make
([]
*
types
.
Transaction
,
len
(
p
.
pool
))
copy
(
txs
,
p
.
pool
)
return
txs
batches
:=
make
(
map
[
common
.
Address
]
types
.
Transactions
)
for
_
,
tx
:=
range
p
.
pool
{
from
,
_
:=
tx
.
From
()
batches
[
from
]
=
append
(
batches
[
from
],
tx
)
}
for
_
,
batch
:=
range
batches
{
sort
.
Sort
(
types
.
TxByNonce
(
batch
))
}
return
batches
}
// newTestTransaction create a new dummy transaction.
...
...
eth/protocol.go
浏览文件 @
0ef327bb
...
...
@@ -97,12 +97,12 @@ var errorToString = map[int]string{
}
type
txPool
interface
{
// Add
Transactions
should add the given transactions to the pool.
Add
Transactions
([]
*
types
.
Transaction
)
// Add
Batch
should add the given transactions to the pool.
Add
Batch
([]
*
types
.
Transaction
)
//
GetTransactions
should return pending transactions.
//
Pending
should return pending transactions.
// The slice should be modifiable by the caller.
GetTransactions
()
types
.
Transactions
Pending
()
map
[
common
.
Address
]
types
.
Transactions
}
// statusData is the network packet for the status message.
...
...
eth/protocol_test.go
浏览文件 @
0ef327bb
...
...
@@ -130,7 +130,7 @@ func testSendTransactions(t *testing.T, protocol int) {
for
nonce
:=
range
alltxs
{
alltxs
[
nonce
]
=
newTestTransaction
(
testAccount
,
uint64
(
nonce
),
txsize
)
}
pm
.
txpool
.
Add
Transactions
(
alltxs
)
pm
.
txpool
.
Add
Batch
(
alltxs
)
// Connect several peers. They should all receive the pending transactions.
var
wg
sync
.
WaitGroup
...
...
eth/sync.go
浏览文件 @
0ef327bb
...
...
@@ -45,7 +45,10 @@ type txsync struct {
// syncTransactions starts sending all currently pending transactions to the given peer.
func
(
pm
*
ProtocolManager
)
syncTransactions
(
p
*
peer
)
{
txs
:=
pm
.
txpool
.
GetTransactions
()
var
txs
types
.
Transactions
for
_
,
batch
:=
range
pm
.
txpool
.
Pending
()
{
txs
=
append
(
txs
,
batch
...
)
}
if
len
(
txs
)
==
0
{
return
}
...
...
internal/ethapi/backend.go
浏览文件 @
0ef327bb
...
...
@@ -58,7 +58,7 @@ type Backend interface {
GetPoolTransaction
(
txHash
common
.
Hash
)
*
types
.
Transaction
GetPoolNonce
(
ctx
context
.
Context
,
addr
common
.
Address
)
(
uint64
,
error
)
Stats
()
(
pending
int
,
queued
int
)
TxPoolContent
()
(
map
[
common
.
Address
]
core
.
TxList
,
map
[
common
.
Address
]
core
.
TxList
)
TxPoolContent
()
(
map
[
common
.
Address
]
types
.
Transactions
,
map
[
common
.
Address
]
types
.
Transactions
)
}
type
State
interface
{
...
...
miner/worker.go
浏览文件 @
0ef327bb
...
...
@@ -501,8 +501,7 @@ func (self *worker) commitNewWork() {
*/
//approach 2
transactions
:=
self
.
eth
.
TxPool
()
.
GetTransactions
()
types
.
SortByPriceAndNonce
(
transactions
)
transactions
:=
types
.
SortByPriceAndNonce
(
self
.
eth
.
TxPool
()
.
Pending
())
/* // approach 3
// commit transactions for this run.
...
...
@@ -533,8 +532,8 @@ func (self *worker) commitNewWork() {
work
.
commitTransactions
(
self
.
mux
,
transactions
,
self
.
gasPrice
,
self
.
chain
)
self
.
eth
.
TxPool
()
.
Remove
Transactions
(
work
.
lowGasTxs
)
self
.
eth
.
TxPool
()
.
Remove
Transactions
(
work
.
failedTxs
)
self
.
eth
.
TxPool
()
.
Remove
Batch
(
work
.
lowGasTxs
)
self
.
eth
.
TxPool
()
.
Remove
Batch
(
work
.
failedTxs
)
// compute uncles for the new block.
var
(
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录