/* * This file is part of the Flowee project * Copyright (C) 2019 Tom Zander * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef CONTEXTDATA_H #define CONTEXTDATA_H #include "Flowee.h" #include #include #include /** * @brief The ContextData class is the main class in the flowee-JS engine. * * Every session is build around this class. It can connect to the hub and indexer * and it has the logic to do searchers on both with ease. * */ class ContextData : public Blockchain::SearchEngine { public: ContextData(Napi::Env env); void setupBindings(Napi::Env env, Napi::Object exports); Napi::Value connect(napi_env env, const std::string &hostname); Napi::Value connectHub(napi_env env, const std::string &hostname, int port); Napi::Value connectIndexer(napi_env env, const std::string &hostname, int port); void initializeHubConnection(NetworkConnection connection, const std::string &hubVersion) override; void initializeIndexerConnection(NetworkConnection connection, const std::set &services) override; void hubDisconnected() override; void indexerDisconnected() override; /* * startSearch takes an object literal argument with lots of details * about the search and return the resulting [something]. */ Napi::Value startSearch(const Napi::CallbackInfo &info); /* * sendTransaction takes an argument that should be possible to convert to * a transaction which it will then send to a connected Hub. * * We return a promise to report on the status. */ Napi::Value sendTransaction(const Napi::CallbackInfo &info); enum UpdateType { Subscribe, Unsubscribe }; /* * (un)subscribes an address from the monitor service. */ Napi::Value updateAddressMonitor(const Napi::CallbackInfo &info, UpdateType type); /* * send a random message (constructed in JS) to hub. */ Napi::Value sendJsMessage(const Napi::CallbackInfo &info); // we have callbacks in the shape of a property: Flowee::Callback m_onIndexerConnect, m_onHubConnect, m_onAllConnected; Flowee::Callback m_addressMonitorCallback; // then we also create promises on the 3 connect methods. Flowee::PromiseCallback m_hubConnectPromise; Flowee::PromiseCallback m_indexerConnectPromise; Flowee::PromiseCallback m_fullConnectPromise; /* a simple connect() creates a promise for both the hub and indexer getting connected * Since promises can only be fulfilled in the JS main thread we need a callback for this, * but only if no other callbacks are set by the user yet. */ bool m_madeFullConnectPromise; Flowee::Callback m_promiseCallback; void startAllConnectedCallbacks(); // callback from SearchEngine. void hubSentMessage(const Message &message) override; void indexerSentMessage(const Message &message) override; /// handle the message for a certain network-job. void handleMessageForNetPromise(Napi::Env env, const Message &message); std::string addressForScriptHash(const Streaming::ConstBuffer &buffer) const; private: // This can only be called in the JS thread, and is useful to future callbacks. void createPromiseCallback(Napi::Env env); // this supports things like the sendTransaction() std::map m_networkJobs; int m_nextNetworkJobId = 1; bool startedHubConnection = false; bool startedIndexerConnection = false; /// the addresses we subscribed to. mutable std::mutex m_subscribeAddressesLock; struct SubscribedAddress { Streaming::ConstBuffer bitcoinScriptHashed; std::string origRequest; }; std::deque m_subscribedAddresses; template bool promiseCallback(DataType* data, Callback callback) { // We need to do a call to resolve the promise as Resolve() is not thread-safe. // An empty ThreadSafeFunction has been added only in node 12.6 (2019-07), // so lets try to not use it if we don't have to. Maybe one of the other two // callbacks are present. if (m_onIndexerConnect.present) { m_onIndexerConnect.acquire(); if (m_onIndexerConnect.present) { m_onIndexerConnect.f.NonBlockingCall(data, callback); return true; } } if (m_onHubConnect.present) { m_onHubConnect.acquire(); if (m_onHubConnect.present) { m_onHubConnect.f.NonBlockingCall(data, callback); return true; } } m_promiseCallback.acquire(); if (m_promiseCallback.present) { m_promiseCallback.f.NonBlockingCall(data, callback); return true; } logCritical() << "promise fulfullment can't happen due to too old NodeJS"; return false; } }; #endif