@ -95,10 +95,20 @@ ImportResult TransactionQueue::import(Transaction const& _transaction, ImportCal
return ret ;
}
std : : unordered_map < h256 , Transaction > TransactionQueue : : transactions ( ) const
Transactions TransactionQueue : : topTransactions ( unsigned _limit ) const
{
ReadGuard l ( m_lock ) ;
return m_current ;
Transactions res ;
unsigned n = _limit ;
for ( auto t = m_current . begin ( ) ; n ! = 0 & & t ! = m_current . end ( ) ; + + t , - - n )
res . push_back ( t - > transaction ) ;
return res ;
}
h256Hash TransactionQueue : : knownTransactions ( ) const
{
ReadGuard l ( m_lock ) ;
return m_known ;
}
ImportResult TransactionQueue : : manageImport_WITH_LOCK ( h256 const & _h , Transaction const & _transaction , ImportCallback const & _cb )
@ -108,35 +118,50 @@ ImportResult TransactionQueue::manageImport_WITH_LOCK(h256 const& _h, Transactio
// Check validity of _transactionRLP as a transaction. To do this we just deserialise and attempt to determine the sender.
// If it doesn't work, the signature is bad.
// The transaction's nonce may yet be invalid (or, it could be "valid" but we may be missing a marginally older transaction).
assert ( _h = = _transaction . sha3 ( ) ) ;
// Remove any prior transaction with the same nonce but a lower gas price.
// Bomb out if there's a prior transaction with higher gas price.
auto r = m_senders . equal_range ( _transaction . from ( ) ) ;
for ( auto it = r . first ; it ! = r . second ; + + it )
if ( m_current . count ( it - > second ) & & m_current [ it - > second ] . nonce ( ) = = _transaction . nonce ( ) )
if ( _transaction . gasPrice ( ) < m_current [ it - > second ] . gasPrice ( ) )
auto cs = m_currentByAddressAndNonce . find ( _transaction . from ( ) ) ;
if ( cs ! = m_currentByAddressAndNonce . end ( ) )
{
auto t = cs - > second . find ( _transaction . nonce ( ) ) ;
if ( t ! = cs - > second . end ( ) )
{
if ( _transaction . gasPrice ( ) < ( * t - > second ) . transaction . gasPrice ( ) )
return ImportResult : : OverbidGasPrice ;
else
{
remove_WITH_LOCK ( it - > second ) ;
break ;
}
else if ( m_future . count ( it - > second ) & & m_future [ it - > second ] . nonce ( ) = = _transaction . nonce ( ) )
if ( _transaction . gasPrice ( ) < m_future [ it - > second ] . gasPrice ( ) )
remove_WITH_LOCK ( ( * t - > second ) . transaction . sha3 ( ) ) ;
}
}
auto fs = m_future . find ( _transaction . from ( ) ) ;
if ( fs ! = m_future . end ( ) )
{
auto t = fs - > second . find ( _transaction . nonce ( ) ) ;
if ( t ! = fs - > second . end ( ) )
{
if ( _transaction . gasPrice ( ) < t - > second . transaction . gasPrice ( ) )
return ImportResult : : OverbidGasPrice ;
else
{
remove_WITH_LOCK ( it - > second ) ;
break ;
fs - > second . erase ( t ) ;
- - m_futureSize ;
}
else { }
}
}
// If valid, append to blocks.
insertCurrent_WITH_LOCK ( make_pair ( _h , _transaction ) ) ;
m_known . insert ( _h ) ;
if ( _cb )
m_callbacks [ _h ] = _cb ;
clog ( TransactionQueueTraceChannel ) < < " Queued vaguely legit-looking transaction " < < _h ;
while ( m_current . size ( ) > m_limit )
{
clog ( TransactionQueueTraceChannel ) < < " Dropping out of bounds transaction " < < _h ;
remove_WITH_LOCK ( m_current . rbegin ( ) - > transaction . sha3 ( ) ) ;
}
m_onReady ( ) ;
}
catch ( Exception const & _e )
@ -163,93 +188,111 @@ u256 TransactionQueue::maxNonce(Address const& _a) const
u256 TransactionQueue : : maxNonce_WITH_LOCK ( Address const & _a ) const
{
u256 ret = 0 ;
auto r = m_senders . equal_range ( _a ) ;
for ( auto it = r . first ; it ! = r . second ; + + it )
if ( m_current . count ( it - > second ) )
{
// cdebug << it->first << "1+" << m_current.at(it->second).nonce();
ret = max ( ret , m_current . at ( it - > second ) . nonce ( ) + 1 ) ;
}
else if ( m_future . count ( it - > second ) )
{
// cdebug << it->first << "1+" << m_future.at(it->second).nonce();
ret = max ( ret , m_future . at ( it - > second ) . nonce ( ) + 1 ) ;
}
else
{
cwarn < < " ERRROR!!!!! m_senders references non-current transaction " ;
cwarn < < " Sender " < < it - > first < < " has transaction " < < it - > second ;
cwarn < < " Count of m_current for " < < it - > second < < " is " < < m_current . count ( it - > second ) ;
}
return ret ;
auto cs = m_currentByAddressAndNonce . find ( _a ) ;
if ( cs ! = m_currentByAddressAndNonce . end ( ) & & ! cs - > second . empty ( ) )
ret = cs - > second . rbegin ( ) - > first ;
auto fs = m_future . find ( _a ) ;
if ( fs ! = m_future . end ( ) & & ! fs - > second . empty ( ) )
ret = std : : max ( ret , fs - > second . rbegin ( ) - > first ) ;
return ret + 1 ;
}
void TransactionQueue : : insertCurrent_WITH_LOCK ( std : : pair < h256 , Transaction > const & _p )
{
// cdebug << "txQ::insertCurrent" << _p.first << _p.second.sender() << _p.second.nonce();
m_senders . insert ( make_pair ( _p . second . sender ( ) , _p . first ) ) ;
if ( m_current . count ( _p . first ) )
if ( m_currentByHash . count ( _p . first ) )
{
cwarn < < " Transaction hash " < < _p . first < < " already in current?! " ;
m_current . insert ( _p ) ;
}
return ;
}
bool TransactionQueue : : remove_WITH_LOCK ( h256 const & _txHash )
{
// cdebug << "txQ::remove" << _txHash;
for ( std : : unordered_map < h256 , Transaction > * pool : { & m_current , & m_future } )
Transaction const & t = _p . second ;
// Insert into current
auto inserted = m_currentByAddressAndNonce [ t . from ( ) ] . insert ( std : : make_pair ( t . nonce ( ) , PriorityQueue : : iterator ( ) ) ) ;
PriorityQueue : : iterator handle = m_current . emplace ( VerifiedTransaction ( t ) ) ;
inserted . first - > second = handle ;
m_currentByHash [ _p . first ] = handle ;
// Move following transactions from future to current
auto fs = m_future . find ( t . from ( ) ) ;
if ( fs ! = m_future . end ( ) )
{
auto pit = pool - > find ( _txHash ) ;
if ( pit ! = pool - > end ( ) )
u256 nonce = t . nonce ( ) + 1 ;
auto fb = fs - > second . find ( nonce ) ;
if ( fb ! = fs - > second . end ( ) )
{
auto r = m_senders . equal_range ( pit - > second . sender ( ) ) ;
for ( auto i = r . first ; i ! = r . second ; + + i )
if ( i - > second = = _txHash )
{
m_senders . erase ( i ) ;
break ;
}
// cdebug << "=> nonce" << pit->second.nonce();
pool - > erase ( pit ) ;
return true ;
auto ft = fb ;
while ( ft ! = fs - > second . end ( ) & & ft - > second . transaction . nonce ( ) = = nonce )
{
inserted = m_currentByAddressAndNonce [ t . from ( ) ] . insert ( std : : make_pair ( ft - > second . transaction . nonce ( ) , PriorityQueue : : iterator ( ) ) ) ;
PriorityQueue : : iterator handle = m_current . emplace ( move ( ft - > second ) ) ;
inserted . first - > second = handle ;
m_currentByHash [ ( * handle ) . transaction . sha3 ( ) ] = handle ;
- - m_futureSize ;
+ + ft ;
+ + nonce ;
}
fs - > second . erase ( fb , ft ) ;
if ( fs - > second . empty ( ) )
m_future . erase ( t . from ( ) ) ;
}
}
return false ;
}
bool TransactionQueue : : remove_WITH_LOCK ( h256 const & _txHash )
{
auto t = m_currentByHash . find ( _txHash ) ;
if ( t = = m_currentByHash . end ( ) )
return false ;
Address from = ( * t - > second ) . transaction . from ( ) ;
auto it = m_currentByAddressAndNonce . find ( from ) ;
assert ( it ! = m_currentByAddressAndNonce . end ( ) ) ;
it - > second . erase ( ( * t - > second ) . transaction . nonce ( ) ) ;
m_current . erase ( t - > second ) ;
m_currentByHash . erase ( t ) ;
if ( it - > second . empty ( ) )
m_currentByAddressAndNonce . erase ( it ) ;
return true ;
}
unsigned TransactionQueue : : waiting ( Address const & _a ) const
{
ReadGuard l ( m_lock ) ;
auto it = m_senders . equal_range ( _a ) ;
unsigned ret = 0 ;
for ( auto i = it . first ; i ! = it . second ; + + i , + + ret ) { }
auto cs = m_currentByAddressAndNonce . find ( _a ) ;
if ( cs ! = m_currentByAddressAndNonce . end ( ) )
ret = cs - > second . size ( ) ;
auto fs = m_future . find ( _a ) ;
if ( fs ! = m_future . end ( ) )
ret + = fs - > second . size ( ) ;
return ret ;
}
void TransactionQueue : : setFuture ( std : : pair < h256 , Transaction > const & _t )
void TransactionQueue : : setFuture ( h256 const & _txHash )
{
// cdebug << "txQ::setFuture" << _t.first;
WriteGuard l ( m_lock ) ;
if ( m_current . count ( _t . first ) )
{
m_future . insert ( _t ) ;
m_current . erase ( _t . first ) ;
}
}
auto it = m_currentByHash . find ( _txHash ) ;
if ( it = = m_currentByHash . end ( ) )
return ;
void TransactionQueue : : noteGood ( std : : pair < h256 , Transaction > const & _t )
{
// cdebug << "txQ::noteGood" << _t.first;
WriteGuard l ( m_lock ) ;
auto r = m_senders . equal_range ( _t . second . sender ( ) ) ;
for ( auto it = r . first ; it ! = r . second ; + + it )
VerifiedTransaction const & st = * ( it - > second ) ;
Address from = st . transaction . from ( ) ;
auto & queue = m_currentByAddressAndNonce [ from ] ;
auto & target = m_future [ from ] ;
auto cutoff = queue . lower_bound ( st . transaction . nonce ( ) ) ;
for ( auto m = cutoff ; m ! = queue . end ( ) ; + + m )
{
auto fit = m_future . find ( it - > second ) ;
if ( fit ! = m_future . end ( ) )
{
m_current . insert ( * fit ) ;
m_future . erase ( fit ) ;
}
VerifiedTransaction & t = const_cast < VerifiedTransaction & > ( * ( m - > second ) ) ; // set has only const iterators. Since we are moving out of container that's fine
m_currentByHash . erase ( t . transaction . sha3 ( ) ) ;
target . emplace ( t . transaction . nonce ( ) , move ( t ) ) ;
m_current . erase ( m - > second ) ;
+ + m_futureSize ;
}
queue . erase ( cutoff , queue . end ( ) ) ;
if ( queue . empty ( ) )
m_currentByAddressAndNonce . erase ( from ) ;
}
void TransactionQueue : : drop ( h256 const & _txHash )
@ -265,3 +308,14 @@ void TransactionQueue::drop(h256 const& _txHash)
remove_WITH_LOCK ( _txHash ) ;
}
void TransactionQueue : : clear ( )
{
WriteGuard l ( m_lock ) ;
m_known . clear ( ) ;
m_current . clear ( ) ;
m_currentByAddressAndNonce . clear ( ) ;
m_currentByHash . clear ( ) ;
m_future . clear ( ) ;
m_futureSize = 0 ;
}