Remove light_client

This commit is contained in:
theoreticalbts 2015-11-23 10:04:54 -05:00
parent 751d68789a
commit 39220456ce
41 changed files with 0 additions and 3565 deletions

View file

@ -4,8 +4,3 @@ add_subdirectory( witness_node )
add_subdirectory( delayed_node )
add_subdirectory( js_operation_serializer )
add_subdirectory( size_checker )
set(BUILD_QT_GUI FALSE CACHE BOOL "Build the Qt-based light client GUI")
if(BUILD_QT_GUI)
# add_subdirectory(light_client)
endif()

View file

@ -1,137 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "Balance.hpp"
#include "ChainDataModel.hpp"
#include "Wallet.hpp"
#include <graphene/chain/account_object.hpp>
void Account::setAccountObject(const graphene::chain::account_object& obj)
{
auto oldName = m_account.name;
auto oldMemoKey = memoKey();
m_account = obj;
if (oldName != m_account.name)
Q_EMIT nameChanged();
if (oldMemoKey != memoKey())
Q_EMIT memoKeyChanged();
if (!m_loaded) {
m_loaded = true;
Q_EMIT loaded();
qDebug() << name() << "loaded.";
}
}
QString Account::memoKey() const
{
return toQString(m_account.options.memo_key);
}
QQmlListProperty<Balance> Account::balances()
{
auto count = [](QQmlListProperty<Balance>* list) {
return reinterpret_cast<Account*>(list->data)->m_balances.size();
};
auto at = [](QQmlListProperty<Balance>* list, int index) {
return reinterpret_cast<Account*>(list->data)->m_balances[index];
};
return QQmlListProperty<Balance>(this, this, count, at);
}
double Account::getActiveControl(Wallet* w, int depth)const
{
if (depth >= GRAPHENE_MAX_SIG_CHECK_DEPTH) return 0;
if (m_account.active.num_auths() == 0) return 0;
if (m_account.active.weight_threshold == 0) return 0;
uint64_t weight = 0;
for (auto& key : m_account.active.key_auths)
{
if (w->hasPrivateKey(toQString(key.first))) weight += key.second;
}
ChainDataModel* model = qobject_cast<ChainDataModel*>(parent());
for (auto& acnt : m_account.active.account_auths)
{
Account* account = model->getAccount(acnt.first.instance.value);
if (!account->m_loaded) {
QEventLoop el;
connect(account, &Account::loaded, &el, &QEventLoop::quit);
QTimer::singleShot(1000, &el, SLOT(quit()));
el.exec();
if (!account->m_loaded)
// We don't have this account loaded yet... Oh well, move along
continue;
}
if (account->getActiveControl(w, depth + 1) >= 1.0)
weight += acnt.second;
}
return double(weight) / double(m_account.active.weight_threshold);
}
double Account::getOwnerControl(Wallet* w)const
{
if (m_account.owner.num_auths() == 0) return 0;
if (m_account.owner.weight_threshold == 0) return 0;
uint64_t weight = 0;
for (auto& key : m_account.owner.key_auths)
{
if (w->hasPrivateKey(toQString(key.first))) weight += key.second;
}
ChainDataModel* model = qobject_cast<ChainDataModel*>(parent());
for (auto& acnt : m_account.owner.account_auths)
{
Account* account = model->getAccount(acnt.first.instance.value);
if (!account->m_loaded)
// We don't have this account loaded yet... Oh well, move along
continue;
if (account->getActiveControl(w) >= 1.0)
weight += acnt.second;
}
return double(weight) / double(m_account.owner.weight_threshold);
}
void Account::update(const graphene::chain::account_balance_object& balance)
{
auto balanceItr = std::find_if(m_balances.begin(), m_balances.end(),
[&balance](Balance* b) { return b->type()->id() == balance.asset_type.instance.value; });
if (balanceItr != m_balances.end()) {
ilog("Updating ${a}'s balance: ${b}", ("a", name().toStdString())("b", balance));
(*balanceItr)->update(balance);
Q_EMIT balancesChanged();
} else {
ilog("Adding to ${a}'s new balance: ${b}", ("a", name().toStdString())("b", balance));
Balance* newBalance = new Balance;
newBalance->setParent(this);
auto model = qobject_cast<ChainDataModel*>(parent());
newBalance->setProperty("type", QVariant::fromValue(model->getAsset(balance.asset_type.instance.value)));
newBalance->setProperty("amount", QVariant::fromValue(balance.balance.value));
m_balances.append(newBalance);
Q_EMIT balancesChanged();
}
}

View file

@ -1,90 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#include "GrapheneObject.hpp"
#include <QQmlListProperty>
#include <graphene/app/full_account.hpp>
namespace graphene { namespace chain {
class account_balance_object;
}}
using graphene::chain::account_object;
using graphene::chain::account_balance_object;
class Balance;
class Wallet;
class Account : public GrapheneObject {
Q_OBJECT
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QQmlListProperty<Balance> balances READ balances NOTIFY balancesChanged)
Q_PROPERTY(QString memoKey READ memoKey NOTIFY memoKeyChanged)
Q_PROPERTY(bool isLoaded MEMBER m_loaded NOTIFY loaded)
account_object m_account;
QList<Balance*> m_balances;
bool m_loaded = false;
public:
Account(ObjectId id = -1, QString name = QString(), QObject* parent = nullptr)
: GrapheneObject(id, parent)
{
m_account.name = name.toStdString();
}
void setAccountObject(const account_object& obj);
const account_object& accountObject()const {
return m_account;
}
QString name()const { return QString::fromStdString(m_account.name); }
QString memoKey()const;
QQmlListProperty<Balance> balances();
void setBalances(QList<Balance*> balances) {
if (balances != m_balances) {
m_balances = balances;
Q_EMIT balancesChanged();
}
}
void update(const account_balance_object& balance);
/**
* Anything greater than 1.0 means full authority.
* Anything between (0 and 1.0) means partial authority
* 0 means no authority.
*
* @return the percent of direct control the wallet has over the account.
*/
Q_INVOKABLE double getOwnerControl(Wallet* w)const;
Q_INVOKABLE double getActiveControl(Wallet* w , int depth = 0)const;
Q_SIGNALS:
void nameChanged();
void balancesChanged();
void memoKeyChanged();
void loaded();
};

View file

@ -1,49 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "Asset.hpp"
#include <graphene/chain/asset_object.hpp>
#include <QVariant>
QString Asset::formatAmount(qint64 amount) const
{
graphene::chain::asset_object ao;
ao.precision = m_precision;
return QString::fromStdString(ao.amount_to_string(amount));
}
void Asset::update(const graphene::chain::asset_object& asset)
{
if (asset.id.instance() != id())
setProperty("id", QVariant::fromValue(asset.id.instance()));
if (asset.symbol != m_symbol.toStdString()) {
m_symbol = QString::fromStdString(asset.symbol);
Q_EMIT symbolChanged();
}
if (asset.precision != m_precision) {
m_precision = asset.precision;
Q_EMIT precisionChanged();
}
if (asset.options.core_exchange_rate != coreExchangeRate)
coreExchangeRate = asset.options.core_exchange_rate;
}

View file

@ -1,61 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#include "GrapheneObject.hpp"
#include "graphene/chain/protocol/asset.hpp"
class Asset : public GrapheneObject {
Q_OBJECT
Q_PROPERTY(QString symbol MEMBER m_symbol READ symbol NOTIFY symbolChanged)
Q_PROPERTY(quint32 precision MEMBER m_precision NOTIFY precisionChanged)
QString m_symbol;
quint32 m_precision;
graphene::chain::price coreExchangeRate;
public:
Asset(ObjectId id = -1, QString symbol = QString(), quint32 precision = 0, QObject* parent = nullptr)
: GrapheneObject(id, parent), m_symbol(symbol), m_precision(precision)
{}
QString symbol() const {
return m_symbol;
}
quint64 precisionPower() const {
quint64 power = 1;
for (int i = 0; i < m_precision; ++i)
power *= 10;
return power;
}
/// Given an amount like 123401, return "1234.01"
Q_INVOKABLE QString formatAmount(qint64 amount) const;
void update(const graphene::chain::asset_object& asset);
Q_SIGNALS:
void symbolChanged();
void precisionChanged();
};

View file

@ -1,36 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "Balance.hpp"
#include "Asset.hpp"
#include <graphene/chain/account_object.hpp>
qreal Balance::amountReal() const {
return amount / qreal(m_type->precisionPower());
}
void Balance::update(const graphene::chain::account_balance_object& update)
{
if (update.balance != amount) {
amount = update.balance.value;
emit amountChanged();
}
}

View file

@ -1,53 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#include "GrapheneObject.hpp"
namespace graphene { namespace chain {
class account_balance_object;
}}
class Asset;
class Balance : public GrapheneObject {
Q_OBJECT
Q_PROPERTY(Asset* type MEMBER m_type READ type NOTIFY typeChanged)
Q_PROPERTY(qint64 amount MEMBER amount NOTIFY amountChanged)
Asset* m_type;
qint64 amount;
public:
// This ultimately needs to be replaced with a string equivalent
Q_INVOKABLE qreal amountReal() const;
Asset* type()const {
return m_type;
}
void update(const graphene::chain::account_balance_object& update);
Q_SIGNALS:
void typeChanged();
void amountChanged();
};

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/mem_fun.hpp>
using boost::multi_index_container;
using boost::multi_index::indexed_by;
using boost::multi_index::hashed_unique;
using boost::multi_index::tag;
using boost::multi_index::const_mem_fun;
using boost::multi_index::ordered_unique;

View file

@ -1,45 +0,0 @@
cmake_minimum_required(VERSION 2.8.11)
project(light_client)
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON)
find_package(Qt5Core)
find_package(Qt5Widgets)
find_package(Qt5Quick)
file(GLOB QML qml/*)
# Skip building QRC in debug mode, since we access the QML files directly on disk in debug mode
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
qt5_add_resources(QML_QRC qml/qml.qrc)
endif()
add_executable(light_client
Wallet.cpp
ChainDataModel.cpp
Operations.cpp
GrapheneApplication.cpp
GrapheneObject.cpp
Asset.cpp
Account.cpp
Balance.cpp
Transaction.cpp
main.cpp ${QML_QRC} ${QML})
if (CMAKE_VERSION VERSION_LESS 3.0)
add_dependencies(light_client gen_qrc)
endif()
target_link_libraries(light_client PRIVATE Qt5::Core Qt5::Widgets Qt5::Quick graphene_chain graphene_egenesis_brief graphene_utilities fc graphene_app )
install( TARGETS
light_client
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)

View file

@ -1,305 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "ChainDataModel.hpp"
#include "Balance.hpp"
#include "Operations.hpp"
#include "Transaction.hpp"
#include <graphene/app/api.hpp>
#include <graphene/chain/protocol/protocol.hpp>
#include <fc/rpc/websocket_api.hpp>
#include <QMetaObject>
using namespace graphene::app;
template<typename T>
QString idToString(T id) {
return QString("%1.%2.%3").arg(T::space_id).arg(T::type_id).arg(ObjectId(id.instance));
}
QString idToString(graphene::db::object_id_type id) {
return QString("%1.%2.%3").arg(id.space(), id.type(), ObjectId(id.instance()));
}
ChainDataModel::ChainDataModel(fc::thread& t, QObject* parent)
:QObject(parent),m_rpc_thread(&t){}
void ChainDataModel::setDatabaseAPI(fc::api<database_api> dbapi) {
m_db_api = dbapi;
m_rpc_thread->async([this] {
m_global_properties = m_db_api->get_global_properties();
m_db_api->subscribe_to_objects([this](const variant& v) {
m_global_properties = v.as<global_property_object>();
}, {m_global_properties.id});
m_dynamic_global_properties = m_db_api->get_dynamic_global_properties();
m_db_api->subscribe_to_objects([this](const variant& d) {
m_dynamic_global_properties = d.as<dynamic_global_property_object>();
}, {m_dynamic_global_properties.id});
m_chain_properties = m_db_api->get_chain_properties();
});
}
void ChainDataModel::setNetworkAPI(fc::api<network_broadcast_api> napi)
{
m_net_api = napi;
}
void ChainDataModel::broadcast(Transaction* transaction)
{
try {
m_net_api->broadcast_transaction_with_callback([transaction](const fc::variant&) {
transaction->setStatus(Transaction::Complete);
}, transaction->internalTransaction());
transaction->setStatus(Transaction::Pending);
} catch (const fc::exception& e) {
transaction->setStatus(Transaction::Failed);
Q_EMIT exceptionThrown(QString::fromStdString(e.to_string()));
}
}
Asset* ChainDataModel::getAsset(ObjectId id)
{
auto& by_id_idx = m_assets.get<by_id>();
auto itr = by_id_idx.find(id);
if (itr == by_id_idx.end())
{
auto result = m_assets.insert(new Asset(id, QString::number(--m_account_query_num), 0, this));
assert(result.second);
// Run in RPC thread
m_rpc_thread->async([this,id,result]{ getAssetImpl(idToString(asset_id_type(id)), &*result.first); });
return *result.first;
}
return *itr;
}
Asset* ChainDataModel::getAsset(QString symbol)
{
auto& by_symbol_idx = m_assets.get<by_symbol_name>();
auto itr = by_symbol_idx.find(symbol);
if (itr == by_symbol_idx.end())
{
auto result = m_assets.insert(new Asset(--m_account_query_num, symbol, 0, this));
assert(result.second);
// Run in RPC thread
m_rpc_thread->async([this,symbol,result](){ getAssetImpl(symbol, &*result.first); });
return *result.first;
}
return *itr;
}
QDateTime ChainDataModel::chainTime() const {
return QDateTime::fromTime_t(m_dynamic_global_properties.time.sec_since_epoch());
}
void ChainDataModel::processUpdatedObject(const fc::variant& update)
{
if (update.is_null())
return;
if (&fc::thread::current() == m_rpc_thread)
{
ilog("Proxying object update to app thread.");
Q_EMIT queueExecute([this,update]{processUpdatedObject(update);});
return;
}
idump((update));
try {
auto id = update.as<variant_object>()["id"].as<object_id_type>();
if (id.space() == protocol_ids) {
switch (id.type()) {
default:
wlog("Update procedure for ${update} is not yet implemented.", ("update", update));
break;
}
} else if (id.space() == implementation_ids) {
switch (id.type()) {
case impl_account_balance_object_type: {
account_balance_object balance = update.as<account_balance_object>();
auto owner = m_accounts.find(balance.owner.instance.value);
if (owner != m_accounts.end())
(*owner)->update(balance);
else
elog("Got unexpected balance update:\n${u}\nfor an account I don't have.",
("u", update));
break;
}
default:
wlog("Update procedure for ${update} is not yet implemented.", ("update", update));
break;
}
} else
wlog("Update procedure for ${update} is not yet implemented.", ("update", update));
} catch (const fc::exception& e) {
elog("Caught exception while updating object: ${e}", ("e", e.to_detail_string()));
}
}
void ChainDataModel::getAssetImpl(QString assetIdentifier, Asset* const * assetInContainer)
{
try {
ilog("Fetching asset ${asset}", ("asset", assetIdentifier.toStdString()));
auto result = m_db_api->lookup_asset_symbols({assetIdentifier.toStdString()});
// Run in main thread
Q_EMIT queueExecute([this,result,assetInContainer](){
ilog("Processing result ${r}", ("r", result));
auto itr = m_assets.iterator_to(*assetInContainer);
if (result.size() == 0 || !result.front()) {
(*itr)->deleteLater();
m_assets.erase(itr);
} else {
m_assets.modify(itr,
[=](Asset* a){
a->setProperty("symbol", QString::fromStdString(result.front()->symbol));
a->setProperty("id", ObjectId(result.front()->id.instance()));
a->setProperty("precision", result.front()->precision);
});
}
});
}
catch ( const fc::exception& e )
{
Q_EMIT exceptionThrown(QString::fromStdString(e.to_string()));
}
}
void ChainDataModel::getAccountImpl(QString accountIdentifier, Account* const * accountInContainer)
{
try {
ilog("Fetching account ${acct}", ("acct", accountIdentifier.toStdString()));
auto result = m_db_api->get_full_accounts([this](const fc::variant& v) {
vector<variant> updates = v.as<vector<variant>>();
for (const variant& update : updates) {
if (update.is_object())
processUpdatedObject(update);
else
elog("Handling object deletions is not yet implemented: ${update}", ("update", update));
}
// TODO: replace true on the next line with a smarter decision as to whether we need status updates or not
}, {accountIdentifier.toStdString()}, true);
fc::optional<full_account> accountPackage;
if (result.count(accountIdentifier.toStdString())) {
accountPackage = result.at(accountIdentifier.toStdString());
// Fetch all necessary assets
QList<asset_id_type> assetsToFetch;
QList<Asset* const *> assetPlaceholders;
assetsToFetch.reserve(accountPackage->balances.size());
// Get list of asset IDs the account has a balance in
std::transform(accountPackage->balances.begin(), accountPackage->balances.end(), std::back_inserter(assetsToFetch),
[](const account_balance_object& b) { return b.asset_type; });
auto function = [this,&assetsToFetch,&assetPlaceholders] {
auto itr = assetsToFetch.begin();
const auto& assets_by_id = m_assets.get<by_id>();
// Filter out assets I already have, create placeholders for the ones I don't.
while (itr != assetsToFetch.end()) {
if (assets_by_id.count(itr->instance))
itr = assetsToFetch.erase(itr);
else {
assetPlaceholders.push_back(&*m_assets.insert(new Asset(itr->instance, QString(), 0, this)).first);
++itr;
}
}
};
QMetaObject::invokeMethod(parent(), "execute", Qt::BlockingQueuedConnection,
Q_ARG(const std::function<void()>&, function));
assert(assetsToFetch.size() == assetPlaceholders.size());
// Blocking call to fetch and complete initialization for all the assets
for (int i = 0; i < assetsToFetch.size(); ++i)
getAssetImpl(idToString(assetsToFetch[i]), assetPlaceholders[i]);
}
// Run in main thread
Q_EMIT queueExecute([this,accountPackage,accountInContainer](){
ilog("Processing result ${r}", ("r", accountPackage));
auto itr = m_accounts.iterator_to(*accountInContainer);
if (!accountPackage.valid()) {
(*itr)->deleteLater();
m_accounts.erase(itr);
} else {
m_accounts.modify(itr, [this,&accountPackage](Account* a){
a->setProperty("id", ObjectId(accountPackage->account.id.instance()));
a->setAccountObject(accountPackage->account);
// Set balances
QList<Balance*> balances;
std::transform(accountPackage->balances.begin(), accountPackage->balances.end(), std::back_inserter(balances),
[this](const account_balance_object& b) {
Balance* bal = new Balance;
bal->setParent(this);
bal->setProperty("amount", QVariant::fromValue(b.balance.value));
bal->setProperty("type", QVariant::fromValue(getAsset(ObjectId(b.asset_type.instance))));
return bal;
});
a->setBalances(balances);
});
}
});
}
catch (const fc::exception& e)
{
Q_EMIT exceptionThrown(QString::fromStdString(e.to_string()));
}
}
Account* ChainDataModel::getAccount(ObjectId id)
{
auto& by_id_idx = m_accounts.get<by_id>();
auto itr = by_id_idx.find(id);
if( itr == by_id_idx.end() )
{
auto tmp = new Account(id, tr("Account #%1").arg(--m_account_query_num), this);
auto result = m_accounts.insert(tmp);
assert(result.second);
// Run in RPC thread
m_rpc_thread->async([this, id, result]{getAccountImpl(idToString(account_id_type(id)), &*result.first);});
return *result.first;
}
return *itr;
}
Account* ChainDataModel::getAccount(QString name)
{
auto& by_name_idx = m_accounts.get<by_account_name>();
auto itr = by_name_idx.find(name);
if( itr == by_name_idx.end() )
{
auto tmp = new Account(--m_account_query_num, name, this);
auto result = m_accounts.insert(tmp);
assert(result.second);
// Run in RPC thread
m_rpc_thread->async([this, name, result]{getAccountImpl(name, &*result.first);});
return *result.first;
}
return *itr;
}

View file

@ -1,108 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#include <fc/network/http/websocket.hpp>
#include <graphene/app/api.hpp>
#include "BoostMultiIndex.hpp"
#include "Asset.hpp"
#include "Account.hpp"
#include <QDateTime>
#include <QObject>
using graphene::chain::by_id;
namespace fc {
class thread;
}
struct by_symbol_name;
typedef multi_index_container<
Asset*,
indexed_by<
hashed_unique< tag<by_id>, const_mem_fun<GrapheneObject, ObjectId, &GrapheneObject::id > >,
ordered_unique< tag<by_symbol_name>, const_mem_fun<Asset, QString, &Asset::symbol> >
>
> asset_multi_index_type;
struct by_account_name;
typedef multi_index_container<
Account*,
indexed_by<
hashed_unique< tag<by_id>, const_mem_fun<GrapheneObject, ObjectId, &GrapheneObject::id > >,
ordered_unique< tag<by_account_name>, const_mem_fun<Account, QString, &Account::name> >
>
> account_multi_index_type;
class Transaction;
class ChainDataModel : public QObject {
Q_OBJECT
Q_PROPERTY(QDateTime chainTime READ chainTime NOTIFY blockReceived)
void processUpdatedObject(const fc::variant& update);
void getAssetImpl(QString assetIdentifier, Asset* const * assetInContainer);
void getAccountImpl(QString accountIdentifier, Account* const * accountInContainer);
public:
Q_INVOKABLE Account* getAccount(ObjectId id);
Q_INVOKABLE Account* getAccount(QString name);
Q_INVOKABLE Asset* getAsset(ObjectId id);
Q_INVOKABLE Asset* getAsset(QString symbol);
QDateTime chainTime() const;
ChainDataModel(){}
ChainDataModel(fc::thread& t, QObject* parent = nullptr);
void setDatabaseAPI(fc::api<graphene::app::database_api> dbapi);
void setNetworkAPI(fc::api<graphene::app::network_broadcast_api> napi);
const graphene::chain::global_property_object& global_properties() const { return m_global_properties; }
const graphene::chain::dynamic_global_property_object& dynamic_global_properties() const { return m_dynamic_global_properties; }
const graphene::chain::chain_property_object& chain_properties() const { return m_chain_properties; }
public Q_SLOTS:
void broadcast(Transaction* transaction);
Q_SIGNALS:
void queueExecute(const std::function<void()>&);
void exceptionThrown(QString message);
void blockReceived();
private:
fc::thread* m_rpc_thread = nullptr;
std::string m_api_url;
fc::api<graphene::app::database_api> m_db_api;
fc::api<graphene::app::network_broadcast_api> m_net_api;
graphene::chain::global_property_object m_global_properties;
graphene::chain::dynamic_global_property_object m_dynamic_global_properties;
graphene::chain::chain_property_object m_chain_properties;
ObjectId m_account_query_num = -1;
account_multi_index_type m_accounts;
asset_multi_index_type m_assets;
};

View file

@ -1,138 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "GrapheneApplication.hpp"
#include "ChainDataModel.hpp"
#include "Wallet.hpp"
#include "Operations.hpp"
#include "Transaction.hpp"
#include <graphene/app/api.hpp>
#include <fc/rpc/websocket_api.hpp>
#include <QStandardPaths>
using graphene::app::login_api;
using graphene::app::database_api;
GrapheneApplication::GrapheneApplication(QObject* parent)
:QObject(parent),m_thread("app")
{
connect(this, &GrapheneApplication::queueExecute,
this, &GrapheneApplication::execute);
m_model = new ChainDataModel(m_thread, this);
m_operationBuilder = new OperationBuilder(*m_model, this);
m_wallet = new Wallet(this);
connect(m_model, &ChainDataModel::queueExecute,
this, &GrapheneApplication::execute);
connect(m_model, &ChainDataModel::exceptionThrown,
this, &GrapheneApplication::exceptionThrown);
}
GrapheneApplication::~GrapheneApplication()
{
}
void GrapheneApplication::setIsConnected(bool v)
{
if (v != m_isConnected)
{
m_isConnected = v;
Q_EMIT isConnectedChanged(m_isConnected);
}
}
void GrapheneApplication::start(QString apiurl, QString user, QString pass)
{
if (!m_thread.is_current())
{
m_done = m_thread.async([=](){ return start(apiurl, user, pass); });
return;
}
try {
m_client = std::make_shared<fc::http::websocket_client>();
ilog("connecting...${s}", ("s",apiurl.toStdString()));
auto con = m_client->connect(apiurl.toStdString());
m_connectionClosed = con->closed.connect([this]{queueExecute([this]{setIsConnected(false);});});
auto apic = std::make_shared<fc::rpc::websocket_api_connection>(*con);
auto remote_api = apic->get_remote_api<login_api>(1);
auto db_api = apic->get_remote_api<database_api>(0);
if (!remote_api->login(user.toStdString(), pass.toStdString()))
{
elog("login failed");
Q_EMIT loginFailed();
return;
}
auto net_api = remote_api->network_broadcast();
ilog("connecting...");
queueExecute([=](){
m_model->setDatabaseAPI(db_api);
m_model->setNetworkAPI(net_api);
});
queueExecute([=](){ setIsConnected(true); });
} catch (const fc::exception& e)
{
Q_EMIT exceptionThrown(QString::fromStdString(e.to_string()));
}
}
QString GrapheneApplication::defaultDataPath()
{
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
}
Transaction* GrapheneApplication::createTransaction() const
{
return new Transaction;
}
void GrapheneApplication::signTransaction(Transaction* transaction) const
{
if (transaction == nullptr) return;
auto getActiveAuth = [this](graphene::chain::account_id_type id) {
return &model()->getAccount(id.instance.value)->accountObject().active;
};
auto getOwnerAuth = [this](graphene::chain::account_id_type id) {
return &model()->getAccount(id.instance.value)->accountObject().owner;
};
auto& chainId = model()->chain_properties().chain_id;
auto& trx = transaction->internalTransaction();
trx.set_reference_block(model()->dynamic_global_properties().head_block_id);
flat_set<public_key_type> pubKeys = wallet()->getAvailablePrivateKeys();
auto requiredKeys = trx.get_required_signatures(chainId, pubKeys, getActiveAuth, getOwnerAuth);
trx.signatures = wallet()->signDigest(trx.digest(), requiredKeys);
idump((trx));
}
Q_SLOT void GrapheneApplication::execute(const std::function<void()>& func)const
{
func();
}

View file

@ -1,99 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#include <fc/thread/thread.hpp>
#include <boost/signals2.hpp>
#include <QObject>
namespace fc { namespace http {
class websocket_client;
}}
class ChainDataModel;
class OperationBuilder;
class OperationBase;
class Transaction;
class Wallet;
class GrapheneApplication : public QObject {
Q_OBJECT
Q_PROPERTY(ChainDataModel* model READ model CONSTANT)
Q_PROPERTY(OperationBuilder* operationBuilder READ operationBuilder CONSTANT)
Q_PROPERTY(Wallet* wallet READ wallet CONSTANT)
Q_PROPERTY(bool isConnected READ isConnected NOTIFY isConnectedChanged)
fc::thread m_thread;
ChainDataModel* m_model = nullptr;
Wallet* m_wallet = nullptr;
OperationBuilder* m_operationBuilder = nullptr;
bool m_isConnected = false;
boost::signals2::scoped_connection m_connectionClosed;
std::shared_ptr<fc::http::websocket_client> m_client;
fc::future<void> m_done;
void setIsConnected(bool v);
protected Q_SLOTS:
void execute(const std::function<void()>&)const;
public:
GrapheneApplication(QObject* parent = nullptr);
~GrapheneApplication();
Wallet* wallet()const { return m_wallet; }
ChainDataModel* model() const {
return m_model;
}
OperationBuilder* operationBuilder() const {
return m_operationBuilder;
}
Q_INVOKABLE void start(QString apiUrl,
QString user,
QString pass);
bool isConnected() const
{
return m_isConnected;
}
Q_INVOKABLE static QString defaultDataPath();
/// Convenience method to get a Transaction in QML. Caller takes ownership of the new Transaction.
Q_INVOKABLE Transaction* createTransaction() const;
Q_INVOKABLE void signTransaction(Transaction* transaction) const;
Q_SIGNALS:
void exceptionThrown(QString message);
void loginFailed();
void isConnectedChanged(bool isConnected);
void queueExecute(const std::function<void()>&);
};

View file

@ -1,23 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "GrapheneObject.hpp"
// This space intentionally left blank

View file

@ -1,51 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#include <QObject>
#include <functional>
using ObjectId = qint64;
Q_DECLARE_METATYPE(ObjectId)
Q_DECLARE_METATYPE(std::function<void()>)
class GrapheneObject : public QObject
{
Q_OBJECT
Q_PROPERTY(ObjectId id MEMBER m_id READ id NOTIFY idChanged)
ObjectId m_id;
public:
GrapheneObject(ObjectId id = -1, QObject* parent = nullptr)
: QObject(parent), m_id(id)
{}
ObjectId id() const {
return m_id;
}
Q_SIGNALS:
void idChanged();
};

View file

@ -1,120 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "Operations.hpp"
#include "Wallet.hpp"
#include <graphene/utilities/key_conversion.hpp>
#include <fc/smart_ref_impl.hpp>
TransferOperation* OperationBuilder::transfer(ObjectId sender, ObjectId receiver, qint64 amount,
ObjectId amountType, QString memo, ObjectId feeType)
{
try {
TransferOperation* op = new TransferOperation;
op->setSender(sender);
op->setReceiver(receiver);
op->setAmount(amount);
op->setAmountType(amountType);
op->setMemo(memo);
op->setFeeType(feeType);
auto feeParameters = model.global_properties().parameters.current_fees->get<graphene::chain::transfer_operation>();
op->setFee(op->operation().calculate_fee(feeParameters).value);
return op;
} catch (const fc::exception& e) {
qDebug() << e.to_detail_string().c_str();
return nullptr;
}
}
QString TransferOperation::memo() const {
if (!m_op.memo)
return QString::null;
if (memoIsEncrypted())
return tr("Encrypted Memo");
QString memo = QString::fromStdString(m_op.memo->get_message({}, {}));
while (memo.endsWith(QChar('\0')))
memo.chop(1);
return memo;
}
bool TransferOperation::memoIsEncrypted() const
{
if (!m_op.memo)
return false;
if (m_op.memo->message.empty())
return false;
if (m_op.memo->from == public_key_type() && m_op.memo->to == public_key_type())
return false;
return true;
}
bool TransferOperation::canEncryptMemo(Wallet* wallet, ChainDataModel* model) const
{
if (!m_op.memo) return false;
auto pub = model->getAccount(sender())->memoKey();
if (!wallet->hasPrivateKey(pub)) return false;
return graphene::utilities::wif_to_key(wallet->getPrivateKey(pub).toStdString()).valid();
}
bool TransferOperation::canDecryptMemo(Wallet* wallet, ChainDataModel* model) const
{
if (!m_op.memo) return false;
auto pub = model->getAccount(receiver())->memoKey();
if (!wallet->hasPrivateKey(pub)) return false;
return graphene::utilities::wif_to_key(wallet->getPrivateKey(pub).toStdString()).valid();
}
QString TransferOperation::decryptedMemo(Wallet* wallet, ChainDataModel* model) const
{
fc::ecc::private_key privateKey;
fc::ecc::public_key publicKey;
if (canEncryptMemo(wallet, model)) {
privateKey = *graphene::utilities::wif_to_key(wallet->getPrivateKey(model->getAccount(sender())->memoKey()).toStdString());
publicKey = m_op.memo->to;
} else if (canDecryptMemo(wallet, model)) {
privateKey = *graphene::utilities::wif_to_key(wallet->getPrivateKey(model->getAccount(receiver())->memoKey()).toStdString());
publicKey = m_op.memo->from;
} else return QString::null;
return QString::fromStdString(m_op.memo->get_message(privateKey, publicKey));
}
void TransferOperation::setMemo(QString memo) {
if (memo == this->memo())
return;
if (!m_op.memo)
m_op.memo = graphene::chain::memo_data();
while (memo.size() % 32)
memo.append('\0');
m_op.memo->set_message({}, {}, memo.toStdString());
Q_EMIT memoChanged();
}
void TransferOperation::encryptMemo(Wallet* wallet, ChainDataModel* model)
{
if (!canEncryptMemo(wallet, model)) return;
auto privateKey = graphene::utilities::wif_to_key(wallet->getPrivateKey(model->getAccount(sender())->memoKey()).toStdString());
if (!privateKey) return;
m_op.memo->set_message(*privateKey, public_key_type(model->getAccount(receiver())->memoKey().toStdString()), memo().toStdString());
Q_EMIT memoChanged();
}

View file

@ -1,163 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#include "ChainDataModel.hpp"
#include <graphene/chain/protocol/transfer.hpp>
#include <QObject>
#include <QtQml>
class OperationBase : public QObject {
Q_OBJECT
Q_PROPERTY(OperationType operationType READ operationType CONSTANT STORED false)
public:
enum OperationType {
TransferOperationType = graphene::chain::operation::tag<graphene::chain::transfer_operation>::value
};
Q_ENUM(OperationType);
OperationBase(QObject* parent = nullptr)
: QObject(parent) {}
virtual ~OperationBase() {}
virtual OperationType operationType() const = 0;
virtual graphene::chain::operation genericOperation() const = 0;
};
QML_DECLARE_INTERFACE(OperationBase)
class TransferOperation : public OperationBase {
Q_OBJECT
Q_PROPERTY(qint64 fee READ fee WRITE setFee NOTIFY feeChanged)
Q_PROPERTY(ObjectId feeType READ feeType WRITE setFeeType NOTIFY feeTypeChanged)
Q_PROPERTY(ObjectId sender READ sender WRITE setSender NOTIFY senderChanged)
Q_PROPERTY(ObjectId receiver READ receiver WRITE setReceiver NOTIFY receiverChanged)
Q_PROPERTY(qint64 amount READ amount WRITE setAmount NOTIFY amountChanged)
Q_PROPERTY(ObjectId amountType READ amountType WRITE setAmountType NOTIFY amountTypeChanged)
Q_PROPERTY(QString memo READ memo WRITE setMemo NOTIFY memoChanged)
Q_PROPERTY(bool memoIsEncrypted READ memoIsEncrypted NOTIFY memoChanged)
graphene::chain::transfer_operation m_op;
public:
TransferOperation(){}
TransferOperation(const graphene::chain::transfer_operation& op)
: m_op(op) {}
virtual OperationBase::OperationType operationType() const override {
return OperationBase::TransferOperationType;
}
virtual graphene::chain::operation genericOperation() const override {
return m_op;
}
qint64 fee() const { return m_op.fee.amount.value; }
ObjectId feeType() const { return m_op.fee.asset_id.instance.value; }
ObjectId sender() const { return m_op.from.instance.value; }
ObjectId receiver() const { return m_op.to.instance.value; }
qint64 amount() const { return m_op.amount.amount.value; }
ObjectId amountType() const { return m_op.amount.asset_id.instance.value; }
/// This does not deal with encrypted memos. The memo stored here is unencrypted. The encryption step must be
/// performed by calling encryptMemo()
QString memo() const;
bool memoIsEncrypted()const;
Q_INVOKABLE bool canEncryptMemo(Wallet* wallet, ChainDataModel* model) const;
Q_INVOKABLE bool canDecryptMemo(Wallet* wallet, ChainDataModel* model) const;
Q_INVOKABLE QString decryptedMemo(Wallet* wallet, ChainDataModel* model) const;
const graphene::chain::transfer_operation& operation() const { return m_op; }
graphene::chain::transfer_operation& operation() { return m_op; }
public Q_SLOTS:
void setFee(qint64 arg) {
if (arg == fee())
return;
m_op.fee.amount = arg;
Q_EMIT feeChanged();
}
void setFeeType(ObjectId arg) {
if (arg == feeType())
return;
m_op.fee.asset_id = arg;
Q_EMIT feeTypeChanged();
}
void setSender(ObjectId arg) {
if (arg == sender())
return;
m_op.from = arg;
Q_EMIT senderChanged();
}
void setReceiver(ObjectId arg) {
if (arg == receiver())
return;
m_op.to = arg;
Q_EMIT receiverChanged();
}
void setAmount(qint64 arg) {
if (arg == amount())
return;
m_op.amount.amount = arg;
Q_EMIT amountChanged();
}
void setAmountType(ObjectId arg) {
if (arg == amountType())
return;
m_op.amount.asset_id = arg;
Q_EMIT amountTypeChanged();
}
/// This does not deal with encrypted memos. The memo stored here is unencrypted. The encryption step must be
/// performed by calling encryptMemo()
void setMemo(QString memo);
void encryptMemo(Wallet* wallet, ChainDataModel* model);
Q_SIGNALS:
void feeChanged();
void feeTypeChanged();
void senderChanged();
void receiverChanged();
void amountChanged();
void amountTypeChanged();
void memoChanged();
};
QML_DECLARE_TYPE(TransferOperation)
/**
* @brief The OperationBuilder class creates operations which are inspectable by the GUI
*
* @note All operations returned by OperationBuilder are heap allocated on-demand and do not have parents. The caller
* must take ownership of these objects to prevent them from leaking.
*/
class OperationBuilder : public QObject {
Q_OBJECT
ChainDataModel& model;
public:
OperationBuilder(ChainDataModel& model, QObject* parent = nullptr)
: QObject(parent), model(model){}
Q_INVOKABLE TransferOperation* transfer(ObjectId sender, ObjectId receiver, qint64 amount,
ObjectId amountType, QString memo, ObjectId feeType);
};

View file

@ -1,11 +0,0 @@
== Graphene Client GUI ==
This is a Qt-based native GUI client for Graphene blockchains.
To build this GUI, run cmake with -DBUILD_QT_GUI=ON
This GUI depends on Qt 5.5 or later. If you do not have Qt 5.5 installed
in the canonical location on your OS (or if your OS does not have a
canonical location for libraries), you can specify the Qt path by running
cmake with -DCMAKE_PREFIX_PATH=/path/to/Qt/5.5/gcc_64 as appropriate for
your environment.

View file

@ -1,79 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "Transaction.hpp"
#include "Operations.hpp"
#include <fc/reflect/typename.hpp>
struct OperationConverter {
using result_type = OperationBase*;
OperationBase* operator()(const graphene::chain::transfer_operation& op) const {
auto ret = new TransferOperation(op);
QObject::connect(ret, &QObject::destroyed, []{qDebug("Cleaned up operation");});
return ret;
}
template<typename Op>
OperationBase* operator()(const Op&) const {
elog("NYI: OperationConverter for ${type}", ("type", fc::get_typename<Op>::name()));
abort();
}
};
QString Transaction::statusString() const
{
return QMetaEnum::fromType<Status>().valueToKey(status());
}
QQmlListProperty<OperationBase> Transaction::operations()
{
auto append = [](QQmlListProperty<OperationBase>* list, OperationBase* op) {
static_cast<Transaction*>(list->data)->appendOperation(op);
};
auto count = [](QQmlListProperty<OperationBase>* list) {
return static_cast<Transaction*>(list->data)->operationCount();
};
auto at = [](QQmlListProperty<OperationBase>* list, int index) {
return static_cast<Transaction*>(list->data)->operationAt(index);
};
auto clear = [](QQmlListProperty<OperationBase>* list) {
static_cast<Transaction*>(list->data)->clearOperations();
};
return QQmlListProperty<OperationBase>(this, this, append, count, at, clear);
}
OperationBase* Transaction::operationAt(int index) const {
return m_transaction.operations[index].visit(OperationConverter());
}
void Transaction::appendOperation(OperationBase* op)
{
if (op == nullptr)
{
qWarning("Unable to append null operation to transaction");
return;
}
op->setParent(this);
m_transaction.operations.push_back(op->genericOperation());
Q_EMIT operationsChanged();
}

View file

@ -1,102 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#include <graphene/chain/protocol/transaction.hpp>
#include <QDateTime>
#include <QObject>
#include <QQmlListProperty>
#include <QDebug>
class OperationBase;
class Transaction : public QObject {
Q_OBJECT
public:
enum Status { Unbroadcasted, Pending, Complete, Failed };
Q_ENUM(Status);
Status status() const { return m_status; }
QString statusString() const;
QQmlListProperty<OperationBase> operations();
OperationBase* operationAt(int index) const;
int operationCount() const {
return m_transaction.operations.size();
}
graphene::chain::signed_transaction& internalTransaction() {
return m_transaction;
}
QDateTime expiration() const
{
return QDateTime::fromTime_t(m_transaction.expiration.sec_since_epoch());
}
public Q_SLOTS:
void setStatus(Status status)
{
if (status == m_status)
return;
m_status = status;
qDebug() << status;
emit statusChanged(status);
}
/**
* @brief Append the operation to the transaction
* @param op The operation to append. This Transaction will take ownership of the operation.
*/
void appendOperation(OperationBase* op);
void clearOperations() {
m_transaction.operations.clear();
Q_EMIT operationsChanged();
}
void setExpiration(QDateTime expiration)
{
fc::time_point_sec exp(expiration.toTime_t());
if (exp == m_transaction.expiration)
return;
m_transaction.expiration = exp;
emit expirationChanged(expiration);
}
signals:
void statusChanged(Status status);
void operationsChanged();
void expirationChanged(QDateTime expiration);
private:
Q_PROPERTY(Status status READ status WRITE setStatus NOTIFY statusChanged)
Q_PROPERTY(QString statusString READ statusString NOTIFY statusChanged STORED false)
Q_PROPERTY(QQmlListProperty<OperationBase> operations READ operations NOTIFY operationsChanged)
Q_PROPERTY(QDateTime expiration READ expiration WRITE setExpiration NOTIFY expirationChanged)
Status m_status = Unbroadcasted;
graphene::chain::signed_transaction m_transaction;
};

View file

@ -1,480 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "Transaction.hpp"
#include "Wallet.hpp"
#include <graphene/utilities/key_conversion.hpp>
#include <graphene/chain/protocol/fee_schedule.hpp>
#include <fc/crypto/aes.hpp>
#include <fc/io/json.hpp>
QString toQString(public_key_type k) { return QString::fromStdString(fc::variant(k).as_string()); }
Wallet::Wallet(QObject* parent)
:QObject(parent)
{
}
Wallet::~Wallet()
{
close();
}
bool Wallet::open(QString file_path)
{
fc::path p(file_path.toStdString());
if( !fc::exists(p) )
{
ilog("Unable to open wallet file '${f}'; it does not exist", ("f",p));
return false;
}
_data = fc::json::from_file(p).as<wallet_file>();
for( const auto& key : _data.encrypted_private_keys )
{
if( key.second.label != string() )
_label_to_key[QString::fromStdString(key.second.label)] = toQString(key.first);
if( key.second.encrypted_private_key.size() )
_available_private_keys.insert(key.first);
}
_wallet_file_path = p;
Q_EMIT isOpenChanged(true);
return true;
}
bool Wallet::isOpen()const
{
return _wallet_file_path != fc::path();
}
bool Wallet::close()
{
if( !isOpen() ) return false;
save();
_wallet_file_path = fc::path();
Q_EMIT isOpenChanged(false);
return false;
}
bool Wallet::save()
{
if( !isOpen() ) return false;
/// TODO: backup existing wallet file first.
fc::json::save_to_file(_data, _wallet_file_path);
return false;
}
bool Wallet::saveAs(QString file_path)
{
if( !isOpen() ) return false;
fc::path p(file_path.toStdString());
if( fc::exists(p) ) return false;
fc::json::save_to_file(_data, _wallet_file_path);
return true;
}
bool Wallet::create(QString file_path, QString password, QString brain_key)
{
if( isOpen() ) return false;
if( password == QString() ) return false;
fc::path p(file_path.toStdString());
if( fc::exists(p) )
{
ilog("Unable to create wallet file '${f}' because a file with that name already exists.", ("f",p));
return false;
}
if( brain_key == QString() )
{
auto key = fc::ecc::private_key::generate().get_secret();
brain_key.fromStdString( fc::variant(key).as_string() );
}
auto brainkey = brain_key.toStdString();
auto pw_str = password.toStdString();
auto password_hash = fc::sha512::hash(pw_str.c_str(), pw_str.size());
_decrypted_master_key = fc::sha512::hash(fc::ecc::private_key::generate().get_secret());
_data.master_key_digest = fc::sha512::hash(_decrypted_master_key);
_data.encrypted_master_key = fc::aes_encrypt(password_hash, fc::raw::pack(_decrypted_master_key));
_data.brain_key_digest = fc::sha512::hash(brainkey.c_str(), brainkey.size());
_data.encrypted_brain_key = fc::aes_encrypt(_decrypted_master_key, fc::raw::pack(brainkey));
QFileInfo(file_path).absoluteDir().mkpath(".");
fc::json::save_to_file(_data, p);
_wallet_file_path = p;
ilog("Created wallet file '${f}'", ("f", p));
return true;
}
bool Wallet::loadBrainKey(QString brain_key)
{
if( !isOpen() ) return false;
if( brain_key == QString() ) return false;
auto brainkey = brain_key.toStdString();
if( _data.brain_key_digest != fc::sha512::hash(brainkey.c_str(), brainkey.size()) )
return false;
_brain_key = brain_key;
return true;
}
bool Wallet::purgeBrainKey()
{
if( !isOpen() ) return false;
_data.encrypted_brain_key.resize(0);
_brain_key = QString();
return save();
}
bool Wallet::hasBrainKey()const
{
if( !isOpen() ) return false;
return _brain_key != QString() || _data.encrypted_brain_key.size();
}
QString Wallet::getBrainKey()
{
if( !isOpen() ) return QString();
if( isLocked() ) return QString();
if( _brain_key != QString() )
return _brain_key;
auto dec_brain_key = fc::aes_decrypt(_decrypted_master_key, _data.encrypted_brain_key);
auto dec_brain_key_str = fc::raw::unpack<string>(dec_brain_key);
_brain_key.fromStdString(dec_brain_key_str);
return _brain_key;
}
bool Wallet::isLocked()const
{
if( !isOpen() ) return true;
return _decrypted_master_key == fc::sha512();
}
bool Wallet::unlock(QString password)
{
try {
if( !isLocked() ) return true;
auto pw_str = password.toStdString();
auto password_hash = fc::sha512::hash(pw_str.c_str(), pw_str.size());
auto plain_txt = fc::aes_decrypt(password_hash, _data.encrypted_master_key);
_decrypted_master_key = fc::raw::unpack<fc::sha512>(plain_txt);
if( _data.master_key_digest != fc::sha512::hash(_decrypted_master_key) )
_decrypted_master_key = fc::sha512();
Q_EMIT isLockedChanged(isLocked());
return !isLocked();
} catch (const fc::exception& e) {
elog(e.to_detail_string());
return false;
}
}
bool Wallet::lock()
{
if( !isOpen() ) return false;
_brain_key = QString();
_decrypted_master_key = fc::sha512();
Q_EMIT isLockedChanged(isLocked());
return true;
}
bool Wallet::changePassword(QString new_password)
{
if( !isOpen() ) return false;
if( isLocked() ) return false;
auto pw_str = new_password.toStdString();
auto password_hash = fc::sha512::hash(pw_str.c_str(), pw_str.size());
_data.encrypted_master_key = fc::aes_encrypt(password_hash, fc::raw::pack(_decrypted_master_key));
save();
return true;
}
bool Wallet::hasPrivateKey(QString pubkey, bool include_with_brain_key)
{
auto pub = fc::variant(pubkey.toStdString()).as<public_key_type>();
auto itr = _data.encrypted_private_keys.find(pub);
if( itr == _data.encrypted_private_keys.end() )
return false;
if( itr->second.encrypted_private_key.size() )
return true;
if( include_with_brain_key && itr->second.brain_sequence >= 0 )
{
if( !itr->second.owner )
return true;
return hasPrivateKey(toQString(*itr->second.owner), include_with_brain_key);
}
return false;
}
QString Wallet::getPrivateKey(QString pubkey)
{
if( !isOpen() ) return QString();
if( isLocked() ) return QString();
auto pub = fc::variant(pubkey.toStdString()).as<public_key_type>();
auto itr = _data.encrypted_private_keys.find(pub);
if( itr == _data.encrypted_private_keys.end() )
return QString();
if( itr->second.encrypted_private_key.size() == 0 )
return QString();
auto plain = fc::aes_decrypt(_decrypted_master_key, itr->second.encrypted_private_key);
return QString::fromStdString(fc::raw::unpack<std::string>(plain));
}
QString Wallet::getPublicKey(QString wif_private_key)const
{
auto priv = graphene::utilities::wif_to_key(wif_private_key.toStdString());
if( !priv ) return QString();
auto pub = public_key_type(priv->get_public_key());
return QString::fromStdString(fc::variant(pub).as_string());
}
QString Wallet::getActivePrivateKey(QString owner_pub_key, uint32_t seq)
{
if( !isOpen() ) return QString();
if( isLocked() ) return QString();
auto owner_wif_private_key = getPrivateKey(owner_pub_key);
if( owner_wif_private_key == QString() ) return QString();
auto seed = (owner_wif_private_key + " " + QString::number(seq)).toStdString();
auto secret = fc::sha256::hash(fc::sha512::hash(seed.c_str(), seed.size()));
auto wif = graphene::utilities::key_to_wif(secret);
auto priv_key = graphene::utilities::wif_to_key(wif);
if( !priv_key ) return QString();
public_key_type active_pub_key(priv_key->get_public_key());
_data.encrypted_private_keys[active_pub_key].encrypted_private_key = fc::aes_encrypt(_decrypted_master_key, fc::raw::pack(wif));
_data.encrypted_private_keys[active_pub_key].owner = fc::variant(owner_pub_key.toStdString()).as<public_key_type>();
_data.encrypted_private_keys[active_pub_key].brain_sequence = seq;
_available_private_keys.insert( active_pub_key );
return QString::fromStdString(wif);
}
QString Wallet::getOwnerPrivateKey(uint32_t seq)
{
if( !isOpen() ) return QString();
if( isLocked() ) return QString();
if( !hasBrainKey() ) return QString();
auto seed = (getBrainKey() + " " + QString::number(seq)).toStdString();
auto secret = fc::sha256::hash(fc::sha512::hash(seed.c_str(), seed.size()));
auto wif = graphene::utilities::key_to_wif(secret);
auto priv_key = graphene::utilities::wif_to_key(wif);
if( !priv_key ) return QString();
public_key_type owner_pub_key(priv_key->get_public_key());
_data.encrypted_private_keys[owner_pub_key].encrypted_private_key = fc::aes_encrypt(_decrypted_master_key, fc::raw::pack(wif));
_data.encrypted_private_keys[owner_pub_key].brain_sequence = seq;
_available_private_keys.insert(owner_pub_key);
return QString::fromStdString(wif);
}
QString Wallet::getActivePublicKey(QString active_pub, uint32_t seq)
{
return getPublicKey(getActivePrivateKey(active_pub, seq));
}
QString Wallet::getOwnerPublicKey(uint32_t seq)
{
return getPublicKey(getOwnerPrivateKey(seq));
}
QString Wallet::getKeyLabel(QString pubkey)
{
if( !isOpen() ) return QString();
public_key_type key = fc::variant( pubkey.toStdString() ).as<public_key_type>();
auto itr = _data.encrypted_private_keys.find(key);
if( itr == _data.encrypted_private_keys.end() )
return QString();
return QString::fromStdString(itr->second.label);
}
/**
* The same label may not be assigned to more than one key, this method will
* fail if a key with the same label already exists.
*
* @return true if the label was set
*/
bool Wallet::setKeyLabel(QString pubkey, QString label)
{
if( label == QString() ) // clear the label
{
auto pub = fc::variant(pubkey.toStdString()).as<public_key_type>();
auto old_label = _data.encrypted_private_keys[pub].label;
_data.encrypted_private_keys[pub].label = string();
if( old_label.size() )
_label_to_key.erase(QString::fromStdString(old_label));
return true;
}
auto itr = _label_to_key.find(label);
if( itr != _label_to_key.end() )
return false;
_label_to_key[label] = pubkey;
auto pub = fc::variant(pubkey.toStdString()).as<public_key_type>();
_data.encrypted_private_keys[pub].label = label.toStdString();
return true;
}
QList<QPair<QString,QString>> Wallet::getAllPublicKeys(bool only_if_private)const
{
QList< QPair<QString,QString>> result;
if( !isOpen() ) return result;
for( const auto& item : _data.encrypted_private_keys )
{
if( only_if_private && !item.second.encrypted_private_key.size() ) continue;
result.push_back(qMakePair(toQString(item.first), QString::fromStdString(item.second.label)));
}
return result;
}
QString Wallet::getPublicKey(QString label)
{
if( !isOpen() ) return QString::null;
auto itr = _label_to_key.find(label);
if( itr != _label_to_key.end() )
return QString::null;
return itr->second;
}
/** imports a public key and assigns it a label */
bool Wallet::importPublicKey(QString pubkey, QString label)
{
return setKeyLabel(pubkey, label);
}
/**
* @param wifkey a private key in (WIF) Wallet Import Format
* @pre !isLocked()
**/
bool Wallet::importPrivateKey(QString wifkey, QString label)
{
if( !isOpen() ) return false;
if( isLocked() ) return false;
auto priv = graphene::utilities::wif_to_key(wifkey.toStdString());
if (!priv) return false;
auto p = priv->get_public_key();
auto pub = toQString(p);
importPublicKey(pub, label);
_data.encrypted_private_keys[p].encrypted_private_key = fc::aes_encrypt(_decrypted_master_key, fc::raw::pack(wifkey.toStdString()));
_available_private_keys.insert(p);
return true;
}
/** removes the key, its lablel and its private key */
bool Wallet::removePublicKey(QString pubkey)
{
if( !isOpen() ) return false;
auto pub = fc::variant( pubkey.toStdString() ).as<public_key_type>();
_available_private_keys.erase(pub);
auto itr = _data.encrypted_private_keys.find(pub);
if( itr != _data.encrypted_private_keys.end() )
{
_label_to_key.erase(QString::fromStdString(itr->second.label));
_data.encrypted_private_keys.erase(itr);
return true;
}
return false;
}
/** removes only the private key, keeping the public key and label
*
* @pre isOpen() && !isLocked()
**/
bool Wallet::removePrivateKey(QString pubkey)
{
if( !isOpen() ) return false;
if( isLocked() ) return false;
auto pub = fc::variant(pubkey.toStdString()).as<public_key_type>();
_data.encrypted_private_keys[pub].encrypted_private_key.resize(0);
_available_private_keys.erase(pub);
return true;
}
/**
* @pre !isLocked()
*/
vector<signature_type> Wallet::signDigest(const digest_type& d, const set<public_key_type>& keys)const
{
vector<signature_type> result;
if( !isOpen() ) return result;
if( isLocked() ) return result;
result.reserve( keys.size() );
for( const auto& key : keys )
{
auto itr = _data.encrypted_private_keys.find(key);
if( itr == _data.encrypted_private_keys.end() )
return vector<signature_type>();
if( itr->second.encrypted_private_key.size() == 0 )
return vector<signature_type>();
auto plain_wif = fc::aes_decrypt(_decrypted_master_key, itr->second.encrypted_private_key);
auto wif = fc::raw::unpack<std::string>(plain_wif);
auto priv = graphene::utilities::wif_to_key(wif);
if( !priv ) return vector<signature_type>();
result.push_back(priv->sign_compact(d));
}
return result;
}
const flat_set<public_key_type>& Wallet::getAvailablePrivateKeys()const
{
return _available_private_keys;
}

View file

@ -1,177 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#pragma once
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#include <graphene/chain/protocol/types.hpp>
#include <QObject>
#include <QList>
#include <QPair>
#include <QtQml>
using std::string;
using std::vector;
using std::set;
using fc::flat_set;
using std::map;
using graphene::chain::public_key_type;
using graphene::chain::digest_type;
using graphene::chain::signature_type;
using fc::optional;
class Transaction;
QString toQString(public_key_type k);
struct key_data
{
string label; /** unique label assigned to this key */
/** encrypted as a packed std::string containing a wif private key */
vector<char> encrypted_private_key;
int32_t brain_sequence = -1;
optional<public_key_type> owner; /// if this key was derived from an owner key + sequence
};
struct wallet_file
{
vector<char> encrypted_brain_key;
fc::sha512 brain_key_digest;
vector<char> encrypted_master_key;
fc::sha512 master_key_digest;
map<public_key_type, key_data> encrypted_private_keys;
};
/**
* @class Wallet
* @brief Securely maintains a set of labeled public and private keys
*
* @note this API is designed for use with QML which does not support exceptions so
* all errors are passed via return values.
*/
class Wallet : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isOpen READ isOpen NOTIFY isOpenChanged)
Q_PROPERTY(bool isLocked READ isLocked NOTIFY isLockedChanged)
public:
Wallet( QObject* parent = nullptr );
~Wallet();
Q_INVOKABLE bool open(QString file_path);
Q_INVOKABLE bool close();
bool isOpen()const;
Q_INVOKABLE bool save();
Q_INVOKABLE bool saveAs(QString file_path);
Q_INVOKABLE bool create(QString file_path, QString password, QString brain_key = QString());
/** required to generate new owner keys */
Q_INVOKABLE bool loadBrainKey(QString brain_key);
/** removes brain key to secure owner keys */
Q_INVOKABLE bool purgeBrainKey();
Q_INVOKABLE bool hasBrainKey()const;
/** @pre hasBrainKey() */
Q_INVOKABLE QString getBrainKey();
bool isLocked()const;
Q_INVOKABLE bool unlock(QString password);
Q_INVOKABLE bool lock();
Q_INVOKABLE bool changePassword(QString new_password);
/**
* @pre !isLocked();
* @post save()
* @return WIF private key
*/
Q_INVOKABLE QString getActivePrivateKey(QString owner_public_key, uint32_t sequence);
Q_INVOKABLE QString getActivePublicKey(QString owner_public_key, uint32_t sequence);
/**
* @pre !isLocked();
* @pre hasBrainKey();
* @post save()
* @return WIF private key
*/
Q_INVOKABLE QString getOwnerPrivateKey(uint32_t sequence);
Q_INVOKABLE QString getOwnerPublicKey(uint32_t sequence);
Q_INVOKABLE QString getPublicKey(QString wif_private_key)const;
Q_INVOKABLE QString getKeyLabel(QString pubkey);
Q_INVOKABLE bool setKeyLabel(QString pubkey, QString label);
Q_INVOKABLE QString getPublicKey(QString label);
Q_INVOKABLE QString getPrivateKey(QString pubkey);
Q_INVOKABLE bool hasPrivateKey(QString pubkey, bool include_with_brain_key = false);
/** imports a public key and assigns it a label */
Q_INVOKABLE bool importPublicKey(QString pubkey, QString label = QString());
/**
* @param wifkey a private key in (WIF) Wallet Import Format
* @pre !isLocked()
**/
Q_INVOKABLE bool importPrivateKey(QString wifkey, QString label = QString());
/** removes the key, its lablel and its private key */
Q_INVOKABLE bool removePublicKey(QString pubkey);
/** removes only the private key, keeping the public key and label */
Q_INVOKABLE bool removePrivateKey(QString pubkey);
/**
* @param only_if_private filter any public keys for which the wallet lacks a private key
* @return a list of PUBLICKEY, LABEL for all known public keys
*/
Q_INVOKABLE QList<QPair<QString,QString>> getAllPublicKeys(bool only_if_private)const;
/**
* @pre !isLocked()
*/
vector<signature_type> signDigest(const digest_type& d,
const set<public_key_type>& keys)const;
const flat_set<public_key_type>& getAvailablePrivateKeys()const;
Q_SIGNALS:
void isLockedChanged(bool state);
void isOpenChanged(bool state);
private:
fc::path _wallet_file_path;
wallet_file _data;
/** used to decrypt all of the encrypted private keys */
fc::sha512 _decrypted_master_key;
flat_set<public_key_type> _available_private_keys;
map<QString,QString> _label_to_key;
QString _brain_key;
};
QML_DECLARE_TYPE(Wallet)
FC_REFLECT( key_data, (label)(encrypted_private_key)(brain_sequence)(owner) )
FC_REFLECT( wallet_file,
(encrypted_brain_key)
(brain_key_digest)
(encrypted_master_key)
(master_key_digest)
(encrypted_private_keys)
)

View file

@ -1,92 +0,0 @@
/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Any modified source or binaries are used only with the BitShares network.
*
* 2. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 3. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include "GrapheneApplication.hpp"
#include "ChainDataModel.hpp"
#include "Transaction.hpp"
#include "Operations.hpp"
#include "Balance.hpp"
#include "Wallet.hpp"
class Crypto {
Q_GADGET
public:
Q_INVOKABLE QString sha256(QByteArray data) {
return QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex();
}
};
QML_DECLARE_TYPE(Crypto)
int main(int argc, char *argv[])
{
#ifndef NDEBUG
QQmlDebuggingEnabler enabler;
#endif
fc::thread::current().set_name("main");
QApplication app(argc, argv);
app.setApplicationName("Graphene Client");
app.setOrganizationDomain("cryptonomex.org");
app.setOrganizationName("Cryptonomex, Inc.");
qRegisterMetaType<std::function<void()>>();
qRegisterMetaType<ObjectId>();
qRegisterMetaType<QList<OperationBase*>>();
qRegisterMetaType<Transaction::Status>();
qmlRegisterType<Asset>("Graphene.Client", 0, 1, "Asset");
qmlRegisterType<Balance>("Graphene.Client", 0, 1, "Balance");
qmlRegisterType<Account>("Graphene.Client", 0, 1, "Account");
qmlRegisterType<ChainDataModel>("Graphene.Client", 0, 1, "DataModel");
qmlRegisterType<Wallet>("Graphene.Client", 0, 1, "Wallet");
qmlRegisterType<GrapheneApplication>("Graphene.Client", 0, 1, "GrapheneApplication");
qmlRegisterType<Transaction>("Graphene.Client", 0, 1, "Transaction");
qmlRegisterUncreatableType<OperationBase>("Graphene.Client", 0, 1, "OperationBase",
"OperationBase is an abstract base class; cannot be created");
qmlRegisterType<TransferOperation>("Graphene.Client", 0, 1, "TransferOperation");
qmlRegisterUncreatableType<OperationBuilder>("Graphene.Client", 0, 1, "OperationBuilder",
QStringLiteral("OperationBuilder cannot be created from QML"));
QQmlApplicationEngine engine;
QVariant crypto;
crypto.setValue(Crypto());
engine.rootContext()->setContextProperty("Crypto", crypto);
#ifdef NDEBUG
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
#else
engine.load(QUrl(QStringLiteral("qml/main.qml")));
QFileSystemWatcher watcher;
qDebug() << watcher.addPath("qml/");
QObject::connect(&watcher, &QFileSystemWatcher::directoryChanged, &engine, [&](QString path) {
qDebug() << "Changed file" << path;
engine.clearComponentCache();
});
#endif
return app.exec();
}
#include "main.moc"

View file

@ -1 +0,0 @@
*.depends

View file

@ -1,104 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.2
import Graphene.Client 0.1
import "."
/// A component for choosing an Account from the chain
RowLayout {
property GrapheneApplication app
/// The text to show in the name field when it is empty
property alias placeholderText: accountNameField.placeholderText
/// Index into the balances Array of the balance to show beneath the name field
property int showBalance: -1
/// The Account object the user has selected
property Account account
/// A real in the range [0,1] representing the amount of control the wallet has over this account
property real accountControlLevel: account && account.isLoaded? account.getActiveControl(app.wallet) : 0
/// An Array of Balance objects held by account
property var balances: account? Object.keys(account.balances).map(function(key){return account.balances[key]})
.filter(function(balance){return balance.amount > 0})
: null
/// Set the name field to have active focus
function setFocus() {
accountNameField.forceActiveFocus()
}
signal balanceClicked(var balance)
Identicon {
name: account && account.name == accountNameField.text? accountNameField.text : ""
width: Scaling.cm(2)
height: Scaling.cm(2)
showOwnership: accountControlLevel > 0
fullOwnership: accountControlLevel >= 1
}
Column {
Layout.fillWidth: true
TextField {
id: accountNameField
width: parent.width
onEditingFinished: accountDetails.update(text)
onTextChanged: if (account && account.name !== text) accountDetails.update("")
}
Text {
id: accountDetails
width: parent.width
height: text? implicitHeight : 0
onLinkActivated: if (link === "balance") balanceClicked(balances[showBalance].amountReal())
Behavior on height { NumberAnimation{ easing.type: Easing.InOutQuad } }
function update(name) {
if (!name)
{
text = ""
account = null
return
}
account = app.model.getAccount(name)
if (account == null) {
text = qsTr("Error fetching account.")
} else {
text = Qt.binding(function() {
if (account == null)
return qsTr("Account does not exist.")
var text = qsTr("Account ID: %1").arg(!account.isLoaded? qsTr("Loading...")
: account.id)
if (showBalance >= 0) {
var bal = balances[showBalance]
text += "<br/>" + qsTr("Balance: <a href='balance'>%1</a> %2").arg(String(bal.amountReal()))
.arg(bal.type.symbol)
}
return text
})
}
}
Behavior on text {
SequentialAnimation {
PropertyAnimation {
target: accountDetails
property: "opacity"
from: 1; to: 0
duration: 100
}
PropertyAction { target: accountDetails; property: "text" }
PropertyAnimation {
target: accountDetails
property: "opacity"
from: 0; to: 1
duration: 100
}
}
}
}
}
}

View file

@ -1,37 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.2
import Graphene.Client 0.1
import "."
/**
* Base for all forms
*
* This base contains all of the properties, slots, and signals which all forms are expected to expose. It also
* automatically lays its children out in a ColumnLayout
*/
Rectangle {
anchors.fill: parent
/// Reference to the GrapheneApplication object
property GrapheneApplication app
/// Parent should trigger this signal to notify the form that it is about to be displayed
/// See specific form for the argument semantics
signal display(var arg)
/// Emitted when the form is canceled -- see specific form for the argument semantics
signal canceled(var arg)
/// Emitted when the form is completed -- see specific form for the argument semantics
signal completed(var arg)
default property alias childItems: childLayout.data
ColumnLayout {
id: childLayout
anchors.centerIn: parent
width: parent.width - Scaling.cm(2)
spacing: Scaling.mm(5)
}
}

View file

@ -1,154 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
Rectangle {
id: greySheet
state: "HIDDEN"
color: Qt.rgba(0, 0, 0, showOpacity)
property real showOpacity: .5
property int animationTime: 300
/// Emitted immediately when opening, before fade-in animation
signal opening
/// Emitted when opened, following fade-in animation
signal opened
/// Emitted immediately when closing, before fade-out animation
signal closing
/// Emitted when closed, following fade-out animation
signal closed
function showForm(formType, params, closedCallback) {
if (formType.status === Component.Error)
console.log(formType.errorString())
if (!params instanceof Object)
params = {app: app}
else
params.app = app
var form = formType.createObject(formContainer, params)
formContainer.data = [form]
form.canceled.connect(function(){state = "HIDDEN"; internal.callbackArgs = []})
form.completed.connect(function(){state = "HIDDEN"; internal.callbackArgs = arguments})
if (closedCallback instanceof Function)
internal.callback = closedCallback
// Notify the form that it's about to go live
form.display({})
state = "SHOWN"
}
FocusScope {
id: scope
anchors.fill: parent
// Do not let focus leave this scope while form is open
onFocusChanged: if (enabled && !focus) forceActiveFocus()
Keys.onEscapePressed: greySheet.state = "HIDDEN"
MouseArea {
id: mouseTrap
anchors.fill: parent
onClicked: {
mouse.accepted = true
greySheet.state = "HIDDEN"
}
acceptedButtons: Qt.AllButtons
}
MouseArea {
// This mouse area blocks clicks inside the form from reaching the mouseTrap
anchors.fill: formContainer
acceptedButtons: Qt.AllButtons
onClicked: mouse.accepted = true
}
Item {
id: formContainer
anchors.centerIn: parent
width: parent.width / 2
height: parent.height / 2
}
}
states: [
State {
name: "HIDDEN"
PropertyChanges {
target: greySheet
opacity: 0
enabled: false
}
StateChangeScript {
name: "preHidden"
script: {
greySheet.closing()
}
}
StateChangeScript {
name: "postHidden"
script: {
greySheet.closed()
formContainer.data = []
if (internal.callback instanceof Function)
internal.callback.apply(this, internal.callbackArgs)
internal.callback = undefined
internal.callbackArgs = []
}
}
},
State {
name: "SHOWN"
PropertyChanges {
target: greySheet
opacity: 1
enabled: true
}
StateChangeScript {
name: "preShown"
script: {
greySheet.opening()
}
}
StateChangeScript {
name: "postShown"
script: {
greySheet.opened()
}
}
}
]
transitions: [
Transition {
from: "HIDDEN"
to: "SHOWN"
SequentialAnimation {
ScriptAction { scriptName: "preShown" }
PropertyAnimation {
target: greySheet
property: "opacity"
duration: animationTime
}
ScriptAction { scriptName: "postShown" }
}
},
Transition {
from: "SHOWN"
to: "HIDDEN"
SequentialAnimation {
ScriptAction { scriptName: "preHidden" }
PropertyAnimation {
target: greySheet
property: "opacity"
duration: animationTime
}
ScriptAction { scriptName: "postHidden" }
}
}
]
QtObject {
id: internal
property var callback
property var callbackArgs
}
}

View file

@ -1,52 +0,0 @@
import QtQuick 2.5
import Graphene.Client 0.1
Flipable {
id: flipable
anchors.fill: parent
property Component frontComponent
property Component backComponent
signal display(var arg)
signal canceled(var arg)
signal completed(var arg)
property bool flipped: false
Component.onCompleted: {
back = backComponent.createObject(flipable, {app: app, enabled: Qt.binding(function(){return rotation.angle > 90})})
front = frontComponent.createObject(flipable, {app: app, enabled: Qt.binding(function(){return rotation.angle < 90})})
front.canceled.connect(function() { canceled.apply(this, arguments) })
front.completed.connect(function() {
back.display.apply(this, arguments)
flipped = true
})
back.canceled.connect(function() {
front.display.apply(this, arguments)
flipped = false
})
back.completed.connect(function() { completed.apply(this, arguments) })
}
transform: Rotation {
id: rotation
origin.x: flipable.width/2
origin.y: flipable.height/2
// set axis.y to 1 to rotate around y-axis
axis.x: 0; axis.y: 1; axis.z: 0
// the default angle
angle: 0
}
states: State {
name: "back"
PropertyChanges { target: rotation; angle: 180 }
when: flipable.flipped
}
transitions: Transition {
NumberAnimation { target: rotation; property: "angle"; duration: 500 }
}
}

View file

@ -1,68 +0,0 @@
import QtQuick 2.5
import "jdenticon/jdenticon-1.0.1.min.js" as Jdenticon
Canvas {
id: identicon
contextType: "2d"
property var name
property bool showOwnership: false
property bool fullOwnership: false
onNameChanged: requestPaint()
onPaint: {
if (name)
Jdenticon.draw(identicon, name)
else {
var context = identicon.context
if (!context) return
context.reset()
var draw_circle = function(context, x, y, radius) {
context.beginPath()
context.arc(x, y, radius, 0, 2 * Math.PI, false)
context.fillStyle = "rgba(0, 0, 0, 0.1)"
context.fill()
}
var size = Math.min(identicon.height, identicon.width)
var centerX = size / 2
var centerY = size / 2
var radius = size/15
draw_circle(context, centerX, centerY, radius)
draw_circle(context, 2*radius, 2*radius, radius)
draw_circle(context, centerX, 2*radius, radius)
draw_circle(context, size - 2*radius, 2*radius, radius)
draw_circle(context, size - 2*radius, centerY, radius)
draw_circle(context, size - 2*radius, size - 2*radius, radius)
draw_circle(context, centerX, size - 2*radius, radius)
draw_circle(context, 2*radius, size - 2*radius, radius)
draw_circle(context, 2*radius, centerY, radius)
}
}
Rectangle {
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
height: parent.height / 4
color: fullOwnership? "green" : "blue"
opacity: .6
visible: showOwnership
Text {
anchors.centerIn: parent
color: "white"
font.pixelSize: parent.height * 2/3
font.bold: true
text: fullOwnership? qsTr("FULL") : qsTr("PARTIAL")
}
TooltipArea {
text: fullOwnership? qsTr("You have full control of this account, and can use it to perform actions directly.")
: qsTr("You have partial control of this account, and can vote for it to take an action.")
}
}
}

View file

@ -1,12 +0,0 @@
pragma Singleton
import QtQuick 2.5
import QtQuick.Window 2.2
Item {
function mm(millimeters) {
return Screen.pixelDensity * millimeters
}
function cm(centimeters) {
return mm(centimeters * 10)
}
}

View file

@ -1,23 +0,0 @@
import QtQuick 2.4
import QtQuick.Controls.Private 1.0
// TooltipArea.qml
// This file contains private Qt Quick modules that might change in future versions of Qt
// Tested on: Qt 5.4.1
MouseArea {
id: _root
property string text: ""
anchors.fill: parent
hoverEnabled: _root.enabled
onExited: Tooltip.hideText()
onCanceled: Tooltip.hideText()
Timer {
interval: 1000
running: _root.enabled && _root.containsMouse && _root.text.length
onTriggered: Tooltip.showText(_root, Qt.point(_root.mouseX, _root.mouseY), _root.text)
}
}

View file

@ -1,105 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.2
import QtGraphicalEffects 1.0
import Graphene.Client 0.1
import "."
/**
* This is the form for previewing and approving a transaction prior to broadcasting it
*
* The arguments property should be populated with an Array of operations. These operations will be used to create a
* Transaction, display it, and get confirmation to sign and broadcast it. This form will populate the transaction with
* the operations and expiration details, sign it, and pass the signed transaction through the completed signal.
*/
FormBase {
id: base
readonly property alias trx: __trx
Component.onCompleted: console.log("Made a transaction confirmation form")
Component.onDestruction: console.log("Destroyed a transaction confirmation form")
onDisplay: {
trx.clearOperations()
for (var op in arg)
trx.appendOperation(arg[op])
}
Transaction {
id: __trx
}
Rectangle {
id: background
Layout.preferredHeight: childrenRect.height + Scaling.mm(4)
Layout.preferredWidth: childrenRect.width + Scaling.mm(4)
layer.enabled: true
layer.effect: DropShadow {
radius: 8.0
samples: 16
horizontalOffset: Scaling.mm(.5)
verticalOffset: Scaling.mm(.5)
source: background
color: "#80000000"
transparentBorder: true
}
// Debugging shim; disable before release
Loader {
id: delegateLoader
x: Scaling.mm(2)
y: Scaling.mm(2)
Layout.preferredWidth: Scaling.cm(10)
Component.onCompleted: setSource("TransactionDelegate.qml", {"trx": __trx, "app": base.app})
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Reloading transaction")
delegateLoader.source = ""
delegateLoader.setSource("TransactionDelegate.qml", {"trx": __trx, "app": base.app})
}
}
}
}
RowLayout {
Label {
text: qsTr("Transaction expires in")
}
ComboBox {
id: expirationSelector
model: [qsTr("five seconds"), qsTr("thirty seconds"), qsTr("a minute"), qsTr("an hour"), qsTr("a month"), qsTr("a year")]
function getExpiration() {
switch(expirationSelector.currentIndex) {
case 0: return new Date(app.model.chainTime.getTime() + 1000*5)
case 1: return new Date(app.model.chainTime.getTime() + 1000*30)
case 2: return new Date(app.model.chainTime.getTime() + 1000*60)
case 3: return new Date(app.model.chainTime.getTime() + 1000*60*60)
case 4: return new Date(app.model.chainTime.getTime() + 1000*60*60*24*30)
case 5: return new Date(app.model.chainTime.getTime() + 1000*60*60*24*365)
}
}
}
}
UnlockingFinishButtons {
app: base.app
Layout.fillWidth: true
rightButtonText: qsTr("Commit")
onLeftButtonClicked: canceled({})
onRightButtonClicked: {
if (app.wallet.isLocked)
app.wallet.unlock(passwordField.text)
else {
trx.setExpiration(expirationSelector.getExpiration())
app.signTransaction(trx)
app.model.broadcast(trx)
completed(trx)
}
}
}
}

View file

@ -1,42 +0,0 @@
import QtQuick 2.5
import QtQuick.Layouts 1.2
import Graphene.Client 0.1
import "."
ColumnLayout {
id: base
spacing: Scaling.mm(2)
property Transaction trx
property GrapheneApplication app
Text {
font.bold: true
font.pointSize: Scaling.cm(.4)
text: qsTr("Transaction Status: %1").arg(trx.statusString)
}
Repeater {
model: trx.operations
Loader {
function load() {
var source
switch (modelData.operationType) {
case OperationBase.TransferOperationType: source = "TransferOperationDelegate.qml"
}
setSource(source, {"op": modelData, "app": base.app, "width": base.width})
}
Component.onCompleted: load()
// Debugging shim; disable before release
MouseArea {
anchors.fill: item
onClicked: {
console.log("Reloading " + parent.source)
parent.source = ""
parent.load()
}
}
}
}
}

View file

@ -1,130 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
import Graphene.Client 0.1
import "."
/**
* This is the form for transferring some amount of asset from one account to another.
*/
FormBase {
id: base
/// The Account object for the sender
property alias senderAccount: senderPicker.account
/// The Account object for the receiver
property alias receiverAccount: recipientPicker.account
function operation() {
if (!finishLine.rightButtonEnabled) return app.operationBuilder.transfer(0,0,0,0, memoField.text, 0)
return app.operationBuilder.transfer(senderPicker.account.id, recipientPicker.account.id,
amountField.value * amountField.precisionAdjustment,
amountField.maxBalance.type.id, memoField.text, 0)
}
Component.onCompleted: console.log("Made a transfer form")
Component.onDestruction: console.log("Destroyed a transfer form")
AccountPicker {
id: senderPicker
// The senderPicker is really the heart of the form. Everything else in the form adjusts based on the account
// selected here. The assetField below updates to contain all assets this account has a nonzero balance in.
// The amountField updates based on the asset selected in the assetField to have the appropriate precision and
// to have a maximum value equal to the account's balance in that asset. The transfer button enables only when
// both accounts are set, and a nonzero amount is selected to be transferred.
app: base.app
Layout.fillWidth: true
Layout.minimumWidth: Scaling.cm(5)
Component.onCompleted: setFocus()
placeholderText: qsTr("Sender")
showBalance: balances? balances.reduce(function(foundIndex, balance, index) {
if (foundIndex >= 0) return foundIndex
return balance.type.symbol === assetField.currentText? index : -1
}, -1) : -1
onBalanceClicked: amountField.value = balance
}
AccountPicker {
id: recipientPicker
app: base.app
Layout.fillWidth: true
Layout.minimumWidth: Scaling.cm(5)
placeholderText: qsTr("Recipient")
layoutDirection: Qt.RightToLeft
}
RowLayout {
Layout.fillWidth: true
TextField {
id: memoField
Layout.fillWidth: true
placeholderText: qsTr("Memo")
}
CheckBox {
id: encryptMemoField
text: qsTr("Encrypt Memo")
checked: true
}
}
RowLayout {
Layout.fillWidth: true
SpinBox {
id: amountField
Layout.preferredWidth: Scaling.cm(4)
Layout.minimumWidth: Scaling.cm(1.5)
enabled: maxBalance
minimumValue: 0
maximumValue: maxBalance? maxBalance.amountReal() : 0
decimals: maxBalance? maxBalance.type.precision : 0
Keys.onReturnPressed: if (finishLine.rightButtonEnabled) finishLine.clickRight()
property Balance maxBalance: assetField.enabled && senderPicker.showBalance >= 0?
senderPicker.balances[senderPicker.showBalance] : null
property int precisionAdjustment: maxBalance? Math.pow(10, maxBalance.type.precision) : 1
// Workaround to preserve value in case form gets disabled then re-enabled
onEnabledChanged: if (!enabled) __valueBackup = value
onMaximumValueChanged: if (enabled && maximumValue > __valueBackup) value = __valueBackup
property real __valueBackup
}
ComboBox {
id: assetField
Layout.minimumWidth: Scaling.cm(1)
enabled: senderPicker.balances instanceof Array && senderPicker.balances.length > 0
model: enabled? senderPicker.balances.filter(function(balance) { return balance.amount > 0 })
.map(function(balance) { return balance.type.symbol })
: ["Asset Type"]
}
Text {
font.pixelSize: assetField.height / 2.5
text: {
if (!senderPicker.account || !amountField.maxBalance)
return ""
return qsTr("Fee:<br/>") + amountField.maxBalance.type.formatAmount(operation().fee) + " CORE"
}
}
}
UnlockingFinishButtons {
id: finishLine
app: base.app
rightButtonText: {
return !senderAccount ||
!senderAccount.isLoaded ||
senderPicker.accountControlLevel >= 1? qsTr("Transfer") : qsTr("Propose")
}
rightButtonEnabled: senderPicker.account && recipientPicker.account && senderPicker.account !== recipientPicker.account && amountField.value
requiresUnlocking: encryptMemoField.checked && memoField.text
Layout.fillWidth: true
onLeftButtonClicked: canceled({})
onRightButtonClicked: {
var op = operation()
if (encryptMemoField.checked)
op.encryptMemo(app.wallet, app.model)
completed([op])
}
}
}

View file

@ -1,59 +0,0 @@
import QtQuick 2.5
import QtQuick.Layouts 1.2
import Graphene.Client 0.1
import "."
ColumnLayout {
id: base
property TransferOperation op
property GrapheneApplication app
property Asset transferAsset: app.model.getAsset(op.amountType)
property Asset feeAsset: app.model.getAsset(op.feeType)
property Account sender: app.model.getAccount(op.sender)
property Account receiver: app.model.getAccount(op.receiver)
RowLayout {
Layout.fillWidth: true
Identicon {
height: Scaling.cm(1)
width: Scaling.cm(1)
name: sender.name
}
Text {
Layout.alignment: Qt.AlignVCenter
text: "-><br/>%1 %2".arg(transferAsset.formatAmount(op.amount)).arg(transferAsset.symbol)
horizontalAlignment: Text.Center
}
Identicon {
height: Scaling.cm(1)
width: Scaling.cm(1)
name: receiver.name
}
ColumnLayout {
Layout.fillWidth: true
Text {
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
text: qsTr("Transfer from %1 to %2").arg(sender.name).arg(receiver.name)
}
Text {
Layout.fillWidth: true
horizontalAlignment: Text.AlignRight
font.pointSize: 9
text: qsTr("Fee: %1 %2").arg(feeAsset.formatAmount(op.fee)).arg(feeAsset.symbol)
}
}
}
Text {
text: op.memo? qsTr("Memo: %1").arg(op.memoIsEncrypted && !app.wallet.isLocked? op.decryptedMemo(app.wallet, app.model)
: op.memo)
: qsTr("No memo")
color: op.memo? "black" : "grey"
font.italic: !op.memo
}
}

View file

@ -1,54 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2
import "."
import Graphene.Client 0.1
RowLayout {
property string leftButtonText: qsTr("Cancel")
property string unlockButtonText: qsTr("Unlock")
property string rightButtonText: qsTr("Finish")
property bool leftButtonEnabled: true
property bool rightButtonEnabled: true
property bool requiresUnlocking: true
property GrapheneApplication app
signal leftButtonClicked
signal rightButtonClicked
function clickLeft() { leftButton.clicked() }
function clickRight() { rightButton.clicked() }
Item { Layout.fillWidth: true }
Button {
id: leftButton
text: leftButtonText
onClicked: leftButtonClicked()
}
TextField {
id: passwordField
property bool shown: requiresUnlocking && app.wallet.isLocked
property real desiredWidth: shown? Scaling.cm(4) : 0
Layout.preferredWidth: desiredWidth
echoMode: TextInput.Password
placeholderText: qsTr("Wallet password")
visible: desiredWidth > 0
onAccepted: rightButton.clicked()
Behavior on desiredWidth { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }
}
Button {
id: rightButton
text: passwordField.shown? unlockButtonText : rightButtonText
enabled: rightButtonEnabled
onClicked: {
if (passwordField.visible)
return app.wallet.unlock(passwordField.text)
rightButtonClicked()
}
Behavior on implicitWidth { NumberAnimation {} }
}
}

View file

@ -1,18 +0,0 @@
// Jdenticon 1.0.1 | jdenticon.com | zlib licensed | (c) 2014-2015 Daniel Mester Pirttijärvi
/*Alteration to official Jdenticon code: make jdenticon() a variable in this context instead of a property of window*/
var jdenticon=function(){function h(){}function n(b,a,c,d){this.h=b;this.i=a;this.k=c;this.f=d}function u(b,a){this.e=b;this.g=a||n.q;b.beginPath()}function r(b,a,c){var d=(b="string"===typeof b?document.querySelector(b):b).getContext("2d");c=Math.min(b.width)*(1-2*(c===t?.08:c));d.save();d.clearRect(0,0,b.width,b.height);d.translate(0|(b.width-c)/2,0|(b.height-c)/2);v(d,a||b.getAttribute("data-jdenticon-hash"),c);d.restore()}function v(b,a,c){function d(b,c,d,e,g){e=e?parseInt(a.charAt(e),16):
0;c=c[parseInt(a.charAt(d),16)%c.length];var f;for(d=0;d<g.length;d++)f=new n(g[d][0]*p,g[d][1]*p,p,e++%4),f=new u(b,f),c(f,p,d),f.fill()}function e(a){if(0<=a.indexOf(k))for(var b=0;b<a.length;b++)if(0<=l.indexOf(a[b]))return!0}function g(a){b.fillStyle=m[l[a]].toString()}if(30>c)throw Error("Jdenticon cannot render identicons smaller than 30 pixels.");if(!/^[0-9a-f]{10,}$/i.test(a))throw Error("Invalid hash passed to Jdenticon.");c|=0;for(var p=2*(0|c/8),f=parseInt(a.substr(-7),16)/268435455,m=
[h.o(76,76,76),h.m(f,.6),h.o(230,230,230),h.m(f,.8),h.n(f,.4)],l=[],k,f=0;3>f;f++){k=parseInt(a.charAt(8+f),16)%m.length;if(e([0,4])||e([2,3]))k=1;l.push(k)}b.clearRect(0,0,c,c);g(0);d(b,w,2,3,[[1,0],[2,0],[2,3],[1,3],[0,1],[3,1],[3,2],[0,2]]);g(1);d(b,w,4,5,[[0,0],[3,0],[3,3],[0,3]]);g(2);d(b,y,1,null,[[1,1],[2,1],[2,2],[1,2]])}function q(){for(var b,a=document.getElementsByTagName("canvas"),c=0;c<a.length;c++)(b=a[c].getAttribute("data-jdenticon-hash"))&&r(a[c],b)}var t,x=window.jQuery;h.o=function(b,
a,c){var d=new h;d.p="rgba("+(b&255)+","+(a&255)+","+(c&255)+","+(void 0===t?1:void 0)+")";return d};h.n=function(b,a){var c=new h;c.p="hsla("+(360*b|0)+",50%,"+(100*a|0)+"%,"+(void 0===t?1:void 0)+")";return c};h.m=function(b,a){return h.n(b,1-[.95,1,1,1,.7,.8,.8][6*b+.5|0]*(1-a))};h.prototype={toString:function(){return this.p}};n.q=new n(0,0,0,0);n.prototype={j:function(b,a,c,d){var e=this.h+this.k,g=this.i+this.k;return 1===this.f?[e-a-(d||0),this.i+b]:2===this.f?[e-b-(c||0),g-a-(d||0)]:3===this.f?
[this.h+a,g-b-(c||0)]:[this.h+b,this.i+a]}};u.prototype={c:function(b,a){var c=a?-2:2,d=a?b.length-2:0,e=this.e;e.moveTo.apply(e,this.g.j(b[d],b[d+1]));for(d+=c;d<b.length&&0<=d;d+=c)e.lineTo.apply(e,this.g.j(b[d],b[d+1]));e.closePath()},d:function(b,a,c,d,e){var g=this.e;a=this.g.j(b,a,c,d);b=a[0];a=a[1];var h=c/2*.5522848,f=d/2*.5522848,m=b+c,l=a+d;c=b+c/2;var k=a+d/2;e&&(l=a,a+=d,f=-f);g.moveTo(b,k);g.bezierCurveTo(b,k-f,c-h,a,c,a);g.bezierCurveTo(c+h,a,m,k-f,m,k);g.bezierCurveTo(m,k+f,c+h,l,c,
l);g.bezierCurveTo(c-h,l,b,k+f,b,k);g.closePath()},a:function(b,a,c,d,e){this.c([b,a,b+c,a,b+c,a+d,b,a+d],e)},b:function(b,a,c,d,e,g){b=[b+c,a,b+c,a+d,b,a+d,b,a];b.splice((e||0)%4,2);this.c(b,g)},l:function(b,a,c,d,e){this.c([b+c/2,a,b+c,a+d/2,b+c/2,a+d,b,a+d/2],e)},fill:function(){this.e.fill()}};var y=[function(b,a){var c=.42*a;b.c([0,0,a,0,a,a-2*c,a-c,a,0,a])},function(b,a){var c=0|.4*a;b.b(a-c,a-2*c,c,2*c,1)},function(b,a){var c=0|a/3;b.a(c,c,a-c,a-c)},function(b,a){var c=0|.1*a,d=0|.25*a;b.a(d,
d,a-c-d,a-c-d)},function(b,a){var c=0|.15*a,d=0|.5*a;b.d(a-d-c,a-d-c,d,d)},function(b,a){var c=.1*a,d=4*c;b.a(0,0,a,a);b.c([d,d,a-c,d,d+(a-d-c)/2,a-c],!0)},function(b,a){b.b(0,0,a,a,0)},function(b,a){b.b(a/2,a/2,a/2,a/2,0)},function(b,a){b.a(0,0,a,a/2);b.a(0,a/2,a/2,a/2);b.b(a/2,a/2,a/2,a/2,1)},function(b,a){var c=0|.14*a,d=0|.35*a;b.a(0,0,a,a);b.a(d,d,a-d-c,a-d-c,!0)},function(b,a){var c=.12*a,d=3*c;b.a(0,0,a,a);b.d(d,d,a-c-d,a-c-d,!0)},function(b,a){b.b(a/2,a/2,a/2,a/2,3)},function(b,a){var c=.25*
a;b.a(0,0,a,a);b.l(c,c,a-c,a-c,!0)},function(b,a,c){var d=.4*a;a*=1.2;c||b.d(d,d,a,a)}],w=[function(b,a){b.b(0,0,a,a,0)},function(b,a){b.b(0,a/2,a,a/2,0)},function(b,a){b.l(0,0,a,a)},function(b,a){var c=a/6;b.d(c,c,a-2*c,a-2*c)}];q.drawIcon=v;q.update=r;x&&(x.fn.jdenticon=function(b,a){this.each(function(c,d){r(d,b,a)});return this});/*Alteration to official Jdenticon code: Removed call to schedule a render immediately on load*/return q}();
// The following functions were added, and are not part of the official Jdenticon code
/// @param canvas The canvas to draw on
/// @param data The data to generate an identicon for (this will be hashed to generate the identicon)
function draw(canvas, data) {
jdenticon.drawIcon(canvas.context, Crypto.sha256(data), Math.min(canvas.width, canvas.height))
}

View file

@ -1,21 +0,0 @@

Jdenticon
Copyright (c) 2014 Daniel Mester Pirttijärvi
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View file

@ -1,207 +0,0 @@
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.2
import Qt.labs.settings 1.0
import Graphene.Client 0.1
ApplicationWindow {
id: window
visible: true
width: 640
height: 480
title: qsTr("Hello World")
menuBar: MenuBar {
Menu {
title: qsTr("File")
MenuItem {
text: qsTr("&Open")
onTriggered: console.log("Open action triggered");
}
MenuItem {
text: qsTr("Exit")
onTriggered: Qt.quit();
shortcut: "Ctrl+Q"
}
}
}
statusBar: StatusBar {
Label {
anchors.right: parent.right
text: qsTr("Network: %1 Wallet: %2").arg(app.isConnected? qsTr("Connected") : qsTr("Disconnected"))
.arg(app.wallet.isLocked? qsTr("Locked") : qsTr("Unlocked"))
}
}
GrapheneApplication {
id: app
Component.onCompleted: {
var walletFile = appSettings.walletPath + "/wallet.json"
if (!wallet.open(walletFile)) {
// TODO: onboarding experience
wallet.create(walletFile, "default password", "default brain key")
}
}
}
Timer {
running: !app.isConnected
interval: 5000
repeat: true
onTriggered: app.start("ws://localhost:8090", "user", "pass")
triggeredOnStart: true
}
Settings {
id: appSettings
category: "appSettings"
property string walletPath: app.defaultDataPath()
}
Connections {
target: app
onExceptionThrown: console.log("Exception from app: " + message)
}
Column {
anchors.centerIn: parent
enabled: app.isConnected
Button {
text: "Transfer"
onClicked: {
var front = Qt.createComponent("TransferForm.qml")
// TODO: make back into a preview and confirm dialog
var back = Qt.createComponent("TransactionConfirmationForm.qml")
formBox.showForm(Qt.createComponent("FormFlipper.qml"), {frontComponent: front, backComponent: back},
function(arg) {
console.log("Closed form: " + JSON.stringify(arg))
})
}
}
TextField {
id: nameField
onAccepted: lookupNameButton.clicked()
focus: true
}
Button {
id: lookupNameButton
text: "Lookup Name"
onClicked: {
var acct = app.model.getAccount(nameField.text)
console.log(JSON.stringify(acct))
// @disable-check M126
if (acct == null)
console.log("Got back null account")
else if (acct.id >= 0)
{
console.log("ID ALREADY SET" );
console.log(JSON.stringify(acct))
}
else
{
console.log("Waiting for result...")
acct.idChanged.connect(function() {
console.log( "ID CHANGED" );
console.log(JSON.stringify(acct))
})
}
}
}
TextField {
id: accountIdField
onAccepted: lookupAccountIdButton.clicked()
focus: true
}
Button {
id: lookupAccountIdButton
text: "Lookup Account ID"
onClicked: {
var acct = app.model.getAccount(parseInt(accountIdField.text))
console.log(JSON.stringify(acct))
// @disable-check M126
if (acct == null)
console.log("Got back null account")
else if ( !(parseInt(acct.name) <= 0) )
console.log(JSON.stringify(acct))
else {
console.log("Waiting for result...")
acct.nameChanged.connect(function() {
console.log(JSON.stringify(acct))
})
}
}
}
TextField {
id: assetIdField
onAccepted: lookupassetIdButton.clicked()
focus: true
}
Button {
id: lookupassetIdButton
text: "Lookup Asset ID"
onClicked: {
var acct = app.model.getAsset(parseInt(assetIdField.text))
console.log(JSON.stringify(acct))
// @disable-check M126
if (acct == null)
console.log("Got back null asset")
else if ( !(parseInt(acct.name) <= 0) )
{
console.log(JSON.stringify(acct))
}
else
{
console.log("Waiting for result...")
acct.nameChanged.connect(function() {
console.log(JSON.stringify(acct))
})
}
}
}
TextField {
id: passwordField
echoMode: TextInput.Password
}
Button {
text: app.wallet.isLocked? "Unlock wallet" : "Lock wallet"
onClicked: {
if (app.wallet.isLocked)
app.wallet.unlock(passwordField.text)
else
app.wallet.lock()
}
}
TextField {
id: keyField
placeholderText: "Private key"
}
TextField {
id: keyLabelField
placeholderText: "Key label"
}
Button {
text: "Import key"
enabled: !app.wallet.isLocked && keyField.text && keyLabelField.text
onClicked: app.wallet.importPrivateKey(keyField.text, keyLabelField.text)
}
}
FormBox {
id: formBox
anchors.fill: parent
z: 10
}
// This Settings is only for geometry -- it doesn't get an id. See appSettings for normal settings
Settings {
category: "windowGeometry"
property alias x: window.x
property alias y: window.y
property alias width: window.width
property alias height: window.height
}
}

View file

@ -1,19 +0,0 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>FormBase.qml</file>
<file>TransferForm.qml</file>
<file>TransactionDelegate.qml</file>
<file>TransferOperationDelegate.qml</file>
<file>TransactionConfirmationForm.qml</file>
<file>FormBox.qml</file>
<file>FormFlipper.qml</file>
<file>UnlockingFinishButtons.qml</file>
<file>Scaling.qml</file>
<file>Identicon.qml</file>
<file>AccountPicker.qml</file>
<file>TooltipArea.qml</file>
<file>jdenticon/jdenticon-1.0.1.min.js</file>
</qresource>
</RCC>

View file

@ -1 +0,0 @@
singleton Scaling Scaling.qml