diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 8d3ea895083..8f72cf46004 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -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. diff --git a/test/unittests/libweb3jsonrpc/jsonrpc.cpp b/test/unittests/libweb3jsonrpc/jsonrpc.cpp index 25a90f4daa6..481fb57f443 100644 --- a/test/unittests/libweb3jsonrpc/jsonrpc.cpp +++ b/test/unittests/libweb3jsonrpc/jsonrpc.cpp @@ -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); @@ -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();