mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-01 07:55:49 +00:00
Add NetworkID field to Transaction common fields, enforced when network id > 1024
This commit is contained in:
@@ -707,6 +707,7 @@ if (tests)
|
||||
src/test/app/LoadFeeTrack_test.cpp
|
||||
src/test/app/Manifest_test.cpp
|
||||
src/test/app/MultiSign_test.cpp
|
||||
src/test/app/NetworkID_test.cpp
|
||||
src/test/app/NFToken_test.cpp
|
||||
src/test/app/NFTokenBurn_test.cpp
|
||||
src/test/app/NFTokenDir_test.cpp
|
||||
|
||||
@@ -46,6 +46,27 @@ namespace ripple {
|
||||
NotTEC
|
||||
preflight0(PreflightContext const& ctx)
|
||||
{
|
||||
uint32_t nodeNID = ctx.app.config().NETWORK_ID;
|
||||
std::optional<uint32_t> txNID = ctx.tx[~sfNetworkID];
|
||||
|
||||
if (nodeNID <= 1024)
|
||||
{
|
||||
// legacy networks have ids less than 1024, these networks cannot
|
||||
// specify NetworkID in txn
|
||||
if (txNID)
|
||||
return telNETWORK_ID_MAKES_TX_NON_CANONICAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// new networks both require the field to be present and require it to
|
||||
// match
|
||||
if (!txNID)
|
||||
return telREQUIRES_NETWORK_ID;
|
||||
|
||||
if (*txNID != nodeNID)
|
||||
return telWRONG_NETWORK;
|
||||
}
|
||||
|
||||
auto const txID = ctx.tx.getTransactionID();
|
||||
|
||||
if (txID == beast::zero)
|
||||
|
||||
@@ -138,6 +138,7 @@ public:
|
||||
std::string START_LEDGER;
|
||||
|
||||
// Network parameters
|
||||
uint32_t NETWORK_ID = 0;
|
||||
|
||||
// The number of fee units a reference transaction costs
|
||||
static constexpr FeeUnit32 TRANSACTION_FEE_BASE{10};
|
||||
|
||||
@@ -102,6 +102,7 @@ struct ConfigSection
|
||||
#define SECTION_BETA_RPC_API "beta_rpc_api"
|
||||
#define SECTION_SWEEP_INTERVAL "sweep_interval"
|
||||
#define SECTION_XPOP_HISTORY "xpop_history"
|
||||
#define SECTION_NETWORK_ID "network_id"
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
|
||||
@@ -483,6 +483,18 @@ Config::loadFromString(std::string const& fileContents)
|
||||
|
||||
if (getSingleSection(secConfig, SECTION_XPOP_HISTORY, strTemp, j_))
|
||||
XPOP_HISTORY = beast::lexicalCastThrow<bool>(strTemp);
|
||||
|
||||
if (getSingleSection(secConfig, SECTION_NETWORK_ID, strTemp, j_))
|
||||
{
|
||||
if (strTemp == "main")
|
||||
NETWORK_ID = 0;
|
||||
else if (strTemp == "testnet")
|
||||
NETWORK_ID = 1;
|
||||
else if (strTemp == "devnet")
|
||||
NETWORK_ID = 2;
|
||||
else
|
||||
NETWORK_ID = beast::lexicalCastThrow<uint32_t>(strTemp);
|
||||
}
|
||||
|
||||
if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
|
||||
PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
|
||||
|
||||
@@ -354,6 +354,7 @@ extern SF_UINT16 const sfHookExecutionIndex;
|
||||
extern SF_UINT16 const sfHookApiVersion;
|
||||
|
||||
// 32-bit integers (common)
|
||||
extern SF_UINT32 const sfNetworkID;
|
||||
extern SF_UINT32 const sfFlags;
|
||||
extern SF_UINT32 const sfSourceTag;
|
||||
extern SF_UINT32 const sfSequence;
|
||||
|
||||
@@ -61,6 +61,9 @@ enum TELcodes : TERUnderlyingType {
|
||||
telCAN_NOT_QUEUE_BLOCKED,
|
||||
telCAN_NOT_QUEUE_FEE,
|
||||
telCAN_NOT_QUEUE_FULL,
|
||||
telWRONG_NETWORK,
|
||||
telREQUIRES_NETWORK_ID,
|
||||
telNETWORK_ID_MAKES_TX_NON_CANONICAL
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -104,6 +104,7 @@ CONSTRUCT_TYPED_SFIELD(sfHookExecutionIndex, "HookExecutionIndex", UINT16,
|
||||
CONSTRUCT_TYPED_SFIELD(sfHookApiVersion, "HookApiVersion", UINT16, 20);
|
||||
|
||||
// 32-bit integers (common)
|
||||
CONSTRUCT_TYPED_SFIELD(sfNetworkID, "NetworkID", UINT32, 1);
|
||||
CONSTRUCT_TYPED_SFIELD(sfFlags, "Flags", UINT32, 2);
|
||||
CONSTRUCT_TYPED_SFIELD(sfSourceTag, "SourceTag", UINT32, 3);
|
||||
CONSTRUCT_TYPED_SFIELD(sfSequence, "Sequence", UINT32, 4);
|
||||
|
||||
@@ -126,6 +126,9 @@ transResults()
|
||||
MAKE_ERROR(telCAN_NOT_QUEUE_BLOCKED, "Can not queue at this time: blocking transaction in queue."),
|
||||
MAKE_ERROR(telCAN_NOT_QUEUE_FEE, "Can not queue at this time: fee insufficient to replace queued transaction."),
|
||||
MAKE_ERROR(telCAN_NOT_QUEUE_FULL, "Can not queue at this time: queue is full."),
|
||||
MAKE_ERROR(telWRONG_NETWORK, "Transaction specifies a Network ID that differs from that of the local node."),
|
||||
MAKE_ERROR(telREQUIRES_NETWORK_ID, "Transactions submitted to this node/network must include a correct NetworkID field."),
|
||||
MAKE_ERROR(telNETWORK_ID_MAKES_TX_NON_CANONICAL, "Transactions submitted to this node/network must NOT include a NetworkID field."),
|
||||
|
||||
MAKE_ERROR(temMALFORMED, "Malformed transaction."),
|
||||
MAKE_ERROR(temBAD_AMOUNT, "Can only send positive amounts."),
|
||||
|
||||
@@ -42,6 +42,7 @@ TxFormats::TxFormats()
|
||||
{sfSigners, soeOPTIONAL}, // submit_multisigned
|
||||
{sfEmitDetails, soeOPTIONAL},
|
||||
{sfFirstLedgerSequence, soeOPTIONAL},
|
||||
{sfNetworkID, soeOPTIONAL},
|
||||
};
|
||||
|
||||
add(jss::AccountSet,
|
||||
|
||||
@@ -87,6 +87,7 @@ JSS(Invoke); // transaction type
|
||||
JSS(LastLedgerSequence); // in: TransactionSign; field
|
||||
JSS(LedgerHashes); // ledger type.
|
||||
JSS(LimitAmount); // field.
|
||||
JSS(NetworkID); // field.
|
||||
JSS(NFTokenBurn); // transaction type.
|
||||
JSS(NFTokenMint); // transaction type.
|
||||
JSS(NFTokenOffer); // ledger type.
|
||||
|
||||
153
src/test/app/NetworkID_test.cpp
Normal file
153
src/test/app/NetworkID_test.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2020 Dev Null Productions
|
||||
|
||||
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/basics/BasicConfig.h>
|
||||
#include <ripple/core/ConfigSections.h>
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/Env.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
class NetworkID_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testNetworkID();
|
||||
}
|
||||
|
||||
std::unique_ptr<Config>
|
||||
makeNetworkConfig(uint32_t networkID)
|
||||
{
|
||||
using namespace jtx;
|
||||
return envconfig([&](std::unique_ptr<Config> cfg) {
|
||||
cfg->NETWORK_ID = networkID;
|
||||
return cfg;
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
testNetworkID()
|
||||
{
|
||||
testcase(
|
||||
"Require txn NetworkID to be specified (or not) depending on the "
|
||||
"network ID of the node");
|
||||
using namespace jtx;
|
||||
|
||||
auto const alice = Account{"alice"};
|
||||
|
||||
auto const runTx = [&](test::jtx::Env& env,
|
||||
Json::Value const& jv,
|
||||
TER expectedOutcome) {
|
||||
env.memoize(env.master);
|
||||
env.memoize(alice);
|
||||
|
||||
// fund alice
|
||||
{
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = env.master.human();
|
||||
jv[jss::Destination] = alice.human();
|
||||
jv[jss::TransactionType] = "Payment";
|
||||
jv[jss::Amount] = "10000000000";
|
||||
if (env.app().config().NETWORK_ID > 1024)
|
||||
jv[jss::NetworkID] =
|
||||
std::to_string(env.app().config().NETWORK_ID);
|
||||
|
||||
env(jv, fee(1000), sig(env.master));
|
||||
}
|
||||
|
||||
// run tx
|
||||
env(jv, fee(1000), ter(expectedOutcome));
|
||||
env.close();
|
||||
};
|
||||
|
||||
// test mainnet
|
||||
{
|
||||
test::jtx::Env env{*this, makeNetworkConfig(0)};
|
||||
BEAST_EXPECT(env.app().config().NETWORK_ID == 0);
|
||||
|
||||
// try to submit a txn without network id, this should work
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = alice.human();
|
||||
jv[jss::TransactionType] = jss::AccountSet;
|
||||
runTx(env, jv, tesSUCCESS);
|
||||
|
||||
// try to submit a txn with NetworkID present against a mainnet
|
||||
// node, this will fail
|
||||
jv[jss::NetworkID] = 0;
|
||||
runTx(env, jv, telNETWORK_ID_MAKES_TX_NON_CANONICAL);
|
||||
|
||||
// change network id to something else, should still return same
|
||||
// error
|
||||
jv[jss::NetworkID] = 10000;
|
||||
runTx(env, jv, telNETWORK_ID_MAKES_TX_NON_CANONICAL);
|
||||
}
|
||||
|
||||
// any network up to and including networkid 1024 cannot support
|
||||
// NetworkID
|
||||
{
|
||||
test::jtx::Env env{*this, makeNetworkConfig(1024)};
|
||||
BEAST_EXPECT(env.app().config().NETWORK_ID == 1024);
|
||||
|
||||
// try to submit a txn without network id, this should work
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = alice.human();
|
||||
jv[jss::TransactionType] = jss::AccountSet;
|
||||
runTx(env, jv, tesSUCCESS);
|
||||
|
||||
// now submit with a network id, this will fail
|
||||
jv[jss::NetworkID] = 1024;
|
||||
runTx(env, jv, telNETWORK_ID_MAKES_TX_NON_CANONICAL);
|
||||
|
||||
jv[jss::NetworkID] = 1000;
|
||||
runTx(env, jv, telNETWORK_ID_MAKES_TX_NON_CANONICAL);
|
||||
}
|
||||
|
||||
// any network above networkid 1024 will produce an error if fed a txn
|
||||
// absent networkid
|
||||
{
|
||||
test::jtx::Env env{*this, makeNetworkConfig(1025)};
|
||||
BEAST_EXPECT(env.app().config().NETWORK_ID == 1025);
|
||||
|
||||
// try to submit a txn without network id, this should not work
|
||||
Json::Value jv;
|
||||
jv[jss::Account] = alice.human();
|
||||
jv[jss::TransactionType] = jss::AccountSet;
|
||||
runTx(env, jv, telREQUIRES_NETWORK_ID);
|
||||
|
||||
// try to submit with wrong network id
|
||||
jv[jss::NetworkID] = 0;
|
||||
runTx(env, jv, telWRONG_NETWORK);
|
||||
|
||||
jv[jss::NetworkID] = 1024;
|
||||
runTx(env, jv, telWRONG_NETWORK);
|
||||
|
||||
// submit the correct network id
|
||||
jv[jss::NetworkID] = 1025;
|
||||
runTx(env, jv, tesSUCCESS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(NetworkID, app, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -411,6 +411,71 @@ port_wss_admin
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testNetworkID()
|
||||
{
|
||||
testcase("network id");
|
||||
std::string error;
|
||||
Config c;
|
||||
try
|
||||
{
|
||||
c.loadFromString(R"rippleConfig(
|
||||
[network_id]
|
||||
main
|
||||
)rippleConfig");
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
error = e.what();
|
||||
}
|
||||
|
||||
BEAST_EXPECT(error == "");
|
||||
BEAST_EXPECT(c.NETWORK_ID == 0);
|
||||
|
||||
try
|
||||
{
|
||||
c.loadFromString(R"rippleConfig(
|
||||
)rippleConfig");
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
error = e.what();
|
||||
}
|
||||
|
||||
BEAST_EXPECT(error == "");
|
||||
BEAST_EXPECT(c.NETWORK_ID == 0);
|
||||
|
||||
try
|
||||
{
|
||||
c.loadFromString(R"rippleConfig(
|
||||
[network_id]
|
||||
255
|
||||
)rippleConfig");
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
error = e.what();
|
||||
}
|
||||
|
||||
BEAST_EXPECT(error == "");
|
||||
BEAST_EXPECT(c.NETWORK_ID == 255);
|
||||
|
||||
try
|
||||
{
|
||||
c.loadFromString(R"rippleConfig(
|
||||
[network_id]
|
||||
10000
|
||||
)rippleConfig");
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
error = e.what();
|
||||
}
|
||||
|
||||
BEAST_EXPECT(error == "");
|
||||
BEAST_EXPECT(c.NETWORK_ID == 10000);
|
||||
}
|
||||
|
||||
void
|
||||
testValidatorsFile()
|
||||
{
|
||||
@@ -1151,6 +1216,7 @@ r.ripple.com 51235
|
||||
testGetters();
|
||||
testAmendment();
|
||||
testOverlay();
|
||||
testNetworkID();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -163,7 +163,10 @@ Env::lookup(AccountID const& id) const
|
||||
{
|
||||
auto const iter = map_.find(id);
|
||||
if (iter == map_.end())
|
||||
{
|
||||
std::cout << "Unknown account: " << id << "\n";
|
||||
Throw<std::runtime_error>("Env::lookup:: unknown account ID");
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user