Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Commit

Permalink
Update transaction nonce check in Executive
Browse files Browse the repository at this point in the history
The old check was restrictive - it prohibited the same account from sending multiple transactions before a new block was mined, because the
check required that the new tx nonce match the nonce retrieved from the (state in the) most recent block in the local chain. The new check is more flexible - it requires that
new tx nonces be greater than or equal to the required nonce.

These changes also add a new test which verifies the new behavior.
  • Loading branch information
halfalicious committed Dec 6, 2019
1 parent d873d73 commit 1f29ce6
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 4 deletions.
10 changes: 6 additions & 4 deletions libethereum/Executive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,14 @@ void Executive::initialize(Transaction const& _transaction)
m_excepted = TransactionException::InvalidSignature;
throw;
}
if (m_t.nonce() != nonceReq)
if (m_t.nonce() < nonceReq)
{
LOG(m_execLogger) << "Sender: " << m_t.sender().hex() << " Invalid Nonce: Require "
<< nonceReq << " Got " << m_t.nonce();
LOG(m_execLogger) << "Sender: " << m_t.sender().hex()
<< " Invalid Nonce: Required nonce >= " << nonceReq << ", received "
<< m_t.nonce();
m_excepted = TransactionException::InvalidNonce;
BOOST_THROW_EXCEPTION(InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce()));
BOOST_THROW_EXCEPTION(
InvalidNonce() << RequirementError((bigint)nonceReq, (bigint)m_t.nonce()));
}

// Avoid unaffordable transactions.
Expand Down
60 changes: 60 additions & 0 deletions test/unittests/libweb3jsonrpc/jsonrpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ BOOST_AUTO_TEST_CASE(eth_sendTransaction)
auto address = coinbase.address();
auto countAt = jsToU256(rpcClient->eth_getTransactionCount(toJS(address), "latest"));

// Verify initial account state
BOOST_CHECK_EQUAL(countAt, web3->ethereum()->countAt(address));
BOOST_CHECK_EQUAL(countAt, 0);
auto balance = web3->ethereum()->balanceAt(address, 0);
Expand Down Expand Up @@ -310,6 +311,65 @@ BOOST_AUTO_TEST_CASE(eth_sendTransaction)
BOOST_CHECK_EQUAL(txAmount, balance2);
}

BOOST_AUTO_TEST_CASE(eth_sendMultipleTransactions)
{
// Send multiple transactions from the same account before mining a block, should succeed
auto const senderAddress = coinbase.address();
auto countAt = jsToU256(rpcClient->eth_getTransactionCount(toJS(senderAddress), "latest"));

// Verify initial account state
BOOST_CHECK_EQUAL(countAt, web3->ethereum()->countAt(senderAddress));
BOOST_CHECK_EQUAL(countAt, 0);
auto senderBalance = web3->ethereum()->balanceAt(senderAddress, 0);
string senderBalanceString = rpcClient->eth_getBalance(toJS(senderAddress), "latest");
BOOST_CHECK_EQUAL(toJS(senderBalance), senderBalanceString);
BOOST_CHECK_EQUAL(jsToDecimal(senderBalanceString), "0");

// Mine a block and verify balance changes
dev::eth::mine(*(web3->ethereum()), 1);
BOOST_CHECK_EQUAL(web3->ethereum()->blockByNumber(LatestBlock).author(), senderAddress);
senderBalance = web3->ethereum()->balanceAt(senderAddress, LatestBlock);
senderBalanceString = rpcClient->eth_getBalance(toJS(senderAddress), "latest");

BOOST_REQUIRE_GT(senderBalance, 0);
BOOST_CHECK_EQUAL(toJS(senderBalance), senderBalanceString);

// Create and send a tx
auto const txAmount = senderBalance / 3u;
auto const gasPrice = 10 * dev::eth::szabo;
auto const gas = EVMSchedule().txGas;
auto const receiver = KeyPair::create();

Json::Value t;
t["from"] = toJS(senderAddress);
t["value"] = jsToDecimal(toJS(txAmount));
t["to"] = toJS(receiver.address());
t["data"] = toJS(bytes());
t["gas"] = toJS(gas);
t["gasPrice"] = toJS(gasPrice);

std::string txHash = rpcClient->eth_sendTransaction(t);
BOOST_REQUIRE(!txHash.empty());

// Send the tx again
txHash = rpcClient->eth_sendTransaction(t);

accountHolder->setAccounts({});
dev::eth::mine(*(web3->ethereum()), 1);

countAt = jsToU256(rpcClient->eth_getTransactionCount(toJS(senderAddress), "latest"));
auto const receiverBalance = web3->ethereum()->balanceAt(receiver.address());
string const receiverBalanceString =
rpcClient->eth_getBalance(toJS(receiver.address()), "latest");

BOOST_CHECK_EQUAL(countAt, web3->ethereum()->countAt(senderAddress));
BOOST_CHECK_EQUAL(countAt, 2);
BOOST_CHECK_EQUAL(toJS(receiverBalance), receiverBalanceString);
BOOST_CHECK_EQUAL(jsToU256(receiverBalanceString), 2 * txAmount);
BOOST_CHECK_EQUAL(txAmount * 2, receiverBalance);
}


BOOST_AUTO_TEST_CASE(eth_sendRawTransaction_validTransaction)
{
auto senderAddress = coinbase.address();
Expand Down

0 comments on commit 1f29ce6

Please sign in to comment.