diff --git a/hook/govern.c b/hook/govern.c index c277b497d..af18607a1 100644 --- a/hook/govern.c +++ b/hook/govern.c @@ -107,7 +107,7 @@ int64_t hook(uint32_t r) int64_t tt = otxn_type(); - if (tt != 99 && tt != ) // ttINVOKE or ttGENESIS_MINT accepted + if (tt != 99) // ttINVOKE or ttGENESIS_MINT accepted DONE("Governance: Passing non-Invoke txn. HookOn should be changed to avoid this."); // get the account id @@ -124,7 +124,6 @@ int64_t hook(uint32_t r) TRACEVAR(member_count); // outgoing txns to other hooks allowed - // but a self ttINVOKE means rewards hook is trying to get the governance hook to distribute governance rewards int64_t is_distribution = 0; int64_t is_setup = member_count == DOESNT_EXIST; diff --git a/src/ripple/app/tx/impl/InvariantCheck.cpp b/src/ripple/app/tx/impl/InvariantCheck.cpp index 821ce18e4..1c4507180 100644 --- a/src/ripple/app/tx/impl/InvariantCheck.cpp +++ b/src/ripple/app/tx/impl/InvariantCheck.cpp @@ -143,13 +143,15 @@ XRPNotCreated::visitEntry( bool XRPNotCreated::finalize( STTx const& tx, - TER const, + TER const res, XRPAmount const fee, ReadView const& view, beast::Journal const& j) { - if (view.rules().enabled(featureImport) && tx.getTxnType() == ttIMPORT) + auto const tt = tx.getTxnType(); + + if (view.rules().enabled(featureImport) && tt == ttIMPORT && res == tesSUCCESS) { // different rules for ttIMPORT auto const [inner, meta] = Import::getInnerTxn(tx, j); @@ -176,6 +178,24 @@ XRPNotCreated::finalize( return (drops_ == dropsAdded.drops() - fee.drops()); } + if (view.rules().enabled(featureXahauGenesis) && tt == ttGENESIS_MINT && res == tesSUCCESS) + { + // different rules for ttGENESIS_MINT + auto const& dests = tx.getFieldArray(sfGenesisMints); + XRPAmount dropsAdded { beast::zero }; + for (auto const& dest: dests) + dropsAdded += dest.getFieldAmount(sfAmount).xrp(); + + JLOG(j.trace()) + << "Invariant XRPNotCreated GenesisMint: " + << "dropsAdded: " << dropsAdded + << " fee.drops(): " << fee.drops() + << " drops_: " << drops_ + << " dropsAdded - fee.drops(): " << dropsAdded - fee.drops(); + + return (drops_ == dropsAdded.drops() - fee.drops()); + } + // The net change should never be positive, as this would mean that the // transaction created XRP out of thin air. That's not possible. if (drops_ > 0) @@ -518,16 +538,16 @@ ValidNewAccountRoot::finalize( if (accountsCreated_ == 0) return true; - if (accountsCreated_ > 1) + auto tt = tx.getTxnType(); + + if (accountsCreated_ > 1 && tt != ttGENESIS_MINT) { JLOG(j.fatal()) << "Invariant failed: multiple accounts " "created in a single transaction"; return false; } - // From this point on we know exactly one account was created. - auto tt = tx.getTxnType(); - if ((tt == ttPAYMENT || tt == ttIMPORT) && result == tesSUCCESS) + if ((tt == ttPAYMENT || tt == ttIMPORT || tt == ttGENESIS_MINT) && result == tesSUCCESS) { std::uint32_t const startingSeq{ view.rules().enabled(featureDeletableAccounts) ? view.seq() : 1}; diff --git a/src/ripple/protocol/SField.h b/src/ripple/protocol/SField.h index fa67c6b14..1243f239e 100644 --- a/src/ripple/protocol/SField.h +++ b/src/ripple/protocol/SField.h @@ -594,6 +594,7 @@ extern SField const sfAffectedNodes; extern SField const sfMemos; extern SField const sfNFTokens; extern SField const sfHooks; +extern SField const sfGenesisMint; // array of objects (uncommon) extern SField const sfMajorities; @@ -603,7 +604,7 @@ extern SField const sfHookExecution; extern SField const sfHookParameters; extern SField const sfHooks; extern SField const sfHookGrants; -extern SField const sfDestinations; +extern SField const sfGenesisMints; //------------------------------------------------------------------------------ diff --git a/src/ripple/protocol/impl/InnerObjectFormats.cpp b/src/ripple/protocol/impl/InnerObjectFormats.cpp index 08944542d..f0a5c7918 100644 --- a/src/ripple/protocol/impl/InnerObjectFormats.cpp +++ b/src/ripple/protocol/impl/InnerObjectFormats.cpp @@ -125,6 +125,13 @@ InnerObjectFormats::InnerObjectFormats() {sfNFTokenID, soeREQUIRED}, {sfURI, soeOPTIONAL}, }); + + add(sfGenesisMint.jsonName.c_str(), + sfGenesisMint.getCode(), + { + {sfDestination, soeREQUIRED}, + {sfAmount, soeREQUIRED}, + }); } InnerObjectFormats const& diff --git a/src/ripple/protocol/impl/SField.cpp b/src/ripple/protocol/impl/SField.cpp index 096c95be3..22fd2957f 100644 --- a/src/ripple/protocol/impl/SField.cpp +++ b/src/ripple/protocol/impl/SField.cpp @@ -336,6 +336,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfHookExecution, "HookExecution", OBJECT, CONSTRUCT_UNTYPED_SFIELD(sfHookDefinition, "HookDefinition", OBJECT, 22); CONSTRUCT_UNTYPED_SFIELD(sfHookParameter, "HookParameter", OBJECT, 23); CONSTRUCT_UNTYPED_SFIELD(sfHookGrant, "HookGrant", OBJECT, 24); +CONSTRUCT_UNTYPED_SFIELD(sfGenesisMint, "GenesisMint", OBJECT, 25); // array of objects // ARRAY/1 is reserved for end of array @@ -356,7 +357,7 @@ CONSTRUCT_UNTYPED_SFIELD(sfDisabledValidators, "DisabledValidators", ARRAY, CONSTRUCT_UNTYPED_SFIELD(sfHookExecutions, "HookExecutions", ARRAY, 18); CONSTRUCT_UNTYPED_SFIELD(sfHookParameters, "HookParameters", ARRAY, 19); CONSTRUCT_UNTYPED_SFIELD(sfHookGrants, "HookGrants", ARRAY, 20); -CONSTRUCT_UNTYPED_SFIELD(sfDestinations, "Destinations", ARRAY, 21); +CONSTRUCT_UNTYPED_SFIELD(sfGenesisMints, "GenesisMints", ARRAY, 21); // clang-format on diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index 28fe1c901..7bd50f9db 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -361,8 +361,7 @@ TxFormats::TxFormats() add(jss::GenesisMint, ttGENESIS_MINT, { - {sfDestinations, soeREQUIRED}, - {sfAmount, soeREQUIRED} + {sfGenesisMints, soeREQUIRED}, }, commonFields);