#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { NotTEC DelegateSet::preflight(PreflightContext const& ctx) { auto const& permissions = ctx.tx.getFieldArray(sfPermissions); if (permissions.size() > kPermissionMaxSize) return temARRAY_TOO_LARGE; // can not authorize self if (ctx.tx[sfAccount] == ctx.tx[sfAuthorize]) return temMALFORMED; std::unordered_set permissionSet; for (auto const& permission : permissions) { if (!permissionSet.insert(permission[sfPermissionValue]).second) return temMALFORMED; if (!Permission::getInstance().isDelegable(permission[sfPermissionValue], ctx.rules)) return temMALFORMED; } return tesSUCCESS; } TER DelegateSet::preclaim(PreclaimContext const& ctx) { if (!ctx.view.exists(keylet::account(ctx.tx[sfAccount]))) return terNO_ACCOUNT; // LCOV_EXCL_LINE auto const sleAuthorize = ctx.view.read(keylet::account(ctx.tx[sfAuthorize])); if (!sleAuthorize) return tecNO_TARGET; if (isPseudoAccount(sleAuthorize)) return tecNO_PERMISSION; // Deleting the delegate object is invalid if it doesn’t exist. if (ctx.tx.getFieldArray(sfPermissions).empty() && !ctx.view.exists(keylet::delegate(ctx.tx[sfAccount], ctx.tx[sfAuthorize]))) { return tecNO_ENTRY; } return tesSUCCESS; } TER DelegateSet::doApply() { auto const sleOwner = ctx_.view().peek(keylet::account(accountID_)); if (!sleOwner) return tefINTERNAL; // LCOV_EXCL_LINE auto const& authAccount = ctx_.tx[sfAuthorize]; auto const delegateKey = keylet::delegate(accountID_, authAccount); auto sle = ctx_.view().peek(delegateKey); if (sle) { auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); if (permissions.empty()) { // if permissions array is empty, delete the ledger object. return deleteDelegate(view(), sle, j_); } sle->setFieldArray(sfPermissions, permissions); ctx_.view().update(sle); return tesSUCCESS; } auto const& permissions = ctx_.tx.getFieldArray(sfPermissions); if (permissions.empty()) return tecINTERNAL; // LCOV_EXCL_LINE STAmount const reserve{ ctx_.view().fees().accountReserve(sleOwner->getFieldU32(sfOwnerCount) + 1)}; if (preFeeBalance_ < reserve) return tecINSUFFICIENT_RESERVE; sle = std::make_shared(delegateKey); sle->setAccountID(sfAccount, accountID_); sle->setAccountID(sfAuthorize, authAccount); sle->setFieldArray(sfPermissions, permissions); // Add to delegating account's owner directory auto const page = ctx_.view().dirInsert( keylet::ownerDir(accountID_), delegateKey, describeOwnerDir(accountID_)); if (!page) return tecDIR_FULL; // LCOV_EXCL_LINE (*sle)[sfOwnerNode] = *page; // Add to authorized account's owner directory so AccountDelete can find // and clean up inbound delegations when the authorized account is deleted. auto const destPage = ctx_.view().dirInsert( keylet::ownerDir(authAccount), delegateKey, describeOwnerDir(authAccount)); if (!destPage) return tecDIR_FULL; // LCOV_EXCL_LINE (*sle)[sfDestinationNode] = *destPage; ctx_.view().insert(sle); adjustOwnerCount(ctx_.view(), sleOwner, 1, ctx_.journal); return tesSUCCESS; } TER DelegateSet::deleteDelegate(ApplyView& view, SLE::ref sle, beast::Journal j) { if (!sle) return tecINTERNAL; // LCOV_EXCL_LINE auto const delegator = (*sle)[sfAccount]; auto const delegatee = (*sle)[sfAuthorize]; // Remove from delegating account's owner directory if (!view.dirRemove(keylet::ownerDir(delegator), (*sle)[sfOwnerNode], sle->key(), false)) { // LCOV_EXCL_START JLOG(j.fatal()) << "Unable to delete Delegate from owner."; return tefBAD_LEDGER; // LCOV_EXCL_STOP } // Remove from authorized account's owner directory, if present if (auto const optPage = (*sle)[~sfDestinationNode]) { if (!view.dirRemove(keylet::ownerDir(delegatee), *optPage, sle->key(), false)) { // LCOV_EXCL_START JLOG(j.fatal()) << "Unable to delete Delegate from authorized account."; return tefBAD_LEDGER; // LCOV_EXCL_STOP } } // Only the delegating account's owner count was incremented on creation auto const sleOwner = view.peek(keylet::account(delegator)); if (!sleOwner) return tecINTERNAL; // LCOV_EXCL_LINE adjustOwnerCount(view, sleOwner, -1, j); view.erase(sle); return tesSUCCESS; } void DelegateSet::visitInvariantEntry(bool, SLE::const_ref, SLE::const_ref) { // No transaction-specific invariants yet (future work). } bool DelegateSet::finalizeInvariants(STTx const&, TER, XRPAmount, ReadView const&, beast::Journal const&) { // No transaction-specific invariants yet (future work). return true; } } // namespace xrpl