diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -9,6 +9,8 @@ #include #include +#include + std::string COutPoint::ToString() const { return strprintf("COutPoint(%s, %u)", txid.ToString().substr(0, 10), n); } @@ -74,12 +76,14 @@ Amount CTransaction::GetValueOut() const { Amount nValueOut = Amount::zero(); for (const auto &tx_out : vout) { - nValueOut += tx_out.nValue; - if (!MoneyRange(tx_out.nValue) || !MoneyRange(nValueOut)) { + if (!MoneyRange(tx_out.nValue) || + !MoneyRange(nValueOut + tx_out.nValue)) { throw std::runtime_error(std::string(__func__) + ": value out of range"); } + nValueOut += tx_out.nValue; } + assert(MoneyRange(nValueOut)); return nValueOut; } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -886,4 +886,38 @@ BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-undersize"); } +BOOST_AUTO_TEST_CASE(tx_getvalueout) { + CMutableTransaction mtx; + + // Negative output value + mtx.vout.resize(1); + mtx.vout[0].nValue = -1 * SATOSHI; + CTransaction negative_tx{mtx}; + BOOST_CHECK_THROW(negative_tx.GetValueOut(), std::runtime_error); + + // Too high output + mtx.vout[0].nValue = MAX_MONEY + 1 * SATOSHI; + CTransaction too_high_tx{mtx}; + BOOST_CHECK_THROW(too_high_tx.GetValueOut(), std::runtime_error); + + // Too high sum + mtx.vout[0].nValue = MAX_MONEY - 1 * SATOSHI; + mtx.vout[1].nValue = 2 * SATOSHI; + CTransaction too_high_sum_tx{mtx}; + BOOST_CHECK_THROW(too_high_sum_tx.GetValueOut(), std::runtime_error); + + // Valid sum + mtx.vout[0].nValue = 42 * SATOSHI; + mtx.vout[1].nValue = 1337 * SATOSHI; + CTransaction valid_tx{mtx}; + BOOST_CHECK_EQUAL(valid_tx.GetValueOut(), 1379 * SATOSHI); + + // First output valid, second output causing an int64 overflow for the sum + mtx.vout.resize(2); + mtx.vout[0].nValue = 2 * SATOSHI; + mtx.vout[1].nValue = (std::numeric_limits::max() - 1) * SATOSHI; + CTransaction overflow_sum_tx{mtx}; + BOOST_CHECK_THROW(overflow_sum_tx.GetValueOut(), std::runtime_error); +} + BOOST_AUTO_TEST_SUITE_END()