diff --git a/hook/utils-tests.js b/hook/utils-tests.js index f198233a1..0af34a6c3 100644 --- a/hook/utils-tests.js +++ b/hook/utils-tests.js @@ -233,7 +233,8 @@ module.exports = { let base_drops = fees.base_fee delete txn_to_send['SigningPubKey'] - txn_to_send['Fee'] = base_drops + ''; + if (txn_to_send['Fee'] === undefined) + txn_to_send['Fee'] = base_drops + ''; api.request( { diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index 68a62fd98..fbbee118f 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -331,12 +331,35 @@ Transactor::calculateBaseFee(ReadView const& view, STTx const& tx) calculateHookChainFee(view, tx, keylet::hook(tshAcc)); } + XRPAmount accumulator = baseFee; + + // fee based on memos, 1 drop per byte + if (tx.isFieldPresent(sfMemos)) + { + auto const& memos = tx.getFieldArray(sfMemos); + for (auto const& memo : memos) + { + auto memoObj = dynamic_cast(&memo); + for (auto const& memoElement : *memoObj) + { + auto const charCount = + const_cast(memoElement).downcast().size(); + + // handle overflow (should be impossible) + if (accumulator + charCount < accumulator) + + return XRPAmount{INITIAL_XRP.drops()}; + + accumulator += charCount; + } + } + } + // RH NOTE: hookExecutionFee = 0, burden = 1 if hooks is not enabled - // The calculation is (baseFee * burden) + (signerCount * baseFee) + hookExecutionFee + // The calculation is (baseFee * burden) + (signerCount * baseFee) + hookExecutionFee + memoBytes // To ensure there are no overflows or illegal negatives we will do each operation with an overflow check // between and if there is a problem then return the highest possible fee to fail to the transaction. - XRPAmount accumulator = baseFee; do { if (accumulator * burden < accumulator) diff --git a/src/ripple/app/tx/impl/URIToken.cpp b/src/ripple/app/tx/impl/URIToken.cpp index 54fd5376f..96918be13 100644 --- a/src/ripple/app/tx/impl/URIToken.cpp +++ b/src/ripple/app/tx/impl/URIToken.cpp @@ -40,7 +40,7 @@ URIToken::preflight(PreflightContext const& ctx) return ret; uint32_t flags = ctx.tx.getFlags(); - uint16_t tt = ctx.tx.getFieldU16(sfTransactionType); + auto const tt = ctx.tx.getTxnType(); // the validation for amount is the same regardless of which txn is appears on if (ctx.tx.isFieldPresent(sfAmount)) @@ -60,13 +60,20 @@ URIToken::preflight(PreflightContext const& ctx) return temBAD_CURRENCY; } - if (tt == ttURITOKEN_MINT && amt == beast::zero && - !ctx.tx.isFieldPresent(sfDestination)) + if (amt == beast::zero && !ctx.tx.isFieldPresent(sfDestination)) { - JLOG(ctx.j.warn()) << "Malformed transaction. " - << "If no sell-to destination is specified " - "then a non-zero price must be set."; - return temMALFORMED; + if (tt == ttURITOKEN_BUY) + { + // buy operation does not specify a destination, and can have a zero amount + // pass + } + else + { + JLOG(ctx.j.warn()) << "Malformed transaction. " + << "If no sell-to destination is specified " + "then a non-zero price must be set."; + return temMALFORMED; + } } }