Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions wallet/api/v6_0/v6_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace beam::wallet
, _wallet(init.wallet)
, _swaps(init.swaps)
, _contracts(init.contracts)
, _nodeNetwork(init.nodeNetwork)
#ifdef BEAM_IPFS_SUPPORT
, _ipfs(init.ipfs)
#endif
Expand Down Expand Up @@ -113,6 +114,15 @@ namespace beam::wallet
return _contracts;
}

void V6Api::checkNodeConnection() const
{
// nodeNetwork is not provided by every API host, in that case we cannot tell
if (_nodeNetwork && _nodeNetwork->getConnections() == 0)
{
throw jsonrpc_exception(ApiError::ContractError, "No connection to the node");
}
}

#ifdef BEAM_IPFS_SUPPORT
IPFSService::Ptr V6Api::getIPFS() const
{
Expand Down
5 changes: 5 additions & 0 deletions wallet/api/v6_0/v6_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ namespace beam::wallet
virtual void fillCoins(json& arr, const std::vector<ApiCoin>& coins);
virtual void fillTransactions(json& arr, const std::vector<Status::Response>& txs);

// Throws if the wallet has no connection to a node. Shader calls need to query
// the node and would otherwise wait forever, blocking all subsequent shader calls
void checkNodeConnection() const;

private:
void FillAddressData(const AddressData& data, WalletAddress& address);
void doTxAlreadyExistsError(const JsonRpcId& id);
Expand Down Expand Up @@ -82,6 +86,7 @@ namespace beam::wallet
Wallet::Ptr _wallet;
ISwapsProvider::Ptr _swaps;
IShadersManager::Ptr _contracts;
NodeNetwork::Ptr _nodeNetwork;

#ifdef BEAM_IPFS_SUPPORT
IPFSService::Ptr _ipfs;
Expand Down
2 changes: 2 additions & 0 deletions wallet/api/v6_0/v6_api_handle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,8 @@ namespace beam::wallet
throw jsonrpc_exception(ApiError::UnexpectedError, "Previous shader call is still in progress");
}

checkNodeConnection();

if (data.createTx)
{
onHandleInvokeContractWithTX(id, std::move(data));
Expand Down
1 change: 1 addition & 0 deletions wallet/api/v6_1/v6_1_api_handle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ namespace beam::wallet
{
BEAM_LOG_VERBOSE() << "InvokeContract(id = " << id << ")";
auto contracts = getContracts();
checkNodeConnection();

if (data.createTx)
{
Expand Down
47 changes: 47 additions & 0 deletions wallet/unittests/wallet_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4297,6 +4297,52 @@ namespace
WALLET_CHECK(txHistory[0].m_txId == txId);
WALLET_CHECK(txHistory[0].m_status == wallet::TxStatus::Completed);
}

void TestInvokeContractNoNodeConnection()
{
cout << "\nTesting invoke_contract without node connection...\n";

io::Reactor::Ptr mainReactor{ io::Reactor::create() };
io::Reactor::Scope scope(*mainReactor);

TestWalletRig sender(createSenderWalletDB(), Wallet::TxCompletedAction(), TestWalletRig::Type::Offline);

// node network that is never connected
auto nodeNetwork = std::make_shared<NodeNetwork>(*sender.m_Wallet);
sender.m_Wallet->SetNodeEndpoint(nodeNetwork);
WALLET_CHECK(nodeNetwork->getConnections() == 0);

ApiInitData data;
data.walletDB = sender.m_WalletDB;
data.wallet = sender.m_Wallet;
data.contracts = IShadersManager::CreateInstance(*sender.m_Wallet, "", "", 0);
data.nodeNetwork = nodeNetwork;

ApiTest api(data);

// every call must fail immediately and must not leave the shader manager busy
for (int i = 0; i < 2; ++i)
{
InvokeContract message;
message.contract = { 0, 'a', 's', 'm' };
message.createTx = false;

bool thrown = false;
try
{
api.onHandleInvokeContract(1, std::move(message));
}
catch (const jsonrpc_exception& ex)
{
thrown = true;
WALLET_CHECK(ex.code() == ApiError::ContractError);
}

WALLET_CHECK(thrown);
WALLET_CHECK(api.m_Messages.empty());
WALLET_CHECK(data.contracts->IsDone());
}
}
}

bool RunNegLoop(beam::Negotiator::IBase& a, beam::Negotiator::IBase& b, const char* szTask)
Expand Down Expand Up @@ -5890,6 +5936,7 @@ int main()
TestCalculateAssetCoinsSelection();

TestContractInvoke();
TestInvokeContractNoNodeConnection();

assert(g_failureCount == 0);
return WALLET_CHECK_RESULT;
Expand Down