Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
a2f23ca9
Commit
a2f23ca9
authored
May 16, 2017
by
Péter Szilágyi
Committed by
Felix Lange
May 16, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
cmd, core, eth, miner: remove txpool gas price limits (#14442)
parent
e2015817
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
619 additions
and
178 deletions
+619
-178
flags.go
cmd/utils/flags.go
+1
-1
events.go
core/events.go
+0
-4
tx_list.go
core/tx_list.go
+162
-4
tx_pool.go
core/tx_pool.go
+195
-74
tx_pool_test.go
core/tx_pool_test.go
+242
-19
api.go
eth/api.go
+3
-1
backend.go
eth/backend.go
+3
-1
config.go
eth/config.go
+1
-1
ethstats.go
ethstats/ethstats.go
+3
-1
miner.go
miner/miner.go
+0
-13
worker.go
miner/worker.go
+9
-59
No files found.
cmd/utils/flags.go
View file @
a2f23ca9
...
@@ -237,7 +237,7 @@ var (
...
@@ -237,7 +237,7 @@ var (
GasPriceFlag
=
BigFlag
{
GasPriceFlag
=
BigFlag
{
Name
:
"gasprice"
,
Name
:
"gasprice"
,
Usage
:
"Minimal gas price to accept for mining a transactions"
,
Usage
:
"Minimal gas price to accept for mining a transactions"
,
Value
:
big
.
NewInt
(
20
*
params
.
Shannon
)
,
Value
:
eth
.
DefaultConfig
.
GasPrice
,
}
}
ExtraDataFlag
=
cli
.
StringFlag
{
ExtraDataFlag
=
cli
.
StringFlag
{
Name
:
"extradata"
,
Name
:
"extradata"
,
...
...
core/events.go
View file @
a2f23ca9
...
@@ -17,8 +17,6 @@
...
@@ -17,8 +17,6 @@
package
core
package
core
import
(
import
(
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
)
)
...
@@ -67,8 +65,6 @@ type ChainUncleEvent struct {
...
@@ -67,8 +65,6 @@ type ChainUncleEvent struct {
type
ChainHeadEvent
struct
{
Block
*
types
.
Block
}
type
ChainHeadEvent
struct
{
Block
*
types
.
Block
}
type
GasPriceChanged
struct
{
Price
*
big
.
Int
}
// Mining operation events
// Mining operation events
type
StartMining
struct
{}
type
StartMining
struct
{}
type
TopMining
struct
{}
type
TopMining
struct
{}
core/tx_list.go
View file @
a2f23ca9
...
@@ -22,7 +22,9 @@ import (
...
@@ -22,7 +22,9 @@ import (
"math/big"
"math/big"
"sort"
"sort"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
)
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
...
@@ -53,11 +55,11 @@ type txSortedMap struct {
...
@@ -53,11 +55,11 @@ type txSortedMap struct {
cache
types
.
Transactions
// Cache of the transactions already sorted
cache
types
.
Transactions
// Cache of the transactions already sorted
}
}
// newTxSortedMap creates a new sorted transaction map.
// newTxSortedMap creates a new
nonce-
sorted transaction map.
func
newTxSortedMap
()
*
txSortedMap
{
func
newTxSortedMap
()
*
txSortedMap
{
return
&
txSortedMap
{
return
&
txSortedMap
{
items
:
make
(
map
[
uint64
]
*
types
.
Transaction
),
items
:
make
(
map
[
uint64
]
*
types
.
Transaction
),
index
:
&
nonceHeap
{}
,
index
:
new
(
nonceHeap
)
,
}
}
}
}
...
@@ -233,6 +235,12 @@ func newTxList(strict bool) *txList {
...
@@ -233,6 +235,12 @@ func newTxList(strict bool) *txList {
}
}
}
}
// Overlaps returns whether the transaction specified has the same nonce as one
// already contained within the list.
func
(
l
*
txList
)
Overlaps
(
tx
*
types
.
Transaction
)
bool
{
return
l
.
txs
.
Get
(
tx
.
Nonce
())
!=
nil
}
// Add tries to insert a new transaction into the list, returning whether the
// Add tries to insert a new transaction into the list, returning whether the
// transaction was accepted, and if yes, any previous transaction it replaced.
// transaction was accepted, and if yes, any previous transaction it replaced.
//
//
...
@@ -241,8 +249,11 @@ func newTxList(strict bool) *txList {
...
@@ -241,8 +249,11 @@ func newTxList(strict bool) *txList {
func
(
l
*
txList
)
Add
(
tx
*
types
.
Transaction
)
(
bool
,
*
types
.
Transaction
)
{
func
(
l
*
txList
)
Add
(
tx
*
types
.
Transaction
)
(
bool
,
*
types
.
Transaction
)
{
// If there's an older better transaction, abort
// If there's an older better transaction, abort
old
:=
l
.
txs
.
Get
(
tx
.
Nonce
())
old
:=
l
.
txs
.
Get
(
tx
.
Nonce
())
if
old
!=
nil
&&
old
.
GasPrice
()
.
Cmp
(
tx
.
GasPrice
())
>=
0
{
if
old
!=
nil
{
return
false
,
nil
threshold
:=
new
(
big
.
Int
)
.
Div
(
new
(
big
.
Int
)
.
Mul
(
old
.
GasPrice
(),
big
.
NewInt
(
100
+
minPriceBumpPercent
)),
big
.
NewInt
(
100
))
if
threshold
.
Cmp
(
tx
.
GasPrice
())
>=
0
{
return
false
,
nil
}
}
}
// Otherwise overwrite the old transaction with the current one
// Otherwise overwrite the old transaction with the current one
l
.
txs
.
Put
(
tx
)
l
.
txs
.
Put
(
tx
)
...
@@ -340,3 +351,150 @@ func (l *txList) Empty() bool {
...
@@ -340,3 +351,150 @@ func (l *txList) Empty() bool {
func
(
l
*
txList
)
Flatten
()
types
.
Transactions
{
func
(
l
*
txList
)
Flatten
()
types
.
Transactions
{
return
l
.
txs
.
Flatten
()
return
l
.
txs
.
Flatten
()
}
}
// priceHeap is a heap.Interface implementation over transactions for retrieving
// price-sorted transactions to discard when the pool fills up.
type
priceHeap
[]
*
types
.
Transaction
func
(
h
priceHeap
)
Len
()
int
{
return
len
(
h
)
}
func
(
h
priceHeap
)
Less
(
i
,
j
int
)
bool
{
return
h
[
i
]
.
GasPrice
()
.
Cmp
(
h
[
j
]
.
GasPrice
())
<
0
}
func
(
h
priceHeap
)
Swap
(
i
,
j
int
)
{
h
[
i
],
h
[
j
]
=
h
[
j
],
h
[
i
]
}
func
(
h
*
priceHeap
)
Push
(
x
interface
{})
{
*
h
=
append
(
*
h
,
x
.
(
*
types
.
Transaction
))
}
func
(
h
*
priceHeap
)
Pop
()
interface
{}
{
old
:=
*
h
n
:=
len
(
old
)
x
:=
old
[
n
-
1
]
*
h
=
old
[
0
:
n
-
1
]
return
x
}
// txPricedList is a price-sorted heap to allow operating on transactions pool
// contents in a price-incrementing way.
type
txPricedList
struct
{
all
*
map
[
common
.
Hash
]
*
types
.
Transaction
// Pointer to the map of all transactions
items
*
priceHeap
// Heap of prices of all the stored transactions
stales
int
// Number of stale price points to (re-heap trigger)
}
// newTxPricedList creates a new price-sorted transaction heap.
func
newTxPricedList
(
all
*
map
[
common
.
Hash
]
*
types
.
Transaction
)
*
txPricedList
{
return
&
txPricedList
{
all
:
all
,
items
:
new
(
priceHeap
),
}
}
// Put inserts a new transaction into the heap.
func
(
l
*
txPricedList
)
Put
(
tx
*
types
.
Transaction
)
{
heap
.
Push
(
l
.
items
,
tx
)
}
// Removed notifies the prices transaction list that an old transaction dropped
// from the pool. The list will just keep a counter of stale objects and update
// the heap if a large enough ratio of transactions go stale.
func
(
l
*
txPricedList
)
Removed
()
{
// Bump the stale counter, but exit if still too low (< 25%)
l
.
stales
++
if
l
.
stales
<=
len
(
*
l
.
items
)
/
4
{
return
}
// Seems we've reached a critical number of stale transactions, reheap
reheap
:=
make
(
priceHeap
,
0
,
len
(
*
l
.
all
))
l
.
stales
,
l
.
items
=
0
,
&
reheap
for
_
,
tx
:=
range
*
l
.
all
{
*
l
.
items
=
append
(
*
l
.
items
,
tx
)
}
heap
.
Init
(
l
.
items
)
}
// Discard finds all the transactions below the given price threshold, drops them
// from the priced list and returs them for further removal from the entire pool.
func
(
l
*
txPricedList
)
Cap
(
threshold
*
big
.
Int
,
local
*
txSet
)
types
.
Transactions
{
drop
:=
make
(
types
.
Transactions
,
0
,
128
)
// Remote underpriced transactions to drop
save
:=
make
(
types
.
Transactions
,
0
,
64
)
// Local underpriced transactions to keep
for
len
(
*
l
.
items
)
>
0
{
// Discard stale transactions if found during cleanup
tx
:=
heap
.
Pop
(
l
.
items
)
.
(
*
types
.
Transaction
)
hash
:=
tx
.
Hash
()
if
_
,
ok
:=
(
*
l
.
all
)[
hash
];
!
ok
{
l
.
stales
--
continue
}
// Stop the discards if we've reached the threshold
if
tx
.
GasPrice
()
.
Cmp
(
threshold
)
>=
0
{
break
}
// Non stale transaction found, discard unless local
if
local
.
contains
(
hash
)
{
save
=
append
(
save
,
tx
)
}
else
{
drop
=
append
(
drop
,
tx
)
}
}
for
_
,
tx
:=
range
save
{
heap
.
Push
(
l
.
items
,
tx
)
}
return
drop
}
// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
// lowest priced transaction currently being tracked.
func
(
l
*
txPricedList
)
Underpriced
(
tx
*
types
.
Transaction
,
local
*
txSet
)
bool
{
// Local transactions cannot be underpriced
if
local
.
contains
(
tx
.
Hash
())
{
return
false
}
// Discard stale price points if found at the heap start
for
len
(
*
l
.
items
)
>
0
{
head
:=
[]
*
types
.
Transaction
(
*
l
.
items
)[
0
]
if
_
,
ok
:=
(
*
l
.
all
)[
head
.
Hash
()];
!
ok
{
l
.
stales
--
heap
.
Pop
(
l
.
items
)
continue
}
break
}
// Check if the transaction is underpriced or not
if
len
(
*
l
.
items
)
==
0
{
log
.
Error
(
"Pricing query for empty pool"
)
// This cannot happen, print to catch programming errors
return
false
}
cheapest
:=
[]
*
types
.
Transaction
(
*
l
.
items
)[
0
]
return
cheapest
.
GasPrice
()
.
Cmp
(
tx
.
GasPrice
())
>=
0
}
// Discard finds a number of most underpriced transactions, removes them from the
// priced list and returs them for further removal from the entire pool.
func
(
l
*
txPricedList
)
Discard
(
count
int
,
local
*
txSet
)
types
.
Transactions
{
drop
:=
make
(
types
.
Transactions
,
0
,
count
)
// Remote underpriced transactions to drop
save
:=
make
(
types
.
Transactions
,
0
,
64
)
// Local underpriced transactions to keep
for
len
(
*
l
.
items
)
>
0
&&
count
>
0
{
// Discard stale transactions if found during cleanup
tx
:=
heap
.
Pop
(
l
.
items
)
.
(
*
types
.
Transaction
)
hash
:=
tx
.
Hash
()
if
_
,
ok
:=
(
*
l
.
all
)[
hash
];
!
ok
{
l
.
stales
--
continue
}
// Non stale transaction found, discard unless local
if
local
.
contains
(
hash
)
{
save
=
append
(
save
,
tx
)
}
else
{
drop
=
append
(
drop
,
tx
)
count
--
}
}
for
_
,
tx
:=
range
save
{
heap
.
Push
(
l
.
items
,
tx
)
}
return
drop
}
core/tx_pool.go
View file @
a2f23ca9
...
@@ -36,23 +36,26 @@ import (
...
@@ -36,23 +36,26 @@ import (
var
(
var
(
// Transaction Pool Errors
// Transaction Pool Errors
ErrInvalidSender
=
errors
.
New
(
"Invalid sender"
)
ErrInvalidSender
=
errors
.
New
(
"invalid sender"
)
ErrNonce
=
errors
.
New
(
"Nonce too low"
)
ErrNonce
=
errors
.
New
(
"nonce too low"
)
ErrCheap
=
errors
.
New
(
"Gas price too low for acceptance"
)
ErrUnderpriced
=
errors
.
New
(
"transaction underpriced"
)
ErrBalance
=
errors
.
New
(
"Insufficient balance"
)
ErrReplaceUnderpriced
=
errors
.
New
(
"replacement transaction underpriced"
)
ErrInsufficientFunds
=
errors
.
New
(
"Insufficient funds for gas * price + value"
)
ErrBalance
=
errors
.
New
(
"insufficient balance"
)
ErrIntrinsicGas
=
errors
.
New
(
"Intrinsic gas too low"
)
ErrInsufficientFunds
=
errors
.
New
(
"insufficient funds for gas * price + value"
)
ErrGasLimit
=
errors
.
New
(
"Exceeds block gas limit"
)
ErrIntrinsicGas
=
errors
.
New
(
"intrinsic gas too low"
)
ErrNegativeValue
=
errors
.
New
(
"Negative value"
)
ErrGasLimit
=
errors
.
New
(
"exceeds block gas limit"
)
ErrNegativeValue
=
errors
.
New
(
"negative value"
)
)
)
var
(
var
(
minPendingPerAccount
=
uint64
(
16
)
// Min number of guaranteed transaction slots per address
minPendingPerAccount
=
uint64
(
16
)
// Min number of guaranteed transaction slots per address
maxPendingTotal
=
uint64
(
4096
)
// Max limit of pending transactions from all accounts (soft)
maxPendingTotal
=
uint64
(
4096
)
// Max limit of pending transactions from all accounts (soft)
maxQueuedPerAccount
=
uint64
(
64
)
// Max limit of queued transactions per address
maxQueuedPerAccount
=
uint64
(
64
)
// Max limit of queued transactions per address
maxQueuedInTotal
=
uint64
(
1024
)
// Max limit of queued transactions from all accounts
maxQueuedTotal
=
uint64
(
1024
)
// Max limit of queued transactions from all accounts
maxQueuedLifetime
=
3
*
time
.
Hour
// Max amount of time transactions from idle accounts are queued
maxQueuedLifetime
=
3
*
time
.
Hour
// Max amount of time transactions from idle accounts are queued
evictionInterval
=
time
.
Minute
// Time interval to check for evictable transactions
minPriceBumpPercent
=
int64
(
10
)
// Minimum price bump needed to replace an old transaction
evictionInterval
=
time
.
Minute
// Time interval to check for evictable transactions
statsReportInterval
=
8
*
time
.
Second
// Time interval to report transaction pool stats
)
)
var
(
var
(
...
@@ -69,7 +72,8 @@ var (
...
@@ -69,7 +72,8 @@ var (
queuedNofundsCounter
=
metrics
.
NewCounter
(
"txpool/queued/nofunds"
)
// Dropped due to out-of-funds
queuedNofundsCounter
=
metrics
.
NewCounter
(
"txpool/queued/nofunds"
)
// Dropped due to out-of-funds
// General tx metrics
// General tx metrics
invalidTxCounter
=
metrics
.
NewCounter
(
"txpool/invalid"
)
invalidTxCounter
=
metrics
.
NewCounter
(
"txpool/invalid"
)
underpricedTxCounter
=
metrics
.
NewCounter
(
"txpool/underpriced"
)
)
)
type
stateFn
func
()
(
*
state
.
StateDB
,
error
)
type
stateFn
func
()
(
*
state
.
StateDB
,
error
)
...
@@ -86,17 +90,18 @@ type TxPool struct {
...
@@ -86,17 +90,18 @@ type TxPool struct {
currentState
stateFn
// The state function which will allow us to do some pre checks
currentState
stateFn
// The state function which will allow us to do some pre checks
pendingState
*
state
.
ManagedState
pendingState
*
state
.
ManagedState
gasLimit
func
()
*
big
.
Int
// The current gas limit function callback
gasLimit
func
()
*
big
.
Int
// The current gas limit function callback
minGasPrice
*
big
.
Int
gasPrice
*
big
.
Int
eventMux
*
event
.
TypeMux
eventMux
*
event
.
TypeMux
events
*
event
.
TypeMuxSubscription
events
*
event
.
TypeMuxSubscription
local
Tx
*
txSet
local
s
*
txSet
signer
types
.
Signer
signer
types
.
Signer
mu
sync
.
RWMutex
mu
sync
.
RWMutex
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
beats
map
[
common
.
Address
]
time
.
Time
// Last heartbeat from each known account
beats
map
[
common
.
Address
]
time
.
Time
// Last heartbeat from each known account
all
map
[
common
.
Hash
]
*
types
.
Transaction
// All transactions to allow lookups
priced
*
txPricedList
// All transactions sorted by price
wg
sync
.
WaitGroup
// for shutdown sync
wg
sync
.
WaitGroup
// for shutdown sync
quit
chan
struct
{}
quit
chan
struct
{}
...
@@ -110,18 +115,18 @@ func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentState
...
@@ -110,18 +115,18 @@ func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentState
signer
:
types
.
NewEIP155Signer
(
config
.
ChainId
),
signer
:
types
.
NewEIP155Signer
(
config
.
ChainId
),
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
),
beats
:
make
(
map
[
common
.
Address
]
time
.
Time
),
beats
:
make
(
map
[
common
.
Address
]
time
.
Time
),
all
:
make
(
map
[
common
.
Hash
]
*
types
.
Transaction
),
eventMux
:
eventMux
,
eventMux
:
eventMux
,
currentState
:
currentStateFn
,
currentState
:
currentStateFn
,
gasLimit
:
gasLimitFn
,
gasLimit
:
gasLimitFn
,
minGasPrice
:
new
(
big
.
Int
),
gasPrice
:
big
.
NewInt
(
1
),
pendingState
:
nil
,
pendingState
:
nil
,
local
Tx
:
newTxSet
(),
local
s
:
newTxSet
(),
events
:
eventMux
.
Subscribe
(
ChainHeadEvent
{},
GasPriceChanged
{},
RemovedTransactionEvent
{}),
events
:
eventMux
.
Subscribe
(
ChainHeadEvent
{},
RemovedTransactionEvent
{}),
quit
:
make
(
chan
struct
{}),
quit
:
make
(
chan
struct
{}),
}
}
pool
.
priced
=
newTxPricedList
(
&
pool
.
all
)
pool
.
resetState
()
pool
.
resetState
()
pool
.
wg
.
Add
(
2
)
pool
.
wg
.
Add
(
2
)
...
@@ -134,27 +139,48 @@ func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentState
...
@@ -134,27 +139,48 @@ func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentState
func
(
pool
*
TxPool
)
eventLoop
()
{
func
(
pool
*
TxPool
)
eventLoop
()
{
defer
pool
.
wg
.
Done
()
defer
pool
.
wg
.
Done
()
// Start a ticker and keep track of interesting pool stats to report
var
prevPending
,
prevQueued
,
prevStales
int
report
:=
time
.
NewTicker
(
statsReportInterval
)
defer
report
.
Stop
()
// Track chain events. When a chain events occurs (new chain canon block)
// Track chain events. When a chain events occurs (new chain canon block)
// we need to know the new state. The new state will help us determine
// we need to know the new state. The new state will help us determine
// the nonces in the managed state
// the nonces in the managed state
for
ev
:=
range
pool
.
events
.
Chan
()
{
for
{
switch
ev
:=
ev
.
Data
.
(
type
)
{
select
{
case
ChainHeadEvent
:
// Handle any events fired by the system
pool
.
mu
.
Lock
()
case
ev
,
ok
:=
<-
pool
.
events
.
Chan
()
:
if
ev
.
Block
!=
nil
{
if
!
ok
{
if
pool
.
config
.
IsHomestead
(
ev
.
Block
.
Number
())
{
return
pool
.
homestead
=
true
}
switch
ev
:=
ev
.
Data
.
(
type
)
{
case
ChainHeadEvent
:
pool
.
mu
.
Lock
()
if
ev
.
Block
!=
nil
{
if
pool
.
config
.
IsHomestead
(
ev
.
Block
.
Number
())
{
pool
.
homestead
=
true
}
}
}
pool
.
resetState
()
pool
.
mu
.
Unlock
()
case
RemovedTransactionEvent
:
pool
.
AddBatch
(
ev
.
Txs
)
}
}
pool
.
resetState
()
// Handle stats reporting ticks
pool
.
mu
.
Unlock
()
case
<-
report
.
C
:
case
GasPriceChanged
:
pool
.
mu
.
RLock
()
pool
.
mu
.
Lock
()
pending
,
queued
:=
pool
.
stats
()
pool
.
minGasPrice
=
ev
.
Price
stales
:=
pool
.
priced
.
stales
pool
.
mu
.
Unlock
()
pool
.
mu
.
RUnlock
()
case
RemovedTransactionEvent
:
pool
.
AddBatch
(
ev
.
Txs
)
if
pending
!=
prevPending
||
queued
!=
prevQueued
||
stales
!=
prevStales
{
log
.
Debug
(
"Transaction pool status report"
,
"executable"
,
pending
,
"queued"
,
queued
,
"stales"
,
stales
)
prevPending
,
prevQueued
,
prevStales
=
pending
,
queued
,
stales
}
}
}
}
}
}
}
...
@@ -191,6 +217,27 @@ func (pool *TxPool) Stop() {
...
@@ -191,6 +217,27 @@ func (pool *TxPool) Stop() {
log
.
Info
(
"Transaction pool stopped"
)
log
.
Info
(
"Transaction pool stopped"
)
}
}
// GasPrice returns the current gas price enforced by the transaction pool.
func
(
pool
*
TxPool
)
GasPrice
()
*
big
.
Int
{
pool
.
mu
.
RLock
()
defer
pool
.
mu
.
RUnlock
()
return
new
(
big
.
Int
)
.
Set
(
pool
.
gasPrice
)
}
// SetGasPrice updates the minimum price required by the transaction pool for a
// new transaction, and drops all transactions below this threshold.
func
(
pool
*
TxPool
)
SetGasPrice
(
price
*
big
.
Int
)
{
pool
.
mu
.
Lock
()
defer
pool
.
mu
.
Unlock
()
pool
.
gasPrice
=
price
for
_
,
tx
:=
range
pool
.
priced
.
Cap
(
price
,
pool
.
locals
)
{
pool
.
removeTx
(
tx
.
Hash
())
}
log
.
Info
(
"Transaction pool price threshold updated"
,
"price"
,
price
)
}
func
(
pool
*
TxPool
)
State
()
*
state
.
ManagedState
{
func
(
pool
*
TxPool
)
State
()
*
state
.
ManagedState
{
pool
.
mu
.
RLock
()
pool
.
mu
.
RLock
()
defer
pool
.
mu
.
RUnlock
()
defer
pool
.
mu
.
RUnlock
()
...
@@ -200,17 +247,25 @@ func (pool *TxPool) State() *state.ManagedState {
...
@@ -200,17 +247,25 @@ func (pool *TxPool) State() *state.ManagedState {
// Stats retrieves the current pool stats, namely the number of pending and the
// Stats retrieves the current pool stats, namely the number of pending and the
// number of queued (non-executable) transactions.
// number of queued (non-executable) transactions.
func
(
pool
*
TxPool
)
Stats
()
(
pending
int
,
queued
int
)
{
func
(
pool
*
TxPool
)
Stats
()
(
int
,
int
)
{
pool
.
mu
.
RLock
()
pool
.
mu
.
RLock
()
defer
pool
.
mu
.
RUnlock
()
defer
pool
.
mu
.
RUnlock
()
return
pool
.
stats
()
}
// stats retrieves the current pool stats, namely the number of pending and the
// number of queued (non-executable) transactions.
func
(
pool
*
TxPool
)
stats
()
(
int
,
int
)
{
pending
:=
0
for
_
,
list
:=
range
pool
.
pending
{
for
_
,
list
:=
range
pool
.
pending
{
pending
+=
list
.
Len
()
pending
+=
list
.
Len
()
}
}
queued
:=
0
for
_
,
list
:=
range
pool
.
queue
{
for
_
,
list
:=
range
pool
.
queue
{
queued
+=
list
.
Len
()
queued
+=
list
.
Len
()
}
}
return
return
pending
,
queued
}
}
// Content retrieves the data content of the transaction pool, returning all the
// Content retrieves the data content of the transaction pool, returning all the
...
@@ -260,16 +315,16 @@ func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
...
@@ -260,16 +315,16 @@ func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
func
(
pool
*
TxPool
)
SetLocal
(
tx
*
types
.
Transaction
)
{
func
(
pool
*
TxPool
)
SetLocal
(
tx
*
types
.
Transaction
)
{
pool
.
mu
.
Lock
()
pool
.
mu
.
Lock
()
defer
pool
.
mu
.
Unlock
()
defer
pool
.
mu
.
Unlock
()
pool
.
local
Tx
.
add
(
tx
.
Hash
())
pool
.
local
s
.
add
(
tx
.
Hash
())
}
}
// validateTx checks whether a transaction is valid according
// validateTx checks whether a transaction is valid according
// to the consensus rules.
// to the consensus rules.
func
(
pool
*
TxPool
)
validateTx
(
tx
*
types
.
Transaction
)
error
{
func
(
pool
*
TxPool
)
validateTx
(
tx
*
types
.
Transaction
)
error
{
local
:=
pool
.
local
Tx
.
contains
(
tx
.
Hash
())
local
:=
pool
.
local
s
.
contains
(
tx
.
Hash
())
// Drop transactions under our own minimal accepted gas price
// Drop transactions under our own minimal accepted gas price
if
!
local
&&
pool
.
minG
asPrice
.
Cmp
(
tx
.
GasPrice
())
>
0
{
if
!
local
&&
pool
.
g
asPrice
.
Cmp
(
tx
.
GasPrice
())
>
0
{
return
Err
Cheap
return
Err
Underpriced
}
}
currentState
,
err
:=
pool
.
currentState
()
currentState
,
err
:=
pool
.
currentState
()
...
@@ -314,31 +369,72 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
...
@@ -314,31 +369,72 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
}
}
// add validates a transaction and inserts it into the non-executable queue for
// add validates a transaction and inserts it into the non-executable queue for
// later pending promotion and execution.
// later pending promotion and execution. If the transaction is a replacement for
func
(
pool
*
TxPool
)
add
(
tx
*
types
.
Transaction
)
error
{
// an already pending or queued one, it overwrites the previous and returns this
// so outer code doesn't uselessly call promote.
func
(
pool
*
TxPool
)
add
(
tx
*
types
.
Transaction
)
(
bool
,
error
)
{
// If the transaction is already known, discard it
// If the transaction is already known, discard it
hash
:=
tx
.
Hash
()
hash
:=
tx
.
Hash
()
if
pool
.
all
[
hash
]
!=
nil
{
if
pool
.
all
[
hash
]
!=
nil
{
log
.
Trace
(
"Discarding already known transaction"
,
"hash"
,
hash
)
log
.
Trace
(
"Discarding already known transaction"
,
"hash"
,
hash
)
return
fmt
.
Errorf
(
"known transaction: %x"
,
hash
)
return
f
alse
,
f
mt
.
Errorf
(
"known transaction: %x"
,
hash
)
}
}
//
Otherwise ensure basic validation passes and queue it up
//
If the transaction fails basic validation, discard it
if
err
:=
pool
.
validateTx
(
tx
);
err
!=
nil
{
if
err
:=
pool
.
validateTx
(
tx
);
err
!=
nil
{
log
.
Trace
(
"Discarding invalid transaction"
,
"hash"
,
hash
,
"err"
,
err
)
log
.
Trace
(
"Discarding invalid transaction"
,
"hash"
,
hash
,
"err"
,
err
)
invalidTxCounter
.
Inc
(
1
)
invalidTxCounter
.
Inc
(
1
)
return
err
return
false
,
err
}
// If the transaction pool is full, discard underpriced transactions
if
uint64
(
len
(
pool
.
all
))
>=
maxPendingTotal
+
maxQueuedTotal
{
// If the new transaction is underpriced, don't accept it
if
pool
.
priced
.
Underpriced
(
tx
,
pool
.
locals
)
{
log
.
Trace
(
"Discarding underpriced transaction"
,
"hash"
,
hash
,
"price"
,
tx
.
GasPrice
())
underpricedTxCounter
.
Inc
(
1
)
return
false
,
ErrUnderpriced
}
// New transaction is better than our worse ones, make room for it
drop
:=
pool
.
priced
.
Discard
(
len
(
pool
.
all
)
-
int
(
maxPendingTotal
+
maxQueuedTotal
-
1
),
pool
.
locals
)
for
_
,
tx
:=
range
drop
{
log
.
Trace
(
"Discarding freshly underpriced transaction"
,
"hash"
,
tx
.
Hash
(),
"price"
,
tx
.
GasPrice
())
underpricedTxCounter
.
Inc
(
1
)
pool
.
removeTx
(
tx
.
Hash
())
}
}
}
pool
.
enqueueTx
(
hash
,
tx
)
// If the transaction is replacing an already pending one, do directly
from
,
_
:=
types
.
Sender
(
pool
.
signer
,
tx
)
// already validated
if
list
:=
pool
.
pending
[
from
];
list
!=
nil
&&
list
.
Overlaps
(
tx
)
{
// Nonce already pending, check if required price bump is met
inserted
,
old
:=
list
.
Add
(
tx
)
if
!
inserted
{
pendingDiscardCounter
.
Inc
(
1
)
return
false
,
ErrReplaceUnderpriced
}
// New transaction is better, replace old one
if
old
!=
nil
{
delete
(
pool
.
all
,
old
.
Hash
())
pool
.
priced
.
Removed
()
pendingReplaceCounter
.
Inc
(
1
)
}
pool
.
all
[
tx
.
Hash
()]
=
tx
pool
.
priced
.
Put
(
tx
)
// Print a log message if low enough level is set
log
.
Trace
(
"Pooled new executable transaction"
,
"hash"
,
hash
,
"from"
,
from
,
"to"
,
tx
.
To
())
log
.
Debug
(
"Pooled new transaction"
,
"hash"
,
hash
,
"from"
,
log
.
Lazy
{
Fn
:
func
()
common
.
Address
{
from
,
_
:=
types
.
Sender
(
pool
.
signer
,
tx
);
return
from
}},
"to"
,
tx
.
To
())
return
old
!=
nil
,
nil
return
nil
}
// New transaction isn't replacing a pending one, push into queue
replace
,
err
:=
pool
.
enqueueTx
(
hash
,
tx
)
if
err
!=
nil
{
return
false
,
err
}
log
.
Trace
(
"Pooled new future transaction"
,
"hash"
,
hash
,
"from"
,
from
,
"to"
,
tx
.
To
())
return
replace
,
nil
}
}
// enqueueTx inserts a new transaction into the non-executable transaction queue.
// enqueueTx inserts a new transaction 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
)
(
bool
,
error
)
{
// Try to insert the transaction into the future queue
// Try to insert the transaction into the future queue
from
,
_
:=
types
.
Sender
(
pool
.
signer
,
tx
)
// already validated
from
,
_
:=
types
.
Sender
(
pool
.
signer
,
tx
)
// already validated
if
pool
.
queue
[
from
]
==
nil
{
if
pool
.
queue
[
from
]
==
nil
{
...
@@ -346,15 +442,19 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) {
...
@@ -346,15 +442,19 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) {
}
}
inserted
,
old
:=
pool
.
queue
[
from
]
.
Add
(
tx
)
inserted
,
old
:=
pool
.
queue
[
from
]
.
Add
(
tx
)
if
!
inserted
{
if
!
inserted
{
// An older transaction was better, discard this
queuedDiscardCounter
.
Inc
(
1
)
queuedDiscardCounter
.
Inc
(
1
)
return
// An older transaction was better, discard this
return
false
,
ErrReplaceUnderpriced
}
}
// Discard any previous transaction and mark this
// Discard any previous transaction and mark this
if
old
!=
nil
{
if
old
!=
nil
{
delete
(
pool
.
all
,
old
.
Hash
())
delete
(
pool
.
all
,
old
.
Hash
())
pool
.
priced
.
Removed
()
queuedReplaceCounter
.
Inc
(
1
)
queuedReplaceCounter
.
Inc
(
1
)
}
}
pool
.
all
[
hash
]
=
tx
pool
.
all
[
hash
]
=
tx
pool
.
priced
.
Put
(
tx
)
return
old
!=
nil
,
nil
}
}
// promoteTx adds a transaction to the pending (processable) list of transactions.
// promoteTx adds a transaction to the pending (processable) list of transactions.
...
@@ -371,16 +471,23 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
...
@@ -371,16 +471,23 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
if
!
inserted
{
if
!
inserted
{
// An older transaction was better, discard this
// An older transaction was better, discard this
delete
(
pool
.
all
,
hash
)
delete
(
pool
.
all
,
hash
)
pool
.
priced
.
Removed
()
pendingDiscardCounter
.
Inc
(
1
)
pendingDiscardCounter
.
Inc
(
1
)
return
return
}
}
// Otherwise discard any previous transaction and mark this
// Otherwise discard any previous transaction and mark this
if
old
!=
nil
{
if
old
!=
nil
{
delete
(
pool
.
all
,
old
.
Hash
())
delete
(
pool
.
all
,
old
.
Hash
())
pool
.
priced
.
Removed
()
pendingReplaceCounter
.
Inc
(
1
)
pendingReplaceCounter
.
Inc
(
1
)
}
}
pool
.
all
[
hash
]
=
tx
// Failsafe to work around direct pending inserts (tests)
// Failsafe to work around direct pending inserts (tests)
if
pool
.
all
[
hash
]
==
nil
{
pool
.
all
[
hash
]
=
tx
pool
.
priced
.
Put
(
tx
)
}
// 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
.
beats
[
addr
]
=
time
.
Now
()
pool
.
pendingState
.
SetNonce
(
addr
,
tx
.
Nonce
()
+
1
)
pool
.
pendingState
.
SetNonce
(
addr
,
tx
.
Nonce
()
+
1
)
...
@@ -392,16 +499,19 @@ func (pool *TxPool) Add(tx *types.Transaction) error {
...
@@ -392,16 +499,19 @@ func (pool *TxPool) Add(tx *types.Transaction) error {
pool
.
mu
.
Lock
()
pool
.
mu
.
Lock
()
defer
pool
.
mu
.
Unlock
()
defer
pool
.
mu
.
Unlock
()
if
err
:=
pool
.
add
(
tx
);
err
!=
nil
{
// Try to inject the transaction and update any state
replace
,
err
:=
pool
.
add
(
tx
)
if
err
!=
nil
{
return
err
return
err
}
}
state
,
err
:=
pool
.
currentState
()
state
,
err
:=
pool
.
currentState
()
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
pool
.
promoteExecutables
(
state
)
// If we added a new transaction, run promotion checks and return
if
!
replace
{
pool
.
promoteExecutables
(
state
)
}
return
nil
return
nil
}
}
...
@@ -411,10 +521,13 @@ func (pool *TxPool) AddBatch(txs []*types.Transaction) error {
...
@@ -411,10 +521,13 @@ func (pool *TxPool) AddBatch(txs []*types.Transaction) error {
defer
pool
.
mu
.
Unlock
()
defer
pool
.
mu
.
Unlock
()
// Add the batch of transaction, tracking the accepted ones
// Add the batch of transaction, tracking the accepted ones
added
:=
0
replaced
,
added
:=
true
,
0
for
_
,
tx
:=
range
txs
{
for
_
,
tx
:=
range
txs
{
if
err
:=
pool
.
add
(
tx
);
err
==
nil
{
if
replace
,
err
:=
pool
.
add
(
tx
);
err
==
nil
{
added
++
added
++
if
!
replace
{
replaced
=
false
}
}
}
}
}
// Only reprocess the internal state if something was actually added
// Only reprocess the internal state if something was actually added
...
@@ -423,7 +536,9 @@ func (pool *TxPool) AddBatch(txs []*types.Transaction) error {
...
@@ -423,7 +536,9 @@ func (pool *TxPool) AddBatch(txs []*types.Transaction) error {
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
pool
.
promoteExecutables
(
state
)
if
!
replaced
{
pool
.
promoteExecutables
(
state
)
}
}
}
return
nil
return
nil
}
}
...
@@ -467,6 +582,7 @@ func (pool *TxPool) removeTx(hash common.Hash) {
...
@@ -467,6 +582,7 @@ func (pool *TxPool) removeTx(hash common.Hash) {
// Remove it from the list of known transactions
// Remove it from the list of known transactions
delete
(
pool
.
all
,
hash
)
delete
(
pool
.
all
,
hash
)
pool
.
priced
.
Removed
()
// Remove the transaction from the pending lists and reset the account nonce
// Remove the transaction from the pending lists and reset the account nonce
if
pending
:=
pool
.
pending
[
addr
];
pending
!=
nil
{
if
pending
:=
pool
.
pending
[
addr
];
pending
!=
nil
{
...
@@ -506,28 +622,31 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
...
@@ -506,28 +622,31 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
// 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
))
{
hash
:=
tx
.
Hash
()
hash
:=
tx
.
Hash
()
log
.
Debug
(
"Removed old queued transaction"
,
"hash"
,
hash
)
log
.
Trace
(
"Removed old queued transaction"
,
"hash"
,
hash
)
delete
(
pool
.
all
,
hash
)
delete
(
pool
.
all
,
hash
)
pool
.
priced
.
Removed
()
}
}
// Drop all transactions that are too costly (low balance)
// Drop all transactions that are too costly (low balance)
drops
,
_
:=
list
.
Filter
(
state
.
GetBalance
(
addr
))
drops
,
_
:=
list
.
Filter
(
state
.
GetBalance
(
addr
))
for
_
,
tx
:=
range
drops
{
for
_
,
tx
:=
range
drops
{
hash
:=
tx
.
Hash
()
hash
:=
tx
.
Hash
()
log
.
Debug
(
"Removed unpayable queued transaction"
,
"hash"
,
hash
)
log
.
Trace
(
"Removed unpayable queued transaction"
,
"hash"
,
hash
)
delete
(
pool
.
all
,
hash
)
delete
(
pool
.
all
,
hash
)
pool
.
priced
.
Removed
()
queuedNofundsCounter
.
Inc
(
1
)
queuedNofundsCounter
.
Inc
(
1
)
}
}
// Gather all executable transactions and promote them
// Gather all executable transactions and promote them
for
_
,
tx
:=
range
list
.
Ready
(
pool
.
pendingState
.
GetNonce
(
addr
))
{
for
_
,
tx
:=
range
list
.
Ready
(
pool
.
pendingState
.
GetNonce
(
addr
))
{
hash
:=
tx
.
Hash
()
hash
:=
tx
.
Hash
()
log
.
Debug
(
"Promoting queued transaction"
,
"hash"
,
hash
)
log
.
Trace
(
"Promoting queued transaction"
,
"hash"
,
hash
)
pool
.
promoteTx
(
addr
,
hash
,
tx
)
pool
.
promoteTx
(
addr
,
hash
,
tx
)
}
}
// Drop all transactions over the allowed limit
// Drop all transactions over the allowed limit
for
_
,
tx
:=
range
list
.
Cap
(
int
(
maxQueuedPerAccount
))
{
for
_
,
tx
:=
range
list
.
Cap
(
int
(
maxQueuedPerAccount
))
{
hash
:=
tx
.
Hash
()
hash
:=
tx
.
Hash
()
log
.
Debug
(
"Removed cap-exceeding queued transaction"
,
"hash"
,
hash
)
log
.
Trace
(
"Removed cap-exceeding queued transaction"
,
"hash"
,
hash
)
delete
(
pool
.
all
,
hash
)
delete
(
pool
.
all
,
hash
)
pool
.
priced
.
Removed
()
queuedRLCounter
.
Inc
(
1
)
queuedRLCounter
.
Inc
(
1
)
}
}
queued
+=
uint64
(
list
.
Len
())
queued
+=
uint64
(
list
.
Len
())
...
@@ -551,7 +670,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
...
@@ -551,7 +670,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
if
uint64
(
list
.
Len
())
>
minPendingPerAccount
{
if
uint64
(
list
.
Len
())
>
minPendingPerAccount
{
// Skip local accounts as pools should maintain backlogs for themselves
// Skip local accounts as pools should maintain backlogs for themselves
for
_
,
tx
:=
range
list
.
txs
.
items
{
for
_
,
tx
:=
range
list
.
txs
.
items
{
if
!
pool
.
local
Tx
.
contains
(
tx
.
Hash
())
{
if
!
pool
.
local
s
.
contains
(
tx
.
Hash
())
{
spammers
.
Push
(
addr
,
float32
(
list
.
Len
()))
spammers
.
Push
(
addr
,
float32
(
list
.
Len
()))
}
}
break
// Checking on transaction for locality is enough
break
// Checking on transaction for locality is enough
...
@@ -593,7 +712,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
...
@@ -593,7 +712,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
pendingRLCounter
.
Inc
(
int64
(
pendingBeforeCap
-
pending
))
pendingRLCounter
.
Inc
(
int64
(
pendingBeforeCap
-
pending
))
}
}
// If we've queued more transactions than the hard limit, drop oldest ones
// If we've queued more transactions than the hard limit, drop oldest ones
if
queued
>
maxQueued
In
Total
{
if
queued
>
maxQueuedTotal
{
// Sort all accounts with queued transactions by heartbeat
// Sort all accounts with queued transactions by heartbeat
addresses
:=
make
(
addresssByHeartbeat
,
0
,
len
(
pool
.
queue
))
addresses
:=
make
(
addresssByHeartbeat
,
0
,
len
(
pool
.
queue
))
for
addr
:=
range
pool
.
queue
{
for
addr
:=
range
pool
.
queue
{
...
@@ -602,7 +721,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
...
@@ -602,7 +721,7 @@ func (pool *TxPool) promoteExecutables(state *state.StateDB) {
sort
.
Sort
(
addresses
)
sort
.
Sort
(
addresses
)
// Drop transactions until the total is below the limit
// Drop transactions until the total is below the limit
for
drop
:=
queued
-
maxQueued
In
Total
;
drop
>
0
;
{
for
drop
:=
queued
-
maxQueuedTotal
;
drop
>
0
;
{
addr
:=
addresses
[
len
(
addresses
)
-
1
]
addr
:=
addresses
[
len
(
addresses
)
-
1
]
list
:=
pool
.
queue
[
addr
.
address
]
list
:=
pool
.
queue
[
addr
.
address
]
...
@@ -639,20 +758,22 @@ func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
...
@@ -639,20 +758,22 @@ func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
// 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
(
nonce
)
{
for
_
,
tx
:=
range
list
.
Forward
(
nonce
)
{
hash
:=
tx
.
Hash
()
hash
:=
tx
.
Hash
()
log
.
Debug
(
"Removed old pending transaction"
,
"hash"
,
hash
)
log
.
Trace
(
"Removed old pending transaction"
,
"hash"
,
hash
)
delete
(
pool
.
all
,
hash
)
delete
(
pool
.
all
,
hash
)
pool
.
priced
.
Removed
()
}
}
// Drop all transactions that are too costly (low balance), and queue any invalids back for later
// Drop all transactions that are too costly (low balance), and queue any invalids back for later
drops
,
invalids
:=
list
.
Filter
(
state
.
GetBalance
(
addr
))
drops
,
invalids
:=
list
.
Filter
(
state
.
GetBalance
(
addr
))
for
_
,
tx
:=
range
drops
{
for
_
,
tx
:=
range
drops
{
hash
:=
tx
.
Hash
()
hash
:=
tx
.
Hash
()
log
.
Debug
(
"Removed unpayable pending transaction"
,
"hash"
,
hash
)
log
.
Trace
(
"Removed unpayable pending transaction"
,
"hash"
,
hash
)
delete
(
pool
.
all
,
hash
)
delete
(
pool
.
all
,
hash
)
pool
.
priced
.
Removed
()
pendingNofundsCounter
.
Inc
(
1
)
pendingNofundsCounter
.
Inc
(
1
)
}
}
for
_
,
tx
:=
range
invalids
{
for
_
,
tx
:=
range
invalids
{
hash
:=
tx
.
Hash
()
hash
:=
tx
.
Hash
()
log
.
Debug
(
"Demoting pending transaction"
,
"hash"
,
hash
)
log
.
Trace
(
"Demoting pending transaction"
,
"hash"
,
hash
)
pool
.
enqueueTx
(
hash
,
tx
)
pool
.
enqueueTx
(
hash
,
tx
)
}
}
// Delete the entire queue entry if it became empty.
// Delete the entire queue entry if it became empty.
...
...
core/tx_pool_test.go
View file @
a2f23ca9
...
@@ -33,7 +33,11 @@ import (
...
@@ -33,7 +33,11 @@ import (
)
)
func
transaction
(
nonce
uint64
,
gaslimit
*
big
.
Int
,
key
*
ecdsa
.
PrivateKey
)
*
types
.
Transaction
{
func
transaction
(
nonce
uint64
,
gaslimit
*
big
.
Int
,
key
*
ecdsa
.
PrivateKey
)
*
types
.
Transaction
{
tx
,
_
:=
types
.
SignTx
(
types
.
NewTransaction
(
nonce
,
common
.
Address
{},
big
.
NewInt
(
100
),
gaslimit
,
big
.
NewInt
(
1
),
nil
),
types
.
HomesteadSigner
{},
key
)
return
pricedTransaction
(
nonce
,
gaslimit
,
big
.
NewInt
(
1
),
key
)
}
func
pricedTransaction
(
nonce
uint64
,
gaslimit
,
gasprice
*
big
.
Int
,
key
*
ecdsa
.
PrivateKey
)
*
types
.
Transaction
{
tx
,
_
:=
types
.
SignTx
(
types
.
NewTransaction
(
nonce
,
common
.
Address
{},
big
.
NewInt
(
100
),
gaslimit
,
gasprice
,
nil
),
types
.
HomesteadSigner
{},
key
)
return
tx
return
tx
}
}
...
@@ -151,9 +155,9 @@ func TestInvalidTransactions(t *testing.T) {
...
@@ -151,9 +155,9 @@ func TestInvalidTransactions(t *testing.T) {
}
}
tx
=
transaction
(
1
,
big
.
NewInt
(
100000
),
key
)
tx
=
transaction
(
1
,
big
.
NewInt
(
100000
),
key
)
pool
.
minG
asPrice
=
big
.
NewInt
(
1000
)
pool
.
g
asPrice
=
big
.
NewInt
(
1000
)
if
err
:=
pool
.
Add
(
tx
);
err
!=
Err
Cheap
{
if
err
:=
pool
.
Add
(
tx
);
err
!=
Err
Underpriced
{
t
.
Error
(
"expected"
,
Err
Cheap
,
"got"
,
err
)
t
.
Error
(
"expected"
,
Err
Underpriced
,
"got"
,
err
)
}
}
pool
.
SetLocal
(
tx
)
pool
.
SetLocal
(
tx
)
...
@@ -262,14 +266,14 @@ func TestTransactionChainFork(t *testing.T) {
...
@@ -262,14 +266,14 @@ func TestTransactionChainFork(t *testing.T) {
resetState
()
resetState
()
tx
:=
transaction
(
0
,
big
.
NewInt
(
100000
),
key
)
tx
:=
transaction
(
0
,
big
.
NewInt
(
100000
),
key
)
if
err
:=
pool
.
add
(
tx
);
err
!=
nil
{
if
_
,
err
:=
pool
.
add
(
tx
);
err
!=
nil
{
t
.
Error
(
"didn't expect error"
,
err
)
t
.
Error
(
"didn't expect error"
,
err
)
}
}
pool
.
RemoveBatch
([]
*
types
.
Transaction
{
tx
})
pool
.
RemoveBatch
([]
*
types
.
Transaction
{
tx
})
// reset the pool's internal state
// reset the pool's internal state
resetState
()
resetState
()
if
err
:=
pool
.
add
(
tx
);
err
!=
nil
{
if
_
,
err
:=
pool
.
add
(
tx
);
err
!=
nil
{
t
.
Error
(
"didn't expect error"
,
err
)
t
.
Error
(
"didn't expect error"
,
err
)
}
}
}
}
...
@@ -293,11 +297,11 @@ func TestTransactionDoubleNonce(t *testing.T) {
...
@@ -293,11 +297,11 @@ func TestTransactionDoubleNonce(t *testing.T) {
tx3
,
_
:=
types
.
SignTx
(
types
.
NewTransaction
(
0
,
common
.
Address
{},
big
.
NewInt
(
100
),
big
.
NewInt
(
1000000
),
big
.
NewInt
(
1
),
nil
),
signer
,
key
)
tx3
,
_
:=
types
.
SignTx
(
types
.
NewTransaction
(
0
,
common
.
Address
{},
big
.
NewInt
(
100
),
big
.
NewInt
(
1000000
),
big
.
NewInt
(
1
),
nil
),
signer
,
key
)
// Add the first two transaction, ensure higher priced stays only
// Add the first two transaction, ensure higher priced stays only
if
err
:=
pool
.
add
(
tx1
);
err
!=
nil
{
if
replace
,
err
:=
pool
.
add
(
tx1
);
err
!=
nil
||
replace
{
t
.
Error
(
"didn't expect error"
,
err
)
t
.
Error
f
(
"first transaction insert failed (%v) or reported replacement (%v)"
,
err
,
replace
)
}
}
if
err
:=
pool
.
add
(
tx2
);
err
!=
nil
{
if
replace
,
err
:=
pool
.
add
(
tx2
);
err
!=
nil
||
!
replace
{
t
.
Error
(
"didn't expect error"
,
err
)
t
.
Error
f
(
"second transaction insert failed (%v) or not reported replacement (%v)"
,
err
,
replace
)
}
}
state
,
_
:=
pool
.
currentState
()
state
,
_
:=
pool
.
currentState
()
pool
.
promoteExecutables
(
state
)
pool
.
promoteExecutables
(
state
)
...
@@ -308,9 +312,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
...
@@ -308,9 +312,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
t
.
Errorf
(
"transaction mismatch: have %x, want %x"
,
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)
// Add the thid transaction and ensure it's not saved (smaller price)
if
err
:=
pool
.
add
(
tx3
);
err
!=
nil
{
pool
.
add
(
tx3
)
t
.
Error
(
"didn't expect error"
,
err
)
}
pool
.
promoteExecutables
(
state
)
pool
.
promoteExecutables
(
state
)
if
pool
.
pending
[
addr
]
.
Len
()
!=
1
{
if
pool
.
pending
[
addr
]
.
Len
()
!=
1
{
t
.
Error
(
"expected 1 pending transactions, got"
,
pool
.
pending
[
addr
]
.
Len
())
t
.
Error
(
"expected 1 pending transactions, got"
,
pool
.
pending
[
addr
]
.
Len
())
...
@@ -330,7 +332,7 @@ func TestMissingNonce(t *testing.T) {
...
@@ -330,7 +332,7 @@ func TestMissingNonce(t *testing.T) {
currentState
,
_
:=
pool
.
currentState
()
currentState
,
_
:=
pool
.
currentState
()
currentState
.
AddBalance
(
addr
,
big
.
NewInt
(
100000000000000
))
currentState
.
AddBalance
(
addr
,
big
.
NewInt
(
100000000000000
))
tx
:=
transaction
(
1
,
big
.
NewInt
(
100000
),
key
)
tx
:=
transaction
(
1
,
big
.
NewInt
(
100000
),
key
)
if
err
:=
pool
.
add
(
tx
);
err
!=
nil
{
if
_
,
err
:=
pool
.
add
(
tx
);
err
!=
nil
{
t
.
Error
(
"didn't expect error"
,
err
)
t
.
Error
(
"didn't expect error"
,
err
)
}
}
if
len
(
pool
.
pending
)
!=
0
{
if
len
(
pool
.
pending
)
!=
0
{
...
@@ -557,8 +559,8 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
...
@@ -557,8 +559,8 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
// some threshold, the higher transactions are dropped to prevent DOS attacks.
// some threshold, the higher transactions are dropped to prevent DOS attacks.
func
TestTransactionQueueGlobalLimiting
(
t
*
testing
.
T
)
{
func
TestTransactionQueueGlobalLimiting
(
t
*
testing
.
T
)
{
// Reduce the queue limits to shorten test time
// Reduce the queue limits to shorten test time
defer
func
(
old
uint64
)
{
maxQueued
InTotal
=
old
}(
maxQueuedIn
Total
)
defer
func
(
old
uint64
)
{
maxQueued
Total
=
old
}(
maxQueued
Total
)
maxQueued
In
Total
=
maxQueuedPerAccount
*
3
maxQueuedTotal
=
maxQueuedPerAccount
*
3
// Create the pool to test the limit enforcement with
// Create the pool to test the limit enforcement with
db
,
_
:=
ethdb
.
NewMemDatabase
()
db
,
_
:=
ethdb
.
NewMemDatabase
()
...
@@ -578,7 +580,7 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
...
@@ -578,7 +580,7 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
// Generate and queue a batch of transactions
// Generate and queue a batch of transactions
nonces
:=
make
(
map
[
common
.
Address
]
uint64
)
nonces
:=
make
(
map
[
common
.
Address
]
uint64
)
txs
:=
make
(
types
.
Transactions
,
0
,
3
*
maxQueued
In
Total
)
txs
:=
make
(
types
.
Transactions
,
0
,
3
*
maxQueuedTotal
)
for
len
(
txs
)
<
cap
(
txs
)
{
for
len
(
txs
)
<
cap
(
txs
)
{
key
:=
keys
[
rand
.
Intn
(
len
(
keys
))]
key
:=
keys
[
rand
.
Intn
(
len
(
keys
))]
addr
:=
crypto
.
PubkeyToAddress
(
key
.
PublicKey
)
addr
:=
crypto
.
PubkeyToAddress
(
key
.
PublicKey
)
...
@@ -596,8 +598,8 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
...
@@ -596,8 +598,8 @@ func TestTransactionQueueGlobalLimiting(t *testing.T) {
}
}
queued
+=
list
.
Len
()
queued
+=
list
.
Len
()
}
}
if
queued
>
int
(
maxQueued
In
Total
)
{
if
queued
>
int
(
maxQueuedTotal
)
{
t
.
Fatalf
(
"total transactions overflow allowance: %d > %d"
,
queued
,
maxQueued
In
Total
)
t
.
Fatalf
(
"total transactions overflow allowance: %d > %d"
,
queued
,
maxQueuedTotal
)
}
}
}
}
...
@@ -791,6 +793,227 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
...
@@ -791,6 +793,227 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
}
}
}
}
// Tests that setting the transaction pool gas price to a higher value correctly
// discards everything cheaper than that and moves any gapped transactions back
// from the pending pool to the queue.
//
// Note, local transactions are never allowed to be dropped.
func
TestTransactionPoolRepricing
(
t
*
testing
.
T
)
{
// Create the pool to test the pricing enforcement with
db
,
_
:=
ethdb
.
NewMemDatabase
()
statedb
,
_
:=
state
.
New
(
common
.
Hash
{},
db
)
pool
:=
NewTxPool
(
params
.
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
,
3
)
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, both pending and queued
txs
:=
types
.
Transactions
{}
txs
=
append
(
txs
,
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
2
),
keys
[
0
]))
txs
=
append
(
txs
,
pricedTransaction
(
1
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
0
]))
txs
=
append
(
txs
,
pricedTransaction
(
2
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
2
),
keys
[
0
]))
txs
=
append
(
txs
,
pricedTransaction
(
1
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
2
),
keys
[
1
]))
txs
=
append
(
txs
,
pricedTransaction
(
2
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
1
]))
txs
=
append
(
txs
,
pricedTransaction
(
3
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
2
),
keys
[
1
]))
txs
=
append
(
txs
,
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
2
]))
pool
.
SetLocal
(
txs
[
len
(
txs
)
-
1
])
// prevent this one from ever being dropped
// Import the batch and that both pending and queued transactions match up
pool
.
AddBatch
(
txs
)
pending
,
queued
:=
pool
.
stats
()
if
pending
!=
4
{
t
.
Fatalf
(
"pending transactions mismatched: have %d, want %d"
,
pending
,
4
)
}
if
queued
!=
3
{
t
.
Fatalf
(
"queued transactions mismatched: have %d, want %d"
,
queued
,
3
)
}
// Reprice the pool and check that underpriced transactions get dropped
pool
.
SetGasPrice
(
big
.
NewInt
(
2
))
pending
,
queued
=
pool
.
stats
()
if
pending
!=
2
{
t
.
Fatalf
(
"pending transactions mismatched: have %d, want %d"
,
pending
,
2
)
}
if
queued
!=
3
{
t
.
Fatalf
(
"queued transactions mismatched: have %d, want %d"
,
queued
,
3
)
}
// Check that we can't add the old transactions back
if
err
:=
pool
.
Add
(
pricedTransaction
(
1
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
0
]));
err
!=
ErrUnderpriced
{
t
.
Fatalf
(
"adding underpriced pending transaction error mismatch: have %v, want %v"
,
err
,
ErrUnderpriced
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
2
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
1
]));
err
!=
ErrUnderpriced
{
t
.
Fatalf
(
"adding underpriced queued transaction error mismatch: have %v, want %v"
,
err
,
ErrUnderpriced
)
}
// However we can add local underpriced transactions
tx
:=
pricedTransaction
(
1
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
2
])
pool
.
SetLocal
(
tx
)
// prevent this one from ever being dropped
if
err
:=
pool
.
Add
(
tx
);
err
!=
nil
{
t
.
Fatalf
(
"failed to add underpriced local transaction: %v"
,
err
)
}
if
pending
,
_
=
pool
.
stats
();
pending
!=
3
{
t
.
Fatalf
(
"pending transactions mismatched: have %d, want %d"
,
pending
,
3
)
}
}
// Tests that when the pool reaches its global transaction limit, underpriced
// transactions are gradually shifted out for more expensive ones and any gapped
// pending transactions are moved into te queue.
//
// Note, local transactions are never allowed to be dropped.
func
TestTransactionPoolUnderpricing
(
t
*
testing
.
T
)
{
// Reduce the queue limits to shorten test time
defer
func
(
old
uint64
)
{
maxPendingTotal
=
old
}(
maxPendingTotal
)
maxPendingTotal
=
2
defer
func
(
old
uint64
)
{
maxQueuedTotal
=
old
}(
maxQueuedTotal
)
maxQueuedTotal
=
2
// Create the pool to test the pricing enforcement with
db
,
_
:=
ethdb
.
NewMemDatabase
()
statedb
,
_
:=
state
.
New
(
common
.
Hash
{},
db
)
pool
:=
NewTxPool
(
params
.
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
,
3
)
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, both pending and queued
txs
:=
types
.
Transactions
{}
txs
=
append
(
txs
,
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
0
]))
txs
=
append
(
txs
,
pricedTransaction
(
1
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
2
),
keys
[
0
]))
txs
=
append
(
txs
,
pricedTransaction
(
1
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
1
]))
txs
=
append
(
txs
,
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
2
]))
pool
.
SetLocal
(
txs
[
len
(
txs
)
-
1
])
// prevent this one from ever being dropped
// Import the batch and that both pending and queued transactions match up
pool
.
AddBatch
(
txs
)
pending
,
queued
:=
pool
.
stats
()
if
pending
!=
3
{
t
.
Fatalf
(
"pending transactions mismatched: have %d, want %d"
,
pending
,
3
)
}
if
queued
!=
1
{
t
.
Fatalf
(
"queued transactions mismatched: have %d, want %d"
,
queued
,
1
)
}
// Ensure that adding an underpriced transaction on block limit fails
if
err
:=
pool
.
Add
(
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
keys
[
1
]));
err
!=
ErrUnderpriced
{
t
.
Fatalf
(
"adding underpriced pending transaction error mismatch: have %v, want %v"
,
err
,
ErrUnderpriced
)
}
// Ensure that adding high priced transactions drops cheap ones, but not own
if
err
:=
pool
.
Add
(
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
3
),
keys
[
1
]));
err
!=
nil
{
t
.
Fatalf
(
"failed to add well priced transaction: %v"
,
err
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
2
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
4
),
keys
[
1
]));
err
!=
nil
{
t
.
Fatalf
(
"failed to add well priced transaction: %v"
,
err
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
3
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
5
),
keys
[
1
]));
err
!=
nil
{
t
.
Fatalf
(
"failed to add well priced transaction: %v"
,
err
)
}
pending
,
queued
=
pool
.
stats
()
if
pending
!=
2
{
t
.
Fatalf
(
"pending transactions mismatched: have %d, want %d"
,
pending
,
2
)
}
if
queued
!=
2
{
t
.
Fatalf
(
"queued transactions mismatched: have %d, want %d"
,
queued
,
2
)
}
// Ensure that adding local transactions can push out even higher priced ones
tx
:=
pricedTransaction
(
1
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
0
),
keys
[
2
])
pool
.
SetLocal
(
tx
)
// prevent this one from ever being dropped
if
err
:=
pool
.
Add
(
tx
);
err
!=
nil
{
t
.
Fatalf
(
"failed to add underpriced local transaction: %v"
,
err
)
}
pending
,
queued
=
pool
.
stats
()
if
pending
!=
2
{
t
.
Fatalf
(
"pending transactions mismatched: have %d, want %d"
,
pending
,
2
)
}
if
queued
!=
2
{
t
.
Fatalf
(
"queued transactions mismatched: have %d, want %d"
,
queued
,
2
)
}
}
// Tests that the pool rejects replacement transactions that don't meet the minimum
// price bump required.
func
TestTransactionReplacement
(
t
*
testing
.
T
)
{
// Create the pool to test the pricing enforcement with
db
,
_
:=
ethdb
.
NewMemDatabase
()
statedb
,
_
:=
state
.
New
(
common
.
Hash
{},
db
)
pool
:=
NewTxPool
(
params
.
TestChainConfig
,
new
(
event
.
TypeMux
),
func
()
(
*
state
.
StateDB
,
error
)
{
return
statedb
,
nil
},
func
()
*
big
.
Int
{
return
big
.
NewInt
(
1000000
)
})
pool
.
resetState
()
// Create a a test account to add transactions with
key
,
_
:=
crypto
.
GenerateKey
()
state
,
_
:=
pool
.
currentState
()
state
.
AddBalance
(
crypto
.
PubkeyToAddress
(
key
.
PublicKey
),
big
.
NewInt
(
1000000000
))
// Add pending transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
price
:=
int64
(
100
)
threshold
:=
(
price
*
(
100
+
minPriceBumpPercent
))
/
100
if
err
:=
pool
.
Add
(
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"failed to add original cheap pending transaction: %v"
,
err
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
0
,
big
.
NewInt
(
100001
),
big
.
NewInt
(
1
),
key
));
err
!=
ErrReplaceUnderpriced
{
t
.
Fatalf
(
"original cheap pending transaction replacement error mismatch: have %v, want %v"
,
err
,
ErrReplaceUnderpriced
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
2
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"failed to replace original cheap pending transaction: %v"
,
err
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
price
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"failed to add original proper pending transaction: %v"
,
err
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
threshold
),
key
));
err
!=
ErrReplaceUnderpriced
{
t
.
Fatalf
(
"original proper pending transaction replacement error mismatch: have %v, want %v"
,
err
,
ErrReplaceUnderpriced
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
0
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
threshold
+
1
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"failed to replace original proper pending transaction: %v"
,
err
)
}
// Add queued transactions, ensuring the minimum price bump is enforced for replacement (for ultra low prices too)
if
err
:=
pool
.
Add
(
pricedTransaction
(
2
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
1
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"failed to add original queued transaction: %v"
,
err
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
2
,
big
.
NewInt
(
100001
),
big
.
NewInt
(
1
),
key
));
err
!=
ErrReplaceUnderpriced
{
t
.
Fatalf
(
"original queued transaction replacement error mismatch: have %v, want %v"
,
err
,
ErrReplaceUnderpriced
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
2
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
2
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"failed to replace original queued transaction: %v"
,
err
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
2
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
price
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"failed to add original queued transaction: %v"
,
err
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
2
,
big
.
NewInt
(
100001
),
big
.
NewInt
(
threshold
),
key
));
err
!=
ErrReplaceUnderpriced
{
t
.
Fatalf
(
"original queued transaction replacement error mismatch: have %v, want %v"
,
err
,
ErrReplaceUnderpriced
)
}
if
err
:=
pool
.
Add
(
pricedTransaction
(
2
,
big
.
NewInt
(
100000
),
big
.
NewInt
(
threshold
+
1
),
key
));
err
!=
nil
{
t
.
Fatalf
(
"failed to replace original queued transaction: %v"
,
err
)
}
}
// Benchmarks the speed of validating the contents of the pending queue of the
// Benchmarks the speed of validating the contents of the pending queue of the
// transaction pool.
// transaction pool.
func
BenchmarkPendingDemotion100
(
b
*
testing
.
B
)
{
benchmarkPendingDemotion
(
b
,
100
)
}
func
BenchmarkPendingDemotion100
(
b
*
testing
.
B
)
{
benchmarkPendingDemotion
(
b
,
100
)
}
...
...
eth/api.go
View file @
a2f23ca9
...
@@ -153,6 +153,8 @@ func (api *PrivateMinerAPI) Start(threads *int) error {
...
@@ -153,6 +153,8 @@ func (api *PrivateMinerAPI) Start(threads *int) error {
}
}
// Start the miner and return
// Start the miner and return
if
!
api
.
e
.
IsMining
()
{
if
!
api
.
e
.
IsMining
()
{
// Propagate the initial price point to the transaction pool
api
.
e
.
txPool
.
SetGasPrice
(
api
.
e
.
gasPrice
)
return
api
.
e
.
StartMining
(
true
)
return
api
.
e
.
StartMining
(
true
)
}
}
return
nil
return
nil
...
@@ -180,7 +182,7 @@ func (api *PrivateMinerAPI) SetExtra(extra string) (bool, error) {
...
@@ -180,7 +182,7 @@ func (api *PrivateMinerAPI) SetExtra(extra string) (bool, error) {
// SetGasPrice sets the minimum accepted gas price for the miner.
// SetGasPrice sets the minimum accepted gas price for the miner.
func
(
api
*
PrivateMinerAPI
)
SetGasPrice
(
gasPrice
hexutil
.
Big
)
bool
{
func
(
api
*
PrivateMinerAPI
)
SetGasPrice
(
gasPrice
hexutil
.
Big
)
bool
{
api
.
e
.
Miner
()
.
SetGasPrice
((
*
big
.
Int
)(
&
gasPrice
))
api
.
e
.
txPool
.
SetGasPrice
((
*
big
.
Int
)(
&
gasPrice
))
return
true
return
true
}
}
...
...
eth/backend.go
View file @
a2f23ca9
...
@@ -20,6 +20,7 @@ package eth
...
@@ -20,6 +20,7 @@ package eth
import
(
import
(
"errors"
"errors"
"fmt"
"fmt"
"math/big"
"runtime"
"runtime"
"sync"
"sync"
"sync/atomic"
"sync/atomic"
...
@@ -76,6 +77,7 @@ type Ethereum struct {
...
@@ -76,6 +77,7 @@ type Ethereum struct {
ApiBackend
*
EthApiBackend
ApiBackend
*
EthApiBackend
miner
*
miner
.
Miner
miner
*
miner
.
Miner
gasPrice
*
big
.
Int
Mining
bool
Mining
bool
MinerThreads
int
MinerThreads
int
etherbase
common
.
Address
etherbase
common
.
Address
...
@@ -167,7 +169,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
...
@@ -167,7 +169,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
}
}
eth
.
miner
=
miner
.
New
(
eth
,
eth
.
chainConfig
,
eth
.
EventMux
(),
eth
.
engine
)
eth
.
miner
=
miner
.
New
(
eth
,
eth
.
chainConfig
,
eth
.
EventMux
(),
eth
.
engine
)
eth
.
miner
.
SetGasPrice
(
config
.
GasPrice
)
eth
.
gasPrice
=
config
.
GasPrice
eth
.
miner
.
SetExtra
(
makeExtraData
(
config
.
ExtraData
))
eth
.
miner
.
SetExtra
(
makeExtraData
(
config
.
ExtraData
))
eth
.
ApiBackend
=
&
EthApiBackend
{
eth
,
nil
}
eth
.
ApiBackend
=
&
EthApiBackend
{
eth
,
nil
}
...
...
eth/config.go
View file @
a2f23ca9
...
@@ -42,7 +42,7 @@ var DefaultConfig = Config{
...
@@ -42,7 +42,7 @@ var DefaultConfig = Config{
NetworkId
:
1
,
NetworkId
:
1
,
LightPeers
:
20
,
LightPeers
:
20
,
DatabaseCache
:
128
,
DatabaseCache
:
128
,
GasPrice
:
big
.
NewInt
(
20
*
params
.
Shannon
),
GasPrice
:
big
.
NewInt
(
18
*
params
.
Shannon
),
GPO
:
gasprice
.
Config
{
GPO
:
gasprice
.
Config
{
Blocks
:
10
,
Blocks
:
10
,
...
...
ethstats/ethstats.go
View file @
a2f23ca9
...
@@ -18,6 +18,7 @@
...
@@ -18,6 +18,7 @@
package
ethstats
package
ethstats
import
(
import
(
"context"
"encoding/json"
"encoding/json"
"errors"
"errors"
"fmt"
"fmt"
...
@@ -639,7 +640,8 @@ func (s *Service) reportStats(conn *websocket.Conn) error {
...
@@ -639,7 +640,8 @@ func (s *Service) reportStats(conn *websocket.Conn) error {
sync
:=
s
.
eth
.
Downloader
()
.
Progress
()
sync
:=
s
.
eth
.
Downloader
()
.
Progress
()
syncing
=
s
.
eth
.
BlockChain
()
.
CurrentHeader
()
.
Number
.
Uint64
()
>=
sync
.
HighestBlock
syncing
=
s
.
eth
.
BlockChain
()
.
CurrentHeader
()
.
Number
.
Uint64
()
>=
sync
.
HighestBlock
gasprice
=
int
(
s
.
eth
.
Miner
()
.
GasPrice
()
.
Uint64
())
price
,
_
:=
s
.
eth
.
ApiBackend
.
SuggestPrice
(
context
.
Background
())
gasprice
=
int
(
price
.
Uint64
())
}
else
{
}
else
{
sync
:=
s
.
les
.
Downloader
()
.
Progress
()
sync
:=
s
.
les
.
Downloader
()
.
Progress
()
syncing
=
s
.
les
.
BlockChain
()
.
CurrentHeader
()
.
Number
.
Uint64
()
>=
sync
.
HighestBlock
syncing
=
s
.
les
.
BlockChain
()
.
CurrentHeader
()
.
Number
.
Uint64
()
>=
sync
.
HighestBlock
...
...
miner/miner.go
View file @
a2f23ca9
...
@@ -19,7 +19,6 @@ package miner
...
@@ -19,7 +19,6 @@ package miner
import
(
import
(
"fmt"
"fmt"
"math/big"
"sync/atomic"
"sync/atomic"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts"
...
@@ -104,18 +103,6 @@ out:
...
@@ -104,18 +103,6 @@ out:
}
}
}
}
func
(
m
*
Miner
)
GasPrice
()
*
big
.
Int
{
return
new
(
big
.
Int
)
.
Set
(
m
.
worker
.
gasPrice
)
}
func
(
m
*
Miner
)
SetGasPrice
(
price
*
big
.
Int
)
{
// FIXME block tests set a nil gas price. Quick dirty fix
if
price
==
nil
{
return
}
m
.
worker
.
setGasPrice
(
price
)
}
func
(
self
*
Miner
)
Start
(
coinbase
common
.
Address
)
{
func
(
self
*
Miner
)
Start
(
coinbase
common
.
Address
)
{
atomic
.
StoreInt32
(
&
self
.
shouldStart
,
1
)
atomic
.
StoreInt32
(
&
self
.
shouldStart
,
1
)
self
.
worker
.
setEtherbase
(
coinbase
)
self
.
worker
.
setEtherbase
(
coinbase
)
...
...
miner/worker.go
View file @
a2f23ca9
...
@@ -59,14 +59,12 @@ type Work struct {
...
@@ -59,14 +59,12 @@ type Work struct {
config
*
params
.
ChainConfig
config
*
params
.
ChainConfig
signer
types
.
Signer
signer
types
.
Signer
state
*
state
.
StateDB
// apply state changes here
state
*
state
.
StateDB
// apply state changes here
ancestors
*
set
.
Set
// ancestor set (used for checking uncle parent validity)
ancestors
*
set
.
Set
// ancestor set (used for checking uncle parent validity)
family
*
set
.
Set
// family set (used for checking uncle invalidity)
family
*
set
.
Set
// family set (used for checking uncle invalidity)
uncles
*
set
.
Set
// uncle set
uncles
*
set
.
Set
// uncle set
tcount
int
// tx count in cycle
tcount
int
// tx count in cycle
ownedAccounts
*
set
.
Set
failedTxs
types
.
Transactions
lowGasTxs
types
.
Transactions
failedTxs
types
.
Transactions
Block
*
types
.
Block
// the new block
Block
*
types
.
Block
// the new block
...
@@ -103,7 +101,6 @@ type worker struct {
...
@@ -103,7 +101,6 @@ type worker struct {
chainDb
ethdb
.
Database
chainDb
ethdb
.
Database
coinbase
common
.
Address
coinbase
common
.
Address
gasPrice
*
big
.
Int
extra
[]
byte
extra
[]
byte
currentMu
sync
.
Mutex
currentMu
sync
.
Mutex
...
@@ -132,7 +129,6 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com
...
@@ -132,7 +129,6 @@ func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase com
mux
:
mux
,
mux
:
mux
,
chainDb
:
eth
.
ChainDb
(),
chainDb
:
eth
.
ChainDb
(),
recv
:
make
(
chan
*
Result
,
resultQueueSize
),
recv
:
make
(
chan
*
Result
,
resultQueueSize
),
gasPrice
:
new
(
big
.
Int
),
chain
:
eth
.
BlockChain
(),
chain
:
eth
.
BlockChain
(),
proc
:
eth
.
BlockChain
()
.
Validator
(),
proc
:
eth
.
BlockChain
()
.
Validator
(),
possibleUncles
:
make
(
map
[
common
.
Hash
]
*
types
.
Block
),
possibleUncles
:
make
(
map
[
common
.
Hash
]
*
types
.
Block
),
...
@@ -252,7 +248,7 @@ func (self *worker) update() {
...
@@ -252,7 +248,7 @@ func (self *worker) update() {
txs
:=
map
[
common
.
Address
]
types
.
Transactions
{
acc
:
{
ev
.
Tx
}}
txs
:=
map
[
common
.
Address
]
types
.
Transactions
{
acc
:
{
ev
.
Tx
}}
txset
:=
types
.
NewTransactionsByPriceAndNonce
(
txs
)
txset
:=
types
.
NewTransactionsByPriceAndNonce
(
txs
)
self
.
current
.
commitTransactions
(
self
.
mux
,
txset
,
self
.
gasPrice
,
self
.
chain
,
self
.
coinbase
)
self
.
current
.
commitTransactions
(
self
.
mux
,
txset
,
self
.
chain
,
self
.
coinbase
)
self
.
currentMu
.
Unlock
()
self
.
currentMu
.
Unlock
()
}
}
}
}
...
@@ -375,22 +371,10 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
...
@@ -375,22 +371,10 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
}
}
// Keep track of transactions which return errors so they can be removed
// Keep track of transactions which return errors so they can be removed
work
.
tcount
=
0
work
.
tcount
=
0
work
.
ownedAccounts
=
accountAddressesSet
(
accounts
)
self
.
current
=
work
self
.
current
=
work
return
nil
return
nil
}
}
func
(
w
*
worker
)
setGasPrice
(
p
*
big
.
Int
)
{
w
.
mu
.
Lock
()
defer
w
.
mu
.
Unlock
()
// calculate the minimal gas price the miner accepts when sorting out transactions.
const
pct
=
int64
(
90
)
w
.
gasPrice
=
gasprice
(
p
,
pct
)
w
.
mux
.
Post
(
core
.
GasPriceChanged
{
Price
:
w
.
gasPrice
})
}
func
(
self
*
worker
)
commitNewWork
()
{
func
(
self
*
worker
)
commitNewWork
()
{
self
.
mu
.
Lock
()
self
.
mu
.
Lock
()
defer
self
.
mu
.
Unlock
()
defer
self
.
mu
.
Unlock
()
...
@@ -460,9 +444,8 @@ func (self *worker) commitNewWork() {
...
@@ -460,9 +444,8 @@ func (self *worker) commitNewWork() {
return
return
}
}
txs
:=
types
.
NewTransactionsByPriceAndNonce
(
pending
)
txs
:=
types
.
NewTransactionsByPriceAndNonce
(
pending
)
work
.
commitTransactions
(
self
.
mux
,
txs
,
self
.
gasPrice
,
self
.
chain
,
self
.
coinbase
)
work
.
commitTransactions
(
self
.
mux
,
txs
,
self
.
chain
,
self
.
coinbase
)
self
.
eth
.
TxPool
()
.
RemoveBatch
(
work
.
lowGasTxs
)
self
.
eth
.
TxPool
()
.
RemoveBatch
(
work
.
failedTxs
)
self
.
eth
.
TxPool
()
.
RemoveBatch
(
work
.
failedTxs
)
// compute uncles for the new block.
// compute uncles for the new block.
...
@@ -515,7 +498,7 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
...
@@ -515,7 +498,7 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
return
nil
return
nil
}
}
func
(
env
*
Work
)
commitTransactions
(
mux
*
event
.
TypeMux
,
txs
*
types
.
TransactionsByPriceAndNonce
,
gasPrice
*
big
.
Int
,
bc
*
core
.
BlockChain
,
coinbase
common
.
Address
)
{
func
(
env
*
Work
)
commitTransactions
(
mux
*
event
.
TypeMux
,
txs
*
types
.
TransactionsByPriceAndNonce
,
bc
*
core
.
BlockChain
,
coinbase
common
.
Address
)
{
gp
:=
new
(
core
.
GasPool
)
.
AddGas
(
env
.
header
.
GasLimit
)
gp
:=
new
(
core
.
GasPool
)
.
AddGas
(
env
.
header
.
GasLimit
)
var
coalescedLogs
[]
*
types
.
Log
var
coalescedLogs
[]
*
types
.
Log
...
@@ -539,17 +522,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
...
@@ -539,17 +522,6 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
txs
.
Pop
()
txs
.
Pop
()
continue
continue
}
}
// Ignore any transactions (and accounts subsequently) with low gas limits
if
tx
.
GasPrice
()
.
Cmp
(
gasPrice
)
<
0
&&
!
env
.
ownedAccounts
.
Has
(
from
)
{
// Pop the current low-priced transaction without shifting in the next from the account
log
.
Warn
(
"Transaction below gas price"
,
"sender"
,
from
,
"hash"
,
tx
.
Hash
(),
"have"
,
tx
.
GasPrice
(),
"want"
,
gasPrice
)
env
.
lowGasTxs
=
append
(
env
.
lowGasTxs
,
tx
)
txs
.
Pop
()
continue
}
// Start executing the transaction
// Start executing the transaction
env
.
state
.
StartRecord
(
tx
.
Hash
(),
common
.
Hash
{},
env
.
tcount
)
env
.
state
.
StartRecord
(
tx
.
Hash
(),
common
.
Hash
{},
env
.
tcount
)
...
@@ -607,25 +579,3 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, c
...
@@ -607,25 +579,3 @@ func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, c
return
nil
,
receipt
.
Logs
return
nil
,
receipt
.
Logs
}
}
// TODO: remove or use
func
(
self
*
worker
)
HashRate
()
int64
{
return
0
}
// gasprice calculates a reduced gas price based on the pct
// XXX Use big.Rat?
func
gasprice
(
price
*
big
.
Int
,
pct
int64
)
*
big
.
Int
{
p
:=
new
(
big
.
Int
)
.
Set
(
price
)
p
.
Div
(
p
,
big
.
NewInt
(
100
))
p
.
Mul
(
p
,
big
.
NewInt
(
pct
))
return
p
}
func
accountAddressesSet
(
accounts
[]
accounts
.
Account
)
*
set
.
Set
{
accountSet
:=
set
.
New
()
for
_
,
account
:=
range
accounts
{
accountSet
.
Add
(
account
.
Address
)
}
return
accountSet
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment