@@ -225,7 +225,9 @@ SetHook::inferOperation(STObject const& hookSetObj)
225225 !hasHash && !hasCode && !hookSetObj.isFieldPresent (sfHookGrants) &&
226226 !hookSetObj.isFieldPresent (sfHookNamespace) &&
227227 !hookSetObj.isFieldPresent (sfHookParameters) &&
228- !hookSetObj.isFieldPresent (sfHookOn) &&
228+ !(hookSetObj.isFieldPresent (sfHookOn) ||
229+ (hookSetObj.isFieldPresent (sfHookOnOutgoing) &&
230+ hookSetObj.isFieldPresent (sfHookOnIncoming))) &&
229231 !hookSetObj.isFieldPresent (sfHookCanEmit) &&
230232 !hookSetObj.isFieldPresent (sfHookApiVersion) &&
231233 !hookSetObj.isFieldPresent (sfFlags))
@@ -261,6 +263,8 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
261263 if (hookSetObj.isFieldPresent (sfHookGrants) ||
262264 hookSetObj.isFieldPresent (sfHookParameters) ||
263265 hookSetObj.isFieldPresent (sfHookOn) ||
266+ hookSetObj.isFieldPresent (sfHookOnOutgoing) ||
267+ hookSetObj.isFieldPresent (sfHookOnIncoming) ||
264268 hookSetObj.isFieldPresent (sfHookCanEmit) ||
265269 hookSetObj.isFieldPresent (sfHookApiVersion) ||
266270 !hookSetObj.isFieldPresent (sfFlags) ||
@@ -291,6 +295,8 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
291295 if (hookSetObj.isFieldPresent (sfHookGrants) ||
292296 hookSetObj.isFieldPresent (sfHookParameters) ||
293297 hookSetObj.isFieldPresent (sfHookOn) ||
298+ hookSetObj.isFieldPresent (sfHookOnOutgoing) ||
299+ hookSetObj.isFieldPresent (sfHookOnIncoming) ||
294300 hookSetObj.isFieldPresent (sfHookCanEmit) ||
295301 hookSetObj.isFieldPresent (sfHookApiVersion) ||
296302 hookSetObj.isFieldPresent (sfHookNamespace) ||
@@ -448,12 +454,53 @@ SetHook::validateHookSetEntry(SetHookCtx& ctx, STObject const& hookSetObj)
448454 // validate sfHookOn
449455 if (!hookSetObj.isFieldPresent (sfHookOn))
450456 {
451- JLOG (ctx.j .trace ())
452- << " HookSet(" << hook::log::HOOKON_MISSING << " )["
453- << HS_ACC ()
454- << " ]: Malformed transaction: SetHook must include "
455- " sfHookOn when creating a new hook." ;
456- return false ;
457+ if (!ctx.rules .enabled (featureHookOnV2))
458+ {
459+ JLOG (ctx.j .trace ())
460+ << " HookSet(" << hook::log::HOOKON_MISSING << " )["
461+ << HS_ACC ()
462+ << " ]: Malformed transaction: SetHook must include "
463+ " sfHookOn before featureHookOnV2 is enabled." ;
464+ return false ;
465+ }
466+
467+ if (!hookSetObj.isFieldPresent (sfHookOnOutgoing) ||
468+ !hookSetObj.isFieldPresent (sfHookOnIncoming))
469+ {
470+ JLOG (ctx.j .trace ())
471+ << " HookSet(" << hook::log::HOOKON_MISSING << " )["
472+ << HS_ACC ()
473+ << " ]: Malformed transaction: SetHook must include "
474+ " sfHookOnOutgoing and sfHookOnIncoming "
475+ " when creating a new hook without sfHookOn." ;
476+ return false ;
477+ }
478+
479+ auto const outgoing = hookSetObj.getFieldH256 (sfHookOnOutgoing);
480+ auto const incoming = hookSetObj.getFieldH256 (sfHookOnIncoming);
481+ if (outgoing == incoming)
482+ {
483+ JLOG (ctx.j .trace ())
484+ << " HookSet(" << hook::log::HOOKON_MISSING << " )["
485+ << HS_ACC ()
486+ << " ]: Malformed transaction: SetHook outgoing and "
487+ " incoming hookon must be different." ;
488+ return false ;
489+ }
490+ }
491+ else
492+ {
493+ if (hookSetObj.isFieldPresent (sfHookOnOutgoing) ||
494+ hookSetObj.isFieldPresent (sfHookOnIncoming))
495+ {
496+ JLOG (ctx.j .trace ())
497+ << " HookSet(" << hook::log::HOOKON_MISSING << " )["
498+ << HS_ACC ()
499+ << " ]: Malformed transaction: SetHook must no"
500+ " include sfHookOnOutgoing and sfHookOnIncoming "
501+ " when creating a new hook with sfHookOn." ;
502+ return false ;
503+ }
457504 }
458505
459506 // validate sfHookCanEmit
@@ -742,7 +789,8 @@ SetHook::preflight(PreflightContext const& ctx)
742789
743790 if (name != sfCreateCode && name != sfHookHash &&
744791 name != sfHookNamespace && name != sfHookParameters &&
745- name != sfHookOn && name != sfHookGrants &&
792+ name != sfHookOn && name != sfHookOnOutgoing &&
793+ name != sfHookOnIncoming && name != sfHookGrants &&
746794 name != sfHookApiVersion && name != sfFlags &&
747795 name != sfHookCanEmit)
748796 {
@@ -1264,10 +1312,14 @@ SetHook::setHook()
12641312 std::optional<ripple::uint256> newNamespace;
12651313 std::optional<ripple::Keylet> newDirKeylet;
12661314
1267- std::optional<uint256> oldHookOn;
12681315 std::optional<uint256> newHookOn;
12691316 std::optional<uint256> defHookOn;
12701317
1318+ std::optional<uint256> newHookOnOutgoing;
1319+ std::optional<uint256> newHookOnIncoming;
1320+ std::optional<uint256> defHookOnOutgoing;
1321+ std::optional<uint256> defHookOnIncoming;
1322+
12711323 std::optional<uint256> oldHookCanEmit;
12721324 std::optional<uint256> newHookCanEmit;
12731325 std::optional<uint256> defHookCanEmit;
@@ -1325,13 +1377,18 @@ SetHook::setHook()
13251377
13261378 oldDirKeylet = keylet::hookStateDir (account_, *oldNamespace);
13271379 oldDirSLE = view ().peek (*oldDirKeylet);
1328- if (oldDefSLE)
1380+ if (oldDefSLE && oldDefSLE-> isFieldPresent (sfHookOn) )
13291381 defHookOn = oldDefSLE->getFieldH256 (sfHookOn);
13301382
1331- if (oldHook->get ().isFieldPresent (sfHookOn))
1332- oldHookOn = oldHook->get ().getFieldH256 (sfHookOn);
1333- else if (defHookOn)
1334- oldHookOn = *defHookOn;
1383+ if (oldDefSLE)
1384+ {
1385+ if (oldDefSLE->isFieldPresent (sfHookOnOutgoing))
1386+ defHookOnOutgoing =
1387+ oldDefSLE->getFieldH256 (sfHookOnOutgoing);
1388+ if (oldDefSLE->isFieldPresent (sfHookOnIncoming))
1389+ defHookOnIncoming =
1390+ oldDefSLE->getFieldH256 (sfHookOnIncoming);
1391+ }
13351392
13361393 if (oldDefSLE && oldDefSLE->isFieldPresent (sfHookCanEmit))
13371394 defHookCanEmit = oldDefSLE->getFieldH256 (sfHookCanEmit);
@@ -1356,6 +1413,14 @@ SetHook::setHook()
13561413 if (hookSetObj->get ().isFieldPresent (sfHookOn))
13571414 newHookOn = hookSetObj->get ().getFieldH256 (sfHookOn);
13581415
1416+ if (hookSetObj->get ().isFieldPresent (sfHookOnOutgoing))
1417+ newHookOnOutgoing =
1418+ hookSetObj->get ().getFieldH256 (sfHookOnOutgoing);
1419+
1420+ if (hookSetObj->get ().isFieldPresent (sfHookOnIncoming))
1421+ newHookOnIncoming =
1422+ hookSetObj->get ().getFieldH256 (sfHookOnIncoming);
1423+
13591424 if (hookSetObj->get ().isFieldPresent (sfHookCanEmit))
13601425 newHookCanEmit = hookSetObj->get ().getFieldH256 (sfHookCanEmit);
13611426
@@ -1469,6 +1534,14 @@ SetHook::setHook()
14691534 if (oldHook->get ().isFieldPresent (sfHookOn))
14701535 newHook.setFieldH256 (
14711536 sfHookOn, oldHook->get ().getFieldH256 (sfHookOn));
1537+ if (oldHook->get ().isFieldPresent (sfHookOnOutgoing))
1538+ newHook.setFieldH256 (
1539+ sfHookOnOutgoing,
1540+ oldHook->get ().getFieldH256 (sfHookOnOutgoing));
1541+ if (oldHook->get ().isFieldPresent (sfHookOnIncoming))
1542+ newHook.setFieldH256 (
1543+ sfHookOnIncoming,
1544+ oldHook->get ().getFieldH256 (sfHookOnIncoming));
14721545 if (oldHook->get ().isFieldPresent (sfHookCanEmit))
14731546 newHook.setFieldH256 (
14741547 sfHookCanEmit,
@@ -1502,6 +1575,24 @@ SetHook::setHook()
15021575 newHook.setFieldH256 (sfHookOn, *newHookOn);
15031576 }
15041577
1578+ if (newHookOnOutgoing)
1579+ {
1580+ if (*defHookOnOutgoing == *newHookOnOutgoing)
1581+ {
1582+ if (newHook.isFieldPresent (sfHookOnOutgoing))
1583+ newHook.makeFieldAbsent (sfHookOnOutgoing);
1584+ }
1585+ }
1586+
1587+ if (newHookOnIncoming)
1588+ {
1589+ if (*defHookOnIncoming == *newHookOnIncoming)
1590+ {
1591+ if (newHook.isFieldPresent (sfHookOnIncoming))
1592+ newHook.makeFieldAbsent (sfHookOnIncoming);
1593+ }
1594+ }
1595+
15051596 // set the hookcanemit field if it differs from definition
15061597 if (newHookCanEmit)
15071598 {
@@ -1663,7 +1754,19 @@ SetHook::setHook()
16631754
16641755 auto newHookDef = std::make_shared<SLE>(keylet);
16651756 newHookDef->setFieldH256 (sfHookHash, *createHookHash);
1666- newHookDef->setFieldH256 (sfHookOn, *newHookOn);
1757+
1758+ // only HookOn or (HookOnOutgoing and HookOnIncoming)
1759+ if (!view ().rules ().enabled (featureHookOnV2) ||
1760+ (!newHookOnOutgoing && !newHookOnIncoming))
1761+ newHookDef->setFieldH256 (sfHookOn, *newHookOn);
1762+ else
1763+ {
1764+ newHookDef->setFieldH256 (
1765+ sfHookOnOutgoing, *newHookOnOutgoing);
1766+ newHookDef->setFieldH256 (
1767+ sfHookOnIncoming, *newHookOnIncoming);
1768+ }
1769+
16671770 if (newHookCanEmit)
16681771 newHookDef->setFieldH256 (
16691772 sfHookCanEmit, *newHookCanEmit);
@@ -1759,17 +1862,49 @@ SetHook::setHook()
17591862
17601863 // change which definition we're using to the new target
17611864 defNamespace = newDefSLE->getFieldH256 (sfHookNamespace);
1762- defHookOn = newDefSLE-> getFieldH256 (sfHookOn);
1865+
17631866 if (newDefSLE->isFieldPresent (sfHookCanEmit))
17641867 defHookCanEmit = newDefSLE->getFieldH256 (sfHookCanEmit);
17651868
17661869 // set the namespace if it differs from the definition namespace
17671870 if (newNamespace && *defNamespace != *newNamespace)
17681871 newHook.setFieldH256 (sfHookNamespace, *newNamespace);
17691872
1873+ defHookOn = newDefSLE->getFieldH256 (sfHookOn);
1874+ defHookOnIncoming = newDefSLE->getFieldH256 (sfHookOnIncoming);
1875+ defHookOnOutgoing = newDefSLE->getFieldH256 (sfHookOnOutgoing);
1876+
17701877 // set the hookon field if it differs from definition
1771- if (newHookOn && *defHookOn != *newHookOn)
1772- newHook.setFieldH256 (sfHookOn, *newHookOn);
1878+ if (newHookOn)
1879+ {
1880+ auto const diffFromDef = defHookOn != *newHookOn;
1881+ auto const hasIncOutgDef =
1882+ *defHookOnIncoming != *defHookOnOutgoing ||
1883+ *newHookOn != *defHookOnIncoming;
1884+ if (diffFromDef || hasIncOutgDef)
1885+ {
1886+ newHook.setFieldH256 (sfHookOn, *newHookOn);
1887+ }
1888+ }
1889+
1890+ // set the incoming/outgoing hookon field if it differs from
1891+ // definition
1892+ if (newHookOnIncoming || newHookOnOutgoing)
1893+ {
1894+ auto const diffFromDef =
1895+ *defHookOnIncoming != *newHookOnIncoming ||
1896+ *defHookOnOutgoing != *newHookOnOutgoing;
1897+ auto const hasHookOnDef =
1898+ *newHookOnIncoming != *defHookOn ||
1899+ *newHookOnOutgoing != *defHookOn;
1900+ if (diffFromDef || hasHookOnDef)
1901+ {
1902+ newHook.setFieldH256 (
1903+ sfHookOnIncoming, *newHookOnIncoming);
1904+ newHook.setFieldH256 (
1905+ sfHookOnOutgoing, *newHookOnOutgoing);
1906+ }
1907+ }
17731908
17741909 // set the hookcanemit field if it differs from definition
17751910 if (newHookCanEmit &&
@@ -1825,8 +1960,8 @@ SetHook::setHook()
18251960 // sfHook: 1 reserve PER non-blank entry
18261961 // sfParameters: 1 reserve PER entry
18271962 // sfGrants are: 1 reserve PER entry
1828- // sfHookHash, sfHookNamespace, sfHookOn, sfHookCanEmit ,
1829- // sfHookApiVersion, sfFlags: free
1963+ // sfHookHash, sfHookNamespace, sfHookOn, sfHookOnOutgoing ,
1964+ // sfHookOnIncoming, sfHookCanEmit sfHookApiVersion, sfFlags: free
18301965
18311966 // sfHookDefinition is not reserved because it is an unowned object,
18321967 // rather the uploader is billed via fee according to the following:
@@ -1988,6 +2123,6 @@ SetHook::setHook()
19882123 }
19892124
19902125 return nsDeleteResult;
1991- }
2126+ } // namespace ripple
19922127
19932128} // namespace ripple
0 commit comments