mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -05:00
scripted-diff: Rename InitInterfaces to NodeContext
-BEGIN VERIFY SCRIPT- s() { git grep -l "$1" src | xargs sed -i "s/$1/$2/g"; } s 'struct InitInterfaces' 'struct NodeContext' s 'InitInterfaces interfaces' 'NodeContext node' s 'InitInterfaces& interfaces' 'NodeContext\& node' s 'InitInterfaces m_interfaces' 'NodeContext m_context' s 'InitInterfaces\* g_rpc_interfaces' 'NodeContext* g_rpc_node' s 'g_rpc_interfaces = &interfaces' 'g_rpc_node = \&node' s 'g_rpc_interfaces' 'g_rpc_node' s 'm_interfaces' 'm_context' s 'interfaces\.chain' 'node.chain' s '\(AppInitMain\|Shutdown\|Construct\)(interfaces)' '\1(node)' s 'init interfaces' 'chain clients' -END VERIFY SCRIPT-
This commit is contained in:
parent
cfec3e01b4
commit
301bd41a2e
11 changed files with 41 additions and 41 deletions
|
@ -39,8 +39,8 @@ static void WaitForShutdown()
|
||||||
//
|
//
|
||||||
static bool AppInit(int argc, char* argv[])
|
static bool AppInit(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
InitInterfaces interfaces;
|
NodeContext node;
|
||||||
interfaces.chain = interfaces::MakeChain();
|
node.chain = interfaces::MakeChain();
|
||||||
|
|
||||||
bool fRet = false;
|
bool fRet = false;
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ static bool AppInit(int argc, char* argv[])
|
||||||
// If locking the data directory failed, exit immediately
|
// If locking the data directory failed, exit immediately
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
fRet = AppInitMain(interfaces);
|
fRet = AppInitMain(node);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e) {
|
catch (const std::exception& e) {
|
||||||
PrintExceptionContinue(&e, "AppInit()");
|
PrintExceptionContinue(&e, "AppInit()");
|
||||||
|
@ -156,7 +156,7 @@ static bool AppInit(int argc, char* argv[])
|
||||||
} else {
|
} else {
|
||||||
WaitForShutdown();
|
WaitForShutdown();
|
||||||
}
|
}
|
||||||
Shutdown(interfaces);
|
Shutdown(node);
|
||||||
|
|
||||||
return fRet;
|
return fRet;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ public:
|
||||||
bool HasWalletSupport() const override {return false;}
|
bool HasWalletSupport() const override {return false;}
|
||||||
void AddWalletOptions() const override;
|
void AddWalletOptions() const override;
|
||||||
bool ParameterInteraction() const override {return true;}
|
bool ParameterInteraction() const override {return true;}
|
||||||
void Construct(InitInterfaces& interfaces) const override {LogPrintf("No wallet support compiled in!\n");}
|
void Construct(NodeContext& node) const override {LogPrintf("No wallet support compiled in!\n");}
|
||||||
};
|
};
|
||||||
|
|
||||||
void DummyWalletInit::AddWalletOptions() const
|
void DummyWalletInit::AddWalletOptions() const
|
||||||
|
|
22
src/init.cpp
22
src/init.cpp
|
@ -170,7 +170,7 @@ void Interrupt()
|
||||||
ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Interrupt(); });
|
ForEachBlockFilterIndex([](BlockFilterIndex& index) { index.Interrupt(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown(InitInterfaces& interfaces)
|
void Shutdown(NodeContext& node)
|
||||||
{
|
{
|
||||||
LogPrintf("%s: In progress...\n", __func__);
|
LogPrintf("%s: In progress...\n", __func__);
|
||||||
static CCriticalSection cs_Shutdown;
|
static CCriticalSection cs_Shutdown;
|
||||||
|
@ -189,7 +189,7 @@ void Shutdown(InitInterfaces& interfaces)
|
||||||
StopREST();
|
StopREST();
|
||||||
StopRPC();
|
StopRPC();
|
||||||
StopHTTPServer();
|
StopHTTPServer();
|
||||||
for (const auto& client : interfaces.chain_clients) {
|
for (const auto& client : node.chain_clients) {
|
||||||
client->flush();
|
client->flush();
|
||||||
}
|
}
|
||||||
StopMapPort();
|
StopMapPort();
|
||||||
|
@ -261,7 +261,7 @@ void Shutdown(InitInterfaces& interfaces)
|
||||||
}
|
}
|
||||||
pblocktree.reset();
|
pblocktree.reset();
|
||||||
}
|
}
|
||||||
for (const auto& client : interfaces.chain_clients) {
|
for (const auto& client : node.chain_clients) {
|
||||||
client->stop();
|
client->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ void Shutdown(InitInterfaces& interfaces)
|
||||||
} catch (const fs::filesystem_error& e) {
|
} catch (const fs::filesystem_error& e) {
|
||||||
LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
|
LogPrintf("%s: Unable to remove PID file: %s\n", __func__, fsbridge::get_filesystem_error_message(e));
|
||||||
}
|
}
|
||||||
interfaces.chain_clients.clear();
|
node.chain_clients.clear();
|
||||||
UnregisterAllValidationInterfaces();
|
UnregisterAllValidationInterfaces();
|
||||||
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
||||||
GetMainSignals().UnregisterWithMempoolSignals(mempool);
|
GetMainSignals().UnregisterWithMempoolSignals(mempool);
|
||||||
|
@ -1207,7 +1207,7 @@ bool AppInitLockDataDirectory()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AppInitMain(InitInterfaces& interfaces)
|
bool AppInitMain(NodeContext& node)
|
||||||
{
|
{
|
||||||
const CChainParams& chainparams = Params();
|
const CChainParams& chainparams = Params();
|
||||||
// ********************************************************* Step 4a: application initialization
|
// ********************************************************* Step 4a: application initialization
|
||||||
|
@ -1275,16 +1275,16 @@ bool AppInitMain(InitInterfaces& interfaces)
|
||||||
// according to -wallet and -disablewallet options. This only constructs
|
// according to -wallet and -disablewallet options. This only constructs
|
||||||
// the interfaces, it doesn't load wallet data. Wallets actually get loaded
|
// the interfaces, it doesn't load wallet data. Wallets actually get loaded
|
||||||
// when load() and start() interface methods are called below.
|
// when load() and start() interface methods are called below.
|
||||||
g_wallet_init_interface.Construct(interfaces);
|
g_wallet_init_interface.Construct(node);
|
||||||
|
|
||||||
/* Register RPC commands regardless of -server setting so they will be
|
/* Register RPC commands regardless of -server setting so they will be
|
||||||
* available in the GUI RPC console even if external calls are disabled.
|
* available in the GUI RPC console even if external calls are disabled.
|
||||||
*/
|
*/
|
||||||
RegisterAllCoreRPCCommands(tableRPC);
|
RegisterAllCoreRPCCommands(tableRPC);
|
||||||
for (const auto& client : interfaces.chain_clients) {
|
for (const auto& client : node.chain_clients) {
|
||||||
client->registerRpcs();
|
client->registerRpcs();
|
||||||
}
|
}
|
||||||
g_rpc_interfaces = &interfaces;
|
g_rpc_node = &node;
|
||||||
#if ENABLE_ZMQ
|
#if ENABLE_ZMQ
|
||||||
RegisterZMQRPCCommands(tableRPC);
|
RegisterZMQRPCCommands(tableRPC);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1302,7 +1302,7 @@ bool AppInitMain(InitInterfaces& interfaces)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ********************************************************* Step 5: verify wallet database integrity
|
// ********************************************************* Step 5: verify wallet database integrity
|
||||||
for (const auto& client : interfaces.chain_clients) {
|
for (const auto& client : node.chain_clients) {
|
||||||
if (!client->verify()) {
|
if (!client->verify()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1661,7 +1661,7 @@ bool AppInitMain(InitInterfaces& interfaces)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ********************************************************* Step 9: load wallet
|
// ********************************************************* Step 9: load wallet
|
||||||
for (const auto& client : interfaces.chain_clients) {
|
for (const auto& client : node.chain_clients) {
|
||||||
if (!client->load()) {
|
if (!client->load()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1815,7 +1815,7 @@ bool AppInitMain(InitInterfaces& interfaces)
|
||||||
SetRPCWarmupFinished();
|
SetRPCWarmupFinished();
|
||||||
uiInterface.InitMessage(_("Done loading").translated);
|
uiInterface.InitMessage(_("Done loading").translated);
|
||||||
|
|
||||||
for (const auto& client : interfaces.chain_clients) {
|
for (const auto& client : node.chain_clients) {
|
||||||
client->start(scheduler);
|
client->start(scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ class ChainClient;
|
||||||
} // namespace interfaces
|
} // namespace interfaces
|
||||||
|
|
||||||
//! Pointers to interfaces used during init and destroyed on shutdown.
|
//! Pointers to interfaces used during init and destroyed on shutdown.
|
||||||
struct InitInterfaces
|
struct NodeContext
|
||||||
{
|
{
|
||||||
std::unique_ptr<interfaces::Chain> chain;
|
std::unique_ptr<interfaces::Chain> chain;
|
||||||
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
|
std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients;
|
||||||
|
@ -29,7 +29,7 @@ class thread_group;
|
||||||
|
|
||||||
/** Interrupt threads */
|
/** Interrupt threads */
|
||||||
void Interrupt();
|
void Interrupt();
|
||||||
void Shutdown(InitInterfaces& interfaces);
|
void Shutdown(NodeContext& node);
|
||||||
//!Initialize the logging infrastructure
|
//!Initialize the logging infrastructure
|
||||||
void InitLogging();
|
void InitLogging();
|
||||||
//!Parameter interaction: change current parameters depending on various rules
|
//!Parameter interaction: change current parameters depending on various rules
|
||||||
|
@ -63,7 +63,7 @@ bool AppInitLockDataDirectory();
|
||||||
* @note This should only be done after daemonization. Call Shutdown() if this function fails.
|
* @note This should only be done after daemonization. Call Shutdown() if this function fails.
|
||||||
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
|
* @pre Parameters should be parsed and config file should be read, AppInitLockDataDirectory should have been called.
|
||||||
*/
|
*/
|
||||||
bool AppInitMain(InitInterfaces& interfaces);
|
bool AppInitMain(NodeContext& node);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setup the arguments for gArgs
|
* Setup the arguments for gArgs
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace {
|
||||||
class NodeImpl : public Node
|
class NodeImpl : public Node
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NodeImpl() { m_interfaces.chain = MakeChain(); }
|
NodeImpl() { m_context.chain = MakeChain(); }
|
||||||
void initError(const std::string& message) override { InitError(message); }
|
void initError(const std::string& message) override { InitError(message); }
|
||||||
bool parseParameters(int argc, const char* const argv[], std::string& error) override
|
bool parseParameters(int argc, const char* const argv[], std::string& error) override
|
||||||
{
|
{
|
||||||
|
@ -75,11 +75,11 @@ public:
|
||||||
return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
|
return AppInitBasicSetup() && AppInitParameterInteraction() && AppInitSanityChecks() &&
|
||||||
AppInitLockDataDirectory();
|
AppInitLockDataDirectory();
|
||||||
}
|
}
|
||||||
bool appInitMain() override { return AppInitMain(m_interfaces); }
|
bool appInitMain() override { return AppInitMain(m_context); }
|
||||||
void appShutdown() override
|
void appShutdown() override
|
||||||
{
|
{
|
||||||
Interrupt();
|
Interrupt();
|
||||||
Shutdown(m_interfaces);
|
Shutdown(m_context);
|
||||||
}
|
}
|
||||||
void startShutdown() override { StartShutdown(); }
|
void startShutdown() override { StartShutdown(); }
|
||||||
bool shutdownRequested() override { return ShutdownRequested(); }
|
bool shutdownRequested() override { return ShutdownRequested(); }
|
||||||
|
@ -255,12 +255,12 @@ public:
|
||||||
}
|
}
|
||||||
std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::vector<std::string>& warnings) override
|
std::unique_ptr<Wallet> loadWallet(const std::string& name, std::string& error, std::vector<std::string>& warnings) override
|
||||||
{
|
{
|
||||||
return MakeWallet(LoadWallet(*m_interfaces.chain, name, error, warnings));
|
return MakeWallet(LoadWallet(*m_context.chain, name, error, warnings));
|
||||||
}
|
}
|
||||||
WalletCreationStatus createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::unique_ptr<Wallet>& result) override
|
WalletCreationStatus createWallet(const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::unique_ptr<Wallet>& result) override
|
||||||
{
|
{
|
||||||
std::shared_ptr<CWallet> wallet;
|
std::shared_ptr<CWallet> wallet;
|
||||||
WalletCreationStatus status = CreateWallet(*m_interfaces.chain, passphrase, wallet_creation_flags, name, error, warnings, wallet);
|
WalletCreationStatus status = CreateWallet(*m_context.chain, passphrase, wallet_creation_flags, name, error, warnings, wallet);
|
||||||
result = MakeWallet(wallet);
|
result = MakeWallet(wallet);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -315,7 +315,7 @@ public:
|
||||||
/* verification progress is unused when a header was received */ 0);
|
/* verification progress is unused when a header was received */ 0);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
InitInterfaces m_interfaces;
|
NodeContext m_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
InitInterfaces* g_rpc_interfaces = nullptr;
|
NodeContext* g_rpc_node = nullptr;
|
||||||
|
|
||||||
void RPCTypeCheck(const UniValue& params,
|
void RPCTypeCheck(const UniValue& params,
|
||||||
const std::list<UniValueType>& typesExpected,
|
const std::list<UniValueType>& typesExpected,
|
||||||
|
|
|
@ -25,12 +25,12 @@
|
||||||
class FillableSigningProvider;
|
class FillableSigningProvider;
|
||||||
class CPubKey;
|
class CPubKey;
|
||||||
class CScript;
|
class CScript;
|
||||||
struct InitInterfaces;
|
struct NodeContext;
|
||||||
|
|
||||||
//! Pointers to interfaces that need to be accessible from RPC methods. Due to
|
//! Pointers to interfaces that need to be accessible from RPC methods. Due to
|
||||||
//! limitations of the RPC framework, there's currently no direct way to pass in
|
//! limitations of the RPC framework, there's currently no direct way to pass in
|
||||||
//! state to RPC method implementations.
|
//! state to RPC method implementations.
|
||||||
extern InitInterfaces* g_rpc_interfaces;
|
extern NodeContext* g_rpc_node;
|
||||||
|
|
||||||
/** Wrapper for UniValue::VType, which includes typeAny:
|
/** Wrapper for UniValue::VType, which includes typeAny:
|
||||||
* Used to denote don't care type. */
|
* Used to denote don't care type. */
|
||||||
|
|
|
@ -112,14 +112,14 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign)
|
||||||
std::string notsigned = r.get_str();
|
std::string notsigned = r.get_str();
|
||||||
std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\"";
|
std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\"";
|
||||||
std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\"";
|
std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\"";
|
||||||
InitInterfaces interfaces;
|
NodeContext node;
|
||||||
interfaces.chain = interfaces::MakeChain();
|
node.chain = interfaces::MakeChain();
|
||||||
g_rpc_interfaces = &interfaces;
|
g_rpc_node = &node;
|
||||||
r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout);
|
r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout);
|
||||||
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false);
|
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false);
|
||||||
r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout);
|
r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout);
|
||||||
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true);
|
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true);
|
||||||
g_rpc_interfaces = nullptr;
|
g_rpc_node = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(rpc_createraw_op_return)
|
BOOST_AUTO_TEST_CASE(rpc_createraw_op_return)
|
||||||
|
|
|
@ -25,8 +25,8 @@ public:
|
||||||
//! Wallets parameter interaction
|
//! Wallets parameter interaction
|
||||||
bool ParameterInteraction() const override;
|
bool ParameterInteraction() const override;
|
||||||
|
|
||||||
//! Add wallets that should be opened to list of init interfaces.
|
//! Add wallets that should be opened to list of chain clients.
|
||||||
void Construct(InitInterfaces& interfaces) const override;
|
void Construct(NodeContext& node) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
const WalletInitInterface& g_wallet_init_interface = WalletInit();
|
const WalletInitInterface& g_wallet_init_interface = WalletInit();
|
||||||
|
@ -125,12 +125,12 @@ bool WalletInit::ParameterInteraction() const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WalletInit::Construct(InitInterfaces& interfaces) const
|
void WalletInit::Construct(NodeContext& node) const
|
||||||
{
|
{
|
||||||
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
|
||||||
LogPrintf("Wallet disabled!\n");
|
LogPrintf("Wallet disabled!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gArgs.SoftSetArg("-wallet", "");
|
gArgs.SoftSetArg("-wallet", "");
|
||||||
interfaces.chain_clients.emplace_back(interfaces::MakeWalletClient(*interfaces.chain, gArgs.GetArgs("-wallet")));
|
node.chain_clients.emplace_back(interfaces::MakeWalletClient(*node.chain, gArgs.GetArgs("-wallet")));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2574,7 +2574,7 @@ static UniValue loadwallet(const JSONRPCRequest& request)
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
std::vector<std::string> warning;
|
std::vector<std::string> warning;
|
||||||
std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_interfaces->chain, location, error, warning);
|
std::shared_ptr<CWallet> const wallet = LoadWallet(*g_rpc_node->chain, location, error, warning);
|
||||||
if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error);
|
if (!wallet) throw JSONRPCError(RPC_WALLET_ERROR, error);
|
||||||
|
|
||||||
UniValue obj(UniValue::VOBJ);
|
UniValue obj(UniValue::VOBJ);
|
||||||
|
@ -2700,7 +2700,7 @@ static UniValue createwallet(const JSONRPCRequest& request)
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
std::shared_ptr<CWallet> wallet;
|
std::shared_ptr<CWallet> wallet;
|
||||||
WalletCreationStatus status = CreateWallet(*g_rpc_interfaces->chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
|
WalletCreationStatus status = CreateWallet(*g_rpc_node->chain, passphrase, flags, request.params[0].get_str(), error, warnings, wallet);
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case WalletCreationStatus::CREATION_FAILED:
|
case WalletCreationStatus::CREATION_FAILED:
|
||||||
throw JSONRPCError(RPC_WALLET_ERROR, error);
|
throw JSONRPCError(RPC_WALLET_ERROR, error);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#ifndef BITCOIN_WALLETINITINTERFACE_H
|
#ifndef BITCOIN_WALLETINITINTERFACE_H
|
||||||
#define BITCOIN_WALLETINITINTERFACE_H
|
#define BITCOIN_WALLETINITINTERFACE_H
|
||||||
|
|
||||||
struct InitInterfaces;
|
struct NodeContext;
|
||||||
|
|
||||||
class WalletInitInterface {
|
class WalletInitInterface {
|
||||||
public:
|
public:
|
||||||
|
@ -15,8 +15,8 @@ public:
|
||||||
virtual void AddWalletOptions() const = 0;
|
virtual void AddWalletOptions() const = 0;
|
||||||
/** Check wallet parameter interaction */
|
/** Check wallet parameter interaction */
|
||||||
virtual bool ParameterInteraction() const = 0;
|
virtual bool ParameterInteraction() const = 0;
|
||||||
/** Add wallets that should be opened to list of init interfaces. */
|
/** Add wallets that should be opened to list of chain clients. */
|
||||||
virtual void Construct(InitInterfaces& interfaces) const = 0;
|
virtual void Construct(NodeContext& node) const = 0;
|
||||||
|
|
||||||
virtual ~WalletInitInterface() {}
|
virtual ~WalletInitInterface() {}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue