diff --git a/src/libxrpl/tx/Transactor.cpp b/src/libxrpl/tx/Transactor.cpp index 920a97f3e2..5b428bed66 100644 --- a/src/libxrpl/tx/Transactor.cpp +++ b/src/libxrpl/tx/Transactor.cpp @@ -929,11 +929,16 @@ Transactor::checkSign( if ((view.rules().enabled(featureLendingProtocol) || view.rules().enabled(featureBatchV1_1) || view.rules().enabled(fixCleanup3_3_0)) && - isPseudoAccount(sle)) + isPseudoAccount(sle) && !(parentBatchId && sle->isFieldPresent(sfContractID))) { // Pseudo-accounts can't sign transactions. This check is gated on a // few different amendments so that it takes effect as soon as any of // them is activated. + // + // A contract's pseudo-account is the exception for inner transactions: + // the contract emits them, and the emit host functions allow only the + // running contract's own account. A Batch can't use this, because its + // BatchSigners are checked without a parentBatchId. return tefBAD_AUTH; } } diff --git a/src/libxrpl/tx/wasm/ContractHostFuncImpl.cpp b/src/libxrpl/tx/wasm/ContractHostFuncImpl.cpp index f528e0e435..3eb2375601 100644 --- a/src/libxrpl/tx/wasm/ContractHostFuncImpl.cpp +++ b/src/libxrpl/tx/wasm/ContractHostFuncImpl.cpp @@ -973,6 +973,11 @@ ContractHostFunctionsImpl::emitBuiltTxn(std::uint32_t const& index) return std::unexpected(HostFunctionError::SubmitTxnFailure); } + // Inner transactions skip the signature check, so a contract may emit + // only from its own account. + if (stx->getAccountID(sfAccount) != contractCtx.result.contractAccount) + return std::unexpected(HostFunctionError::SubmitTxnFailure); + // Use a persistent emit view that is seeded with the // transactor's pending state changes (balances, consumed // sequence, etc.) so that each emitted transaction validates @@ -1040,6 +1045,10 @@ ContractHostFunctionsImpl::emitTxn(std::shared_ptr const& stxPtr) return std::unexpected(HostFunctionError::SubmitTxnFailure); } + // See emitBuiltTxn: a contract may emit only from its own account. + if (txPtr->getAccountID(sfAccount) != contractCtx.result.contractAccount) + return std::unexpected(HostFunctionError::SubmitTxnFailure); + // Use a persistent emit view seeded with the transactor's // pending state, and do a full apply() for each emission // (see emitBuiltTxn for detailed rationale).