Compare commits

...

2 Commits

Author SHA1 Message Date
Richard Holland
8ffe1ee1a9 ensure default-value amount fields are correctly recorded in metadata for created nodes (fixXahau1) 2023-11-23 11:18:54 +00:00
RichardAH
c917977eb0 wildcard network (id=65535) ignores signature verification (#201)
* wildcard network (id=65535) ignores signature verification

* move isWildcardNetwork deeper to mimick normal signature checking in multisig

* again

* add wildcard test

* Update Transactor.cpp

* clang-format

* fix UNLReport

* move free pass

* update test for multisign

---------

Co-authored-by: Denis Angell <dangell@transia.co>
2023-11-21 18:47:08 +01:00
6 changed files with 258 additions and 18 deletions

View File

@@ -754,6 +754,7 @@ if (tests)
src/test/app/ValidatorList_test.cpp
src/test/app/ValidatorSite_test.cpp
src/test/app/SetHook_test.cpp
src/test/app/Wildcard_test.cpp
src/test/app/XahauGenesis_test.cpp
src/test/app/tx/apply_test.cpp
#[===============================[

View File

@@ -772,6 +772,11 @@ Transactor::checkSign(PreclaimContext const& ctx)
return telNON_LOCAL_EMITTED_TXN;
}
// wildcard network gets a free pass on all signatures
if (ctx.tx.isFieldPresent(sfNetworkID) &&
ctx.tx.getFieldU32(sfNetworkID) == 65535)
return tesSUCCESS;
// pass ttIMPORTs, their signatures are checked at the preflight against the
// internal xpop txn
if (ctx.view.rules().enabled(featureImport) &&

View File

@@ -130,6 +130,9 @@ ApplyStateTable::generateTxMeta(
if (!hookEmission.empty())
meta.setHookEmissions(STArray{hookEmission, sfHookEmissions});
bool const recordDefaultAmounts =
to.rules().enabled(fixXahau1);
Mods newMod;
for (auto& item : items_)
{
@@ -232,8 +235,11 @@ ApplyStateTable::generateTxMeta(
STObject news(sfNewFields);
for (auto const& obj : *curNode)
{
bool const shouldRecord =
(obj.getSType() == STI_AMOUNT && recordDefaultAmounts) || !obj.isDefault();
// save non-default values
if (!obj.isDefault() &&
if (shouldRecord &&
obj.getFName().shouldMeta(
SField::sMD_Create | SField::sMD_Always))
news.emplace_back(obj);

View File

@@ -300,6 +300,10 @@ STTx::checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const
if (isFieldPresent(sfSigners))
return Unexpected("Cannot both single- and multi-sign.");
// wildcard network gets a free pass on all signatures
bool const isWildcardNetwork =
isFieldPresent(sfNetworkID) && getFieldU32(sfNetworkID) == 65535;
bool validSig = false;
try
{
@@ -313,11 +317,11 @@ STTx::checkSingleSign(RequireFullyCanonicalSig requireCanonicalSig) const
Blob const signature = getFieldVL(sfTxnSignature);
Blob const data = getSigningData(*this);
validSig = verify(
PublicKey(makeSlice(spk)),
makeSlice(data),
makeSlice(signature),
fullyCanonical);
validSig = isWildcardNetwork ||
verify(PublicKey(makeSlice(spk)),
makeSlice(data),
makeSlice(signature),
fullyCanonical);
}
}
catch (std::exception const&)
@@ -368,6 +372,9 @@ STTx::checkMultiSign(
// Signers must be in sorted order by AccountID.
AccountID lastAccountID(beast::zero);
bool const isWildcardNetwork =
isFieldPresent(sfNetworkID) && getFieldU32(sfNetworkID) == 65535;
for (auto const& signer : signers)
{
auto const accountID = signer.getAccountID(sfAccount);
@@ -400,11 +407,12 @@ STTx::checkMultiSign(
{
Blob const signature = signer.getFieldVL(sfTxnSignature);
validSig = verify(
PublicKey(makeSlice(spk)),
s.slice(),
makeSlice(signature),
fullyCanonical);
// wildcard network gets a free pass
validSig = isWildcardNetwork ||
verify(PublicKey(makeSlice(spk)),
s.slice(),
makeSlice(signature),
fullyCanonical);
}
}
catch (std::exception const&)

View File

@@ -0,0 +1,121 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2023 XRPL-Labs.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/jss.h>
#include <test/jtx.h>
namespace ripple {
namespace test {
class Wildcard_test : public beast::unit_test::suite
{
std::unique_ptr<Config>
makeNetworkConfig(uint32_t networkID)
{
using namespace jtx;
return envconfig([&](std::unique_ptr<Config> cfg) {
cfg->NETWORK_ID = networkID;
Section config;
config.append(
{"reference_fee = 10",
"account_reserve = 1000000",
"owner_reserve = 200000"});
auto setup = setup_FeeVote(config);
cfg->FEES = setup;
return cfg;
});
}
void
testWildcardSign(FeatureBitset features)
{
using namespace test::jtx;
testcase("wildcard sign");
for (bool const wildcardNetwork : {true, false})
{
auto const network = wildcardNetwork ? 65535 : 21337;
Env env{*this, makeNetworkConfig(network), features};
Account const alice{"alice"};
Account const bob{"bob"};
Account const carol{"carol"};
Account const dave{"dave"};
env.fund(XRP(1000), alice, bob, carol, dave);
env.close();
Json::Value jv;
jv[jss::Account] = alice.human();
jv[jss::Destination] = bob.human();
jv[jss::TransactionType] = "Payment";
jv[jss::Amount] = "1000000";
// lambda that submits an STTx and returns the resulting JSON.
auto submitSTTx = [&env](STTx const& stx) {
Json::Value jvResult;
jvResult[jss::tx_blob] = strHex(stx.getSerializer().slice());
return env.rpc("json", "submit", to_string(jvResult));
};
// Account/RegularKey Sign
{
JTx tx = env.jt(jv, sig(bob));
STTx local = *(tx.stx);
auto const info = submitSTTx(local);
auto const tecResult =
wildcardNetwork ? "tesSUCCESS" : "tefBAD_AUTH";
BEAST_EXPECT(
info[jss::result][jss::engine_result] == tecResult);
}
// Multi Sign
{
env(signers(alice, 1, {{bob, 1}, {carol, 1}}));
env.close();
JTx tx = env.jt(jv, msig(dave), fee(XRP(1)));
STTx local = *(tx.stx);
auto const info = submitSTTx(local);
auto const tecResult =
wildcardNetwork ? "tesSUCCESS" : "tefBAD_SIGNATURE";
BEAST_EXPECT(
info[jss::result][jss::engine_result] == tecResult);
}
}
}
void
testWithFeats(FeatureBitset features)
{
testWildcardSign(features);
}
public:
void
run() override
{
using namespace test::jtx;
auto const sa = supported_amendments();
testWithFeats(sa);
}
};
BEAST_DEFINE_TESTSUITE(Wildcard, app, ripple);
} // namespace test
} // namespace ripple

View File

@@ -31,6 +31,7 @@
#include <ripple/ledger/View.h>
#include <ripple/protocol/Feature.h>
#include <string>
#include <test/csf.h>
#include <test/jtx.h>
#include <vector>
@@ -60,12 +61,12 @@ namespace test {
// * @param hasToReEnable if expect ToDisable in ledger
// * @return true if meet all three expectation
// */
// bool
// negUnlSizeTest(
// std::shared_ptr<Ledger const> const& l,
// size_t size,
// bool hasToDisable,
// bool hasToReEnable);
inline bool
negUnlSizeTest(
std::shared_ptr<Ledger const> const& l,
size_t size,
bool hasToDisable,
bool hasToReEnable);
// /**
// * Try to apply a ttUNL_MODIFY Tx, and test the apply result
@@ -164,6 +165,38 @@ createUNLRTx(
PublicKey const& importKey,
PublicKey const& valKey);
/**
* Count the number of Tx in a TxSet
*
* @param txSet the TxSet
* @return the number of Tx
*/
inline std::size_t
countTx(std::shared_ptr<SHAMap> const& txSet);
/**
* Create ttUNL_MODIFY Tx
*
* @param disabling disabling or re-enabling a validator
* @param seq current ledger seq
* @param txKey the public key of the validator
* @return the ttUNL_MODIFY Tx
*/
inline STTx
createTx(bool disabling, LedgerIndex seq, PublicKey const& txKey);
/**
* Try to apply a ttUNL_MODIFY Tx, and test the apply result
*
* @param env the test environment
* @param view the OpenView of the ledger
* @param tx the ttUNL_MODIFY Tx
* @param pass if the Tx should be applied successfully
* @return true if meet the expectation of apply result
*/
inline bool
applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass);
class UNLReport_test : public beast::unit_test::suite
{
// Import VL Keys
@@ -1201,6 +1234,23 @@ BEAST_DEFINE_TESTSUITE(UNLReportVoteNewValidator, consensus, ripple);
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
inline bool
negUnlSizeTest(
std::shared_ptr<Ledger const> const& l,
size_t size,
bool hasToDisable,
bool hasToReEnable)
{
bool sameSize = l->negativeUNL().size() == size;
bool sameToDisable =
(l->validatorToDisable() != std::nullopt) == hasToDisable;
bool sameToReEnable =
(l->validatorToReEnable() != std::nullopt) == hasToReEnable;
return sameSize && sameToDisable && sameToReEnable;
}
bool
applyAndTestUNLRResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
{
@@ -1362,5 +1412,54 @@ createUNLRTx(
return STTx(ttUNL_REPORT, fill);
}
inline STTx
createTx(bool disabling, LedgerIndex seq, PublicKey const& txKey)
{
auto fill = [&](auto& obj) {
obj.setFieldU8(sfUNLModifyDisabling, disabling ? 1 : 0);
obj.setFieldU32(sfLedgerSequence, seq);
obj.setFieldVL(sfUNLModifyValidator, txKey);
};
return STTx(ttUNL_MODIFY, fill);
}
inline std::size_t
countTx(std::shared_ptr<SHAMap> const& txSet)
{
/*uint64_t counter = 0;
if (txSet)
for (auto const& item : *txSet)
{
SerialIter sit(item.slice());
auto tx = std::make_shared<STTx
const>(SerialIter{sit.getSlice(sit.getVLDataLength())});
if (tx->getFieldU16(sfTransactionType) == ttUNL_MODIFY)
counter++;
}
*/
std::size_t count = 0;
for (auto i = txSet->begin(); i != txSet->end(); ++i)
{
// RH TODO: why does the above parse??
auto raw = i->slice();
if (raw[0] == 0x12U && raw[1] == 0 && raw[2] == 0x66U)
count++;
}
return count;
};
inline bool
applyAndTestResult(jtx::Env& env, OpenView& view, STTx const& tx, bool pass)
{
auto res = apply(env.app(), view, tx, ApplyFlags::tapNONE, env.journal);
if (pass)
return res.first == tesSUCCESS;
else
return res.first == tefFAILURE || res.first == temDISABLED;
}
} // namespace test
} // namespace ripple
} // namespace ripple