Compare commits

..

30 Commits

Author SHA1 Message Date
Richard Holland
e5b5efcc87 Merge branch 'dev' into coverage 2026-04-28 11:34:19 +10:00
tequ
a77e6ee7fd use clang-format-18 on check-genesis-hooks workflow 2026-04-28 11:30:54 +10:00
tequ
67034cda77 Update build_xahau.h.sh 2026-04-28 11:30:54 +10:00
tequ
aa1e4d38d3 update workflow 2026-04-28 11:30:54 +10:00
tequ
d4417d3461 remove workflow cache 2026-04-28 11:30:54 +10:00
tequ
6f6e56952e Remove path filters for Genesis Hooks in GitHub Actions workflow 2026-04-28 11:30:54 +10:00
tequ
e673cb5b80 Update GitHub Actions workflow to install binaryen version 100 directly from GitHub Releases 2026-04-28 11:30:54 +10:00
tequ
cfb1b36bae Add wasm-opt to required tools and enforce version check 2026-04-28 11:30:54 +10:00
tequ
628c942a98 update xahau.h using binaryen version=100 2026-04-28 11:30:54 +10:00
tequ
4cb64497e9 revert xahau.h 2026-04-28 11:30:54 +10:00
tequ
96065296ab revert arm 2026-04-28 11:30:54 +10:00
tequ
6ab87ed696 wasmcc version 2026-04-28 11:30:54 +10:00
tequ
1b7c70d3ba try arm 2026-04-28 11:30:54 +10:00
tequ
ba2b188aaf fix 2026-04-28 11:30:54 +10:00
tequ
7a78672983 fix 2026-04-28 11:30:54 +10:00
tequ
14de6cc76d fix wasienv script 2026-04-28 11:30:54 +10:00
tequ
13f4313f64 restore headers for xahau.h and build, check in github actions 2026-04-28 11:30:54 +10:00
tequ
2244fae464 Merge branch 'dev' into coverage 2026-04-24 14:12:11 +09:00
tequ
8d0e6eedea Merge branch 'dev' into coverage 2026-03-06 16:29:37 +09:00
tequ
59580e2b28 Merge branch 'dev' into coverage 2026-02-24 17:09:52 +09:00
RichardAH
866119cf80 Merge branch 'dev' into coverage 2026-02-18 09:34:45 +10:00
Niq Dudfield
498f63651d Merge branch 'dev' into coverage 2026-02-17 11:14:33 +07:00
Niq Dudfield
4c5c9b14b7 fix: ci build action and coverage workflow fixes (#682)
- move --target before ${VERBOSE_FLAG} so [ci-ga-cmake-verbose] doesn't
  swallow the target argument
- add stdlib: default option for GCC entries (GCC doesn't support -stdlib flag)
- pass stdlib explicitly to coverage build step
- remove dead CMAKE_BUILD_PARALLEL_LEVEL export (doesn't persist across steps,
  and build step already uses --parallel)
- remove duplicate ccache config block
2026-02-17 10:22:59 +09:00
tequ
689f3c07c9 Merge branch 'dev' into coverage 2026-02-16 13:36:42 +09:00
tequ
407cc83241 Merge branch 'dev' into coverage 2026-01-28 14:12:07 +09:00
tequ
97deee10ca Merge branch 'dev' into coverage 2026-01-27 21:30:10 +09:00
tequ
0c8de81657 Merge branch 'dev' into coverage 2026-01-05 19:49:26 +09:00
tequ
cb40a9d726 fix cmake-target 2025-12-24 22:03:45 +09:00
Bronek Kozicki
3d9f8aa7a9 test: improve code coverage reporting (#4849)
* Speed up the generation of coverage reports by using multiple cores.

* Add codecov step to coverage workflow.
2025-12-24 22:03:23 +09:00
tequ
d7fd2adb34 Add coverage workflow 2025-12-24 17:10:46 +09:00
20 changed files with 108 additions and 1804 deletions

View File

@@ -230,9 +230,6 @@ jobs:
build:
needs: matrix-setup
runs-on: ${{ fromJSON(matrix.runs_on) }}
permissions:
id-token: write
contents: read
container:
image: ubuntu:24.04
volumes:
@@ -335,7 +332,7 @@ jobs:
# Install gcovr for coverage jobs
if [ "${{ matrix.job_type }}" = "coverage" ]; then
pipx install "gcovr>=7,<9"
apt-get install -y curl lcov
apt-get install -y lcov
fi
- name: Check environment
@@ -410,8 +407,7 @@ jobs:
cache_version: ${{ env.CACHE_VERSION }}
main_branch: ${{ env.MAIN_BRANCH_NAME }}
stdlib: ${{ matrix.stdlib }}
# Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness
cmake-args: '-Dcoverage=ON -Dcoverage_format=xml -Dcoverage_test_parallelism=$(($(nproc)/2)) -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_CXX_FLAGS="-O0" -DCMAKE_C_FLAGS="-O0"'
cmake-args: '-Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_CXX_FLAGS="-O0" -DCMAKE_C_FLAGS="-O0"'
cmake-target: 'coverage'
ccache_max_size: '100G'
@@ -456,11 +452,15 @@ jobs:
- name: Upload coverage report
if: matrix.job_type == 'coverage'
uses: codecov/codecov-action@v5
uses: wandalen/wretry.action/main@v3
with:
files: coverage.xml
fail_ci_if_error: true
disable_search: true
verbose: true
plugins: noop
use_oidc: true
action: codecov/codecov-action@v4.3.0
with: |
files: coverage.xml
fail_ci_if_error: true
disable_search: true
verbose: true
plugin: noop
token: ${{ secrets.CODECOV_TOKEN }}
attempt_limit: 5
attempt_delay: 210000 # in milliseconds

View File

@@ -80,7 +80,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 114;
static constexpr std::size_t numFeatures = 113;
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated

View File

@@ -31,7 +31,6 @@
// If you add an amendment here, then do not forget to increment `numFeatures`
// in include/xrpl/protocol/Feature.h.
XRPL_FIX (ImportIssuer, Supported::yes, VoteBehavior::DefaultYes)
XRPL_FEATURE(HookAPISerializedType240, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(PermissionedDomains, Supported::no, VoteBehavior::DefaultNo)
XRPL_FEATURE(DynamicNFT, Supported::no, VoteBehavior::DefaultNo)

View File

@@ -18,7 +18,6 @@
//==============================================================================
#include <test/jtx.h>
#include <test/jtx/AMM.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpl/protocol/Feature.h>
#include <xrpl/protocol/jss.h>
@@ -301,28 +300,6 @@ struct ClaimReward_test : public beast::unit_test::suite
env(tx, reward::issuer(issuer), ter(tecNO_ISSUER));
env.close();
}
// tecNO_PERMISSION
// issuer is an AMM account
{
test::jtx::Env env{*this, network::makeNetworkConfig(21337)};
auto const alice = Account("alice");
auto const issuer = Account("issuer");
auto const USD = issuer["USD"];
env.fund(XRP(1000), alice, issuer);
env.close();
AMM amm(env, issuer, XRP(100), USD(100));
BEAST_EXPECT(amm.ammExists());
env(reward::claim(alice),
reward::issuer(amm.ammAccount()),
ter(tecNO_PERMISSION));
env.close();
}
}
void

View File

@@ -19,7 +19,6 @@
#include <test/app/Import_json.h>
#include <test/jtx.h>
#include <test/jtx/AMM.h>
#include <xrpld/app/ledger/LedgerMaster.h>
#include <xrpld/app/misc/AmendmentTable.h>
#include <xrpld/app/misc/HashRouter.h>
@@ -2636,77 +2635,6 @@ class Import_test : public beast::unit_test::suite
ter(temDISABLED));
}
// tecNO_ISSUER, tecNO_PERMISSION
// issuer not found, issuer is an AMM account
{
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const issuer = Account("issuer");
auto const USD = issuer["USD"];
for (bool const withFixImportIssuer : {true, false})
{
auto const amend =
withFixImportIssuer ? features : features - fixImportIssuer;
test::jtx::Env env{
*this, network::makeNetworkVLConfig(21337, keys), amend};
env.fund(XRP(1000), alice, issuer);
env.close();
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
env(import::import(
alice, import::loadXpop(ImportTCAccountSet::w_seed)),
import::issuer(bob),
fee(100'000'000),
withFixImportIssuer ? ter(tecNO_ISSUER) : ter(tesSUCCESS));
env.close();
}
for (bool const withFixImportIssuer : {true, false})
{
auto const amend =
withFixImportIssuer ? features : features - fixImportIssuer;
test::jtx::Env env{
*this, network::makeNetworkVLConfig(21337, keys), amend};
env.fund(XRP(1000), alice, issuer);
env.close();
// burn 10'000 xrp
auto const master = Account("masterpassphrase");
env(noop(master), fee(10'000'000'000), ter(tesSUCCESS));
env.close();
AMM amm(env, issuer, XRP(100), USD(100));
BEAST_EXPECT(amm.ammExists());
env(import::import(
alice, import::loadXpop(ImportTCAccountSet::w_seed)),
import::issuer(amm.ammAccount()),
fee(100'000'000),
withFixImportIssuer ? ter(tecNO_PERMISSION)
: ter(tesSUCCESS));
env.close();
}
// env.enableFeature(fixImportIssuer);
// env.close();
// env(import::import(
// carol, import::loadXpop(ImportTCAccountSet::w_seed)),
// import::issuer(bob),
// ter(tecNO_ISSUER));
// env.close();
// env(import::import(
// dave, import::loadXpop(ImportTCAccountSet::w_seed)),
// import::issuer(amm.ammAccount()),
// // fee(100'000'000),
// ter(tecNO_PERMISSION));
// env.close();
}
// tefINTERNAL
// during preclaim could not parse xpop, bailing.
{

View File

@@ -18,7 +18,6 @@
//==============================================================================
#include <test/jtx.h>
#include <test/jtx/AMM.h>
#include <xrpl/protocol/jss.h>
namespace ripple {
@@ -169,27 +168,6 @@ class Invoke_test : public beast::unit_test::suite
fee(feeDrops),
ter(tecNO_TARGET));
}
// tecNO_PERMISSION
// issuer is an AMM account
{
test::jtx::Env env{*this, network::makeNetworkConfig(21337)};
auto const alice = Account("alice");
auto const issuer = Account("issuer");
auto const USD = issuer["USD"];
env.fund(XRP(1000), alice, issuer);
env.close();
AMM amm(env, issuer, XRP(100), USD(100));
BEAST_EXPECT(amm.ammExists());
env(invoke::invoke(alice),
invoke::dest(amm.ammAccount()),
ter(tecNO_PERMISSION));
env.close();
}
}
void

View File

@@ -18,7 +18,6 @@
//==============================================================================
#include <test/jtx.h>
#include <test/jtx/AMM.h>
#include <xrpld/core/ConfigSections.h>
#include <xrpld/ledger/Dir.h>
#include <xrpl/basics/chrono.h>
@@ -411,21 +410,6 @@ struct Remit_test : public beast::unit_test::suite
env.close();
}
// tecNO_PERMISSION - inform account is an AMM
{
Env env{*this, features};
env.fund(XRP(1000), gw, alice, bob);
env.close();
AMM amm(env, gw, XRP(100), USD(100));
BEAST_EXPECT(amm.ammExists());
auto tx = remit::remit(alice, bob);
tx[sfInform.jsonName] = to_string(amm.ammAccount());
env(tx, alice, ter(tecNO_PERMISSION));
env.close();
}
// tecNO_PERMISSION - lsfDisallowIncomingRemit
// DA: see testAllowIncoming

File diff suppressed because it is too large Load Diff

View File

@@ -140,7 +140,8 @@ struct XahauGenesis_test : public beast::unit_test::suite
bool skipTests = false,
bool const testFlag = false,
bool const badNetID = false,
uint32_t const expectedOwnerCount = 14 /** case for testFlag=false */)
uint32_t const expectedOwnerCount =
10 /** testFlag ? 10 : 14 (default) */)
{
using namespace jtx;
@@ -249,7 +250,9 @@ struct XahauGenesis_test : public beast::unit_test::suite
genesisAccRoot->getFieldAmount(sfBalance) ==
XahauGenesis::GenesisAmount);
BEAST_EXPECT(
genesisAccRoot->getFieldU32(sfOwnerCount) == expectedOwnerCount);
genesisAccRoot->getFieldU32(sfOwnerCount) == !testFlag
? expectedOwnerCount
: 14);
// ensure the definitions are correctly set
{
@@ -592,8 +595,7 @@ struct XahauGenesis_test : public beast::unit_test::suite
false,
true,
{},
2 /*Hook objects *2 */ + 3 /* IRR,IRD,IMC HookStates */ +
members.size());
3 /* IRR,IRD,IMC */ + members.size() + tables.size());
env.close();
env.close();
@@ -2325,7 +2327,7 @@ struct XahauGenesis_test : public beast::unit_test::suite
{
BEAST_EXPECT(
root->getFieldU32(sfOwnerCount) ==
(mc * 2 + 2 + paramsCount));
mc * 2 + 2 + paramsCount);
BEAST_EXPECT(root->getFieldU32(sfFlags) & lsfDisableMaster);
BEAST_EXPECT(root->getAccountID(sfRegularKey) == noAccount());
}

View File

@@ -44,7 +44,7 @@ import(jtx::Account const& account, Json::Value const& xpop)
void
issuer::operator()(Env& env, JTx& jt) const
{
jt.jv[sfIssuer.jsonName] = to_string(issuer_);
jt.jv[sfIssuer.jsonName] = issuer_.human();
}
Json::Value

View File

@@ -66,7 +66,7 @@ blob::operator()(Env& env, JTx& jt) const
void
dest::operator()(Env& env, JTx& jt) const
{
jt.jv[sfDestination.jsonName] = to_string(dest_);
jt.jv[sfDestination.jsonName] = dest_.human();
}
} // namespace invoke

View File

@@ -29,24 +29,18 @@ namespace reward {
// Claim a reward.
Json::Value
claim(jtx::Account const& account)
{
return claim(account.id());
}
Json::Value
claim(AccountID const& account)
{
using namespace jtx;
Json::Value jv;
jv[jss::TransactionType] = jss::ClaimReward;
jv[jss::Account] = to_string(account);
jv[jss::Account] = account.human();
return jv;
}
void
issuer::operator()(Env& env, JTx& jt) const
{
jt.jv[sfIssuer.jsonName] = to_string(issuer_);
jt.jv[sfIssuer.jsonName] = issuer_.human();
}
} // namespace reward

View File

@@ -37,10 +37,10 @@ import(jtx::Account const& account, Json::Value const& xpop);
class issuer
{
private:
AccountID issuer_;
jtx::Account issuer_;
public:
explicit issuer(AccountID const& issuer) : issuer_(issuer)
explicit issuer(jtx::Account const& issuer) : issuer_(issuer)
{
}

View File

@@ -58,10 +58,10 @@ public:
class dest
{
private:
AccountID dest_;
jtx::Account dest_;
public:
explicit dest(AccountID const& dest) : dest_(dest)
explicit dest(jtx::Account const& dest) : dest_(dest)
{
}

View File

@@ -34,24 +34,17 @@ namespace reward {
Json::Value
claim(jtx::Account const& account);
Json::Value
claim(AccountID const& account);
/** Sets the optional Issuer on a JTx. */
class issuer
{
private:
AccountID issuer_;
jtx::Account issuer_;
public:
explicit issuer(jtx::Account const& issuer) : issuer_(issuer)
{
}
explicit issuer(AccountID const& issuer) : issuer_(issuer)
{
}
void
operator()(Env&, JTx& jtx) const;
};

View File

@@ -301,14 +301,18 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
bool issuerCanRollback = nft::getFlags(nid) & tfStrongTSH;
ADD_TSH(issuer, issuerCanRollback);
for (auto const& offer : {bo, so})
if (bo)
{
if (offer)
{
ADD_TSH(offer->getAccountID(sfOwner), tshSTRONG);
if (offer->isFieldPresent(sfDestination))
ADD_TSH(offer->getAccountID(sfDestination), tshSTRONG);
}
ADD_TSH(bo->getAccountID(sfOwner), tshSTRONG);
if (bo->isFieldPresent(sfDestination))
ADD_TSH(bo->getAccountID(sfDestination), tshSTRONG);
}
if (so)
{
ADD_TSH(so->getAccountID(sfOwner), tshSTRONG);
if (so->isFieldPresent(sfDestination))
ADD_TSH(so->getAccountID(sfDestination), tshSTRONG);
}
break;
@@ -547,8 +551,7 @@ getTransactionalStakeHolders(STTx const& tx, ReadView const& rv)
break;
}
case ttLEDGER_STATE_FIX: {
if (tx.isFieldPresent(sfOwner))
ADD_TSH(tx.getAccountID(sfOwner), tshWEAK);
// TODO: Implement if needed
break;
}
case ttMPTOKEN_ISSUANCE_CREATE:
@@ -2727,23 +2730,26 @@ DEFINE_HOOK_FUNCTION(
return serialize_keylet(kl, memory, write_ptr, write_len);
}
// These keylet types are not yet implemented. Their
// corresponding amendments are not yet supported on the
// network. Each case needs a full implementation (see
// above cases for reference) before its amendment can be
// enabled.
// featureXChainBridge
case keylet_code::BRIDGE:
case keylet_code::XCHAIN_OWNED_CLAIM_ID:
case keylet_code::XCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID:
// featureMPTokensV1
case keylet_code::XCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID: {
if (!applyCtx.view().rules().enabled(featureXChainBridge))
return INVALID_ARGUMENT;
}
case keylet_code::MPTOKEN_ISSUANCE:
case keylet_code::MPTOKEN:
// featureCredentials
case keylet_code::CREDENTIAL:
// featurePermissionedDomains
case keylet_code::PERMISSIONED_DOMAIN:
return INVALID_ARGUMENT;
case keylet_code::MPTOKEN: {
if (!applyCtx.view().rules().enabled(featureMPTokensV1))
return INVALID_ARGUMENT;
}
case keylet_code::CREDENTIAL: {
if (!applyCtx.view().rules().enabled(featureCredentials))
return INVALID_ARGUMENT;
}
case keylet_code::PERMISSIONED_DOMAIN: {
if (!applyCtx.view().rules().enabled(
featurePermissionedDomains))
return INVALID_ARGUMENT;
}
}
}
catch (std::exception& e)

View File

@@ -82,15 +82,8 @@ ClaimReward::preclaim(PreclaimContext const& ctx)
if ((issuer && isOptOut) || (!issuer && !isOptOut))
return temMALFORMED;
if (issuer)
{
auto const sleIssuer = ctx.view.read(keylet::account(*issuer));
if (!sleIssuer)
return tecNO_ISSUER;
if (sleIssuer->isFieldPresent(sfAMMID))
return tecNO_PERMISSION;
}
if (issuer && !ctx.view.exists(keylet::account(*issuer)))
return tecNO_ISSUER;
return tesSUCCESS;
}

View File

@@ -870,17 +870,6 @@ Import::preclaim(PreclaimContext const& ctx)
if (!ctx.tx.isFieldPresent(sfBlob))
return tefINTERNAL;
if (ctx.tx.isFieldPresent(sfIssuer) &&
ctx.view.rules().enabled(fixImportIssuer))
{
auto const sleIssuer = ctx.view.read(keylet::account(ctx.tx[sfIssuer]));
if (!sleIssuer)
return tecNO_ISSUER;
if (sleIssuer->isFieldPresent(sfAMMID))
return tecNO_PERMISSION;
}
// parse blob as json
auto const xpop = syntaxCheckXPOP(ctx.tx.getFieldVL(sfBlob), ctx.j);

View File

@@ -64,13 +64,8 @@ Invoke::preclaim(PreclaimContext const& ctx)
if (ctx.tx.isFieldPresent(sfDestination))
{
auto const sleDest =
ctx.view.read(keylet::account(ctx.tx[sfDestination]));
if (!sleDest)
if (!ctx.view.exists(keylet::account(ctx.tx[sfDestination])))
return tecNO_TARGET;
if (sleDest->isFieldPresent(sfAMMID))
return tecNO_PERMISSION;
}
return tesSUCCESS;

View File

@@ -256,18 +256,11 @@ Remit::doApply()
if (ctx_.tx.isFieldPresent(sfInform))
{
auto const informAcc = ctx_.tx.getAccountID(sfInform);
auto const sleInformAcc = sb.read(keylet::account(informAcc));
if (!sleInformAcc)
if (!sb.exists(keylet::account(informAcc)))
{
JLOG(j.warn()) << "Remit: sfInform account does not exist.";
return tecNO_TARGET;
}
if (sleInformAcc->isFieldPresent(sfAMMID))
{
JLOG(j.warn()) << "Remit: sfInform account is an AMM.";
return tecNO_PERMISSION;
}
}
XRPAmount const accountReserve{sb.fees().accountReserve(0)};