Merge branch 'master' of github.com:cryptonomex/graphene
This commit is contained in:
commit
fa9b71a463
23 changed files with 287 additions and 131 deletions
|
|
@ -15,6 +15,8 @@
|
||||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
* 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.
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
#include <graphene/app/api.hpp>
|
#include <graphene/app/api.hpp>
|
||||||
#include <graphene/app/api_access.hpp>
|
#include <graphene/app/api_access.hpp>
|
||||||
#include <graphene/app/application.hpp>
|
#include <graphene/app/application.hpp>
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ namespace graphene { namespace chain {
|
||||||
void memo_data::set_message(const fc::ecc::private_key& priv, const fc::ecc::public_key& pub,
|
void memo_data::set_message(const fc::ecc::private_key& priv, const fc::ecc::public_key& pub,
|
||||||
const string& msg, uint64_t custom_nonce)
|
const string& msg, uint64_t custom_nonce)
|
||||||
{
|
{
|
||||||
if( from != public_key_type() )
|
if( priv != fc::ecc::private_key() && public_key_type(pub) != public_key_type() )
|
||||||
{
|
{
|
||||||
|
from = priv.get_public_key();
|
||||||
|
to = pub;
|
||||||
if( custom_nonce == 0 )
|
if( custom_nonce == 0 )
|
||||||
{
|
{
|
||||||
uint64_t entropy = fc::sha224::hash(fc::ecc::private_key::generate())._hash[0];
|
uint64_t entropy = fc::sha224::hash(fc::ecc::private_key::generate())._hash[0];
|
||||||
|
|
|
||||||
|
|
@ -208,18 +208,24 @@ void verify_authority( const vector<operation>& ops, const flat_set<public_key_t
|
||||||
s.approved_by.insert( id );
|
s.approved_by.insert( id );
|
||||||
|
|
||||||
for( const auto& auth : other )
|
for( const auto& auth : other )
|
||||||
|
{
|
||||||
GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, "Missing Authority", ("auth",auth)("sigs",sigs) );
|
GRAPHENE_ASSERT( s.check_authority(&auth), tx_missing_other_auth, "Missing Authority", ("auth",auth)("sigs",sigs) );
|
||||||
|
}
|
||||||
|
|
||||||
// fetch all of the top level authorities
|
// fetch all of the top level authorities
|
||||||
for( auto id : required_active )
|
for( auto id : required_active )
|
||||||
|
{
|
||||||
GRAPHENE_ASSERT( s.check_authority(id) ||
|
GRAPHENE_ASSERT( s.check_authority(id) ||
|
||||||
s.check_authority(get_owner(id)),
|
s.check_authority(get_owner(id)),
|
||||||
tx_missing_active_auth, "Missing Active Authority ${id}", ("id",id)("auth",*get_active(id))("owner",*get_owner(id)) );
|
tx_missing_active_auth, "Missing Active Authority ${id}", ("id",id)("auth",*get_active(id))("owner",*get_owner(id)) );
|
||||||
|
}
|
||||||
|
|
||||||
for( auto id : required_owner )
|
for( auto id : required_owner )
|
||||||
|
{
|
||||||
GRAPHENE_ASSERT( owner_approvals.find(id) != owner_approvals.end() ||
|
GRAPHENE_ASSERT( owner_approvals.find(id) != owner_approvals.end() ||
|
||||||
s.check_authority(get_owner(id)),
|
s.check_authority(get_owner(id)),
|
||||||
tx_missing_other_auth, "Missing Owner Authority ${id}", ("id",id)("auth",*get_owner(id)) );
|
tx_missing_other_auth, "Missing Owner Authority ${id}", ("id",id)("auth",*get_owner(id)) );
|
||||||
|
}
|
||||||
|
|
||||||
FC_ASSERT( !s.remove_unused_signatures(), "Unnecessary signatures detected" );
|
FC_ASSERT( !s.remove_unused_signatures(), "Unnecessary signatures detected" );
|
||||||
} FC_CAPTURE_AND_RETHROW( (ops)(sigs) ) }
|
} FC_CAPTURE_AND_RETHROW( (ops)(sigs) ) }
|
||||||
|
|
@ -230,7 +236,9 @@ flat_set<public_key_type> signed_transaction::get_signature_keys()const
|
||||||
auto d = digest();
|
auto d = digest();
|
||||||
flat_set<public_key_type> result;
|
flat_set<public_key_type> result;
|
||||||
for( const auto& sig : signatures )
|
for( const auto& sig : signatures )
|
||||||
|
{
|
||||||
FC_ASSERT( result.insert( fc::ecc::public_key(sig,d) ).second, "Duplicate Signature detected" );
|
FC_ASSERT( result.insert( fc::ecc::public_key(sig,d) ).second, "Duplicate Signature detected" );
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
} FC_CAPTURE_AND_RETHROW() }
|
} FC_CAPTURE_AND_RETHROW() }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ namespace graphene { namespace db {
|
||||||
* @brief abstract base class for accessing objects indexed in various ways.
|
* @brief abstract base class for accessing objects indexed in various ways.
|
||||||
*
|
*
|
||||||
* All indexes assume that there exists an object ID space that will grow
|
* All indexes assume that there exists an object ID space that will grow
|
||||||
* for ever in a seqential manner. These IDs are used to identify the
|
* forever in a seqential manner. These IDs are used to identify the
|
||||||
* index, type, and instance of the object.
|
* index, type, and instance of the object.
|
||||||
*
|
*
|
||||||
* Items in an index can only be modified via a call to modify and
|
* Items in an index can only be modified via a call to modify and
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit e9eeb3300c59bca19b8c79c14ed85226d2262c63
|
Subproject commit a31f0f503df4d70ebb5960803c7092dc170bee92
|
||||||
|
|
@ -2983,8 +2983,10 @@ blind_confirmation wallet_api::blind_transfer_help( string from_key_or_label,
|
||||||
if( broadcast )
|
if( broadcast )
|
||||||
{
|
{
|
||||||
for( const auto& out : confirm.outputs )
|
for( const auto& out : confirm.outputs )
|
||||||
|
{
|
||||||
try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){}
|
try { receive_blind_transfer( out.confirmation_receipt, from_key_or_label, "" ); } catch ( ... ){}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return confirm;
|
return confirm;
|
||||||
} FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) }
|
} FC_CAPTURE_AND_RETHROW( (from_key_or_label)(to_key_or_label)(amount_in)(symbol)(broadcast)(confirm) ) }
|
||||||
|
|
@ -3070,8 +3072,10 @@ blind_confirmation wallet_api::transfer_to_blind( string from_account_id_or_name
|
||||||
if( broadcast )
|
if( broadcast )
|
||||||
{
|
{
|
||||||
for( const auto& out : confirm.outputs )
|
for( const auto& out : confirm.outputs )
|
||||||
|
{
|
||||||
try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){}
|
try { receive_blind_transfer( out.confirmation_receipt, "@"+from_account.name, "from @"+from_account.name ); } catch ( ... ){}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return confirm;
|
return confirm;
|
||||||
} FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) }
|
} FC_CAPTURE_AND_RETHROW( (from_account_id_or_name)(asset_symbol)(to_amounts) ) }
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ public:
|
||||||
|
|
||||||
void update(const account_balance_object& balance);
|
void update(const account_balance_object& balance);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Anything greater than 1.0 means full authority.
|
* Anything greater than 1.0 means full authority.
|
||||||
* Anything between (0 and 1.0) means partial authority
|
* Anything between (0 and 1.0) means partial authority
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,13 @@
|
||||||
|
|
||||||
#include <QVariant>
|
#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)
|
void Asset::update(const graphene::chain::asset_object& asset)
|
||||||
{
|
{
|
||||||
if (asset.id.instance() != id())
|
if (asset.id.instance() != id())
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,8 @@ public:
|
||||||
power *= 10;
|
power *= 10;
|
||||||
return power;
|
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);
|
void update(const graphene::chain::asset_object& asset);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "ChainDataModel.hpp"
|
#include "ChainDataModel.hpp"
|
||||||
#include "Wallet.hpp"
|
#include "Wallet.hpp"
|
||||||
#include "Operations.hpp"
|
#include "Operations.hpp"
|
||||||
|
#include "Transaction.hpp"
|
||||||
|
|
||||||
#include <graphene/app/api.hpp>
|
#include <graphene/app/api.hpp>
|
||||||
|
|
||||||
|
|
@ -74,6 +75,11 @@ void GrapheneApplication::start(QString apiurl, QString user, QString pass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transaction* GrapheneApplication::createTransaction() const
|
||||||
|
{
|
||||||
|
return new Transaction;
|
||||||
|
}
|
||||||
|
|
||||||
Q_SLOT void GrapheneApplication::execute(const std::function<void()>& func)const
|
Q_SLOT void GrapheneApplication::execute(const std::function<void()>& func)const
|
||||||
{
|
{
|
||||||
func();
|
func();
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ class websocket_client;
|
||||||
|
|
||||||
class ChainDataModel;
|
class ChainDataModel;
|
||||||
class OperationBuilder;
|
class OperationBuilder;
|
||||||
|
class OperationBase;
|
||||||
|
class Transaction;
|
||||||
class Wallet;
|
class Wallet;
|
||||||
class GrapheneApplication : public QObject {
|
class GrapheneApplication : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
@ -62,6 +64,9 @@ public:
|
||||||
return m_isConnected;
|
return m_isConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience method to get a Transaction in QML. Caller takes ownership of the new Transaction.
|
||||||
|
Q_INVOKABLE Transaction* createTransaction() const;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void exceptionThrown(QString message);
|
void exceptionThrown(QString message);
|
||||||
void loginFailed();
|
void loginFailed();
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@
|
||||||
TransferOperation* OperationBuilder::transfer(ObjectId sender, ObjectId receiver, qint64 amount,
|
TransferOperation* OperationBuilder::transfer(ObjectId sender, ObjectId receiver, qint64 amount,
|
||||||
ObjectId amountType, QString memo, ObjectId feeType)
|
ObjectId amountType, QString memo, ObjectId feeType)
|
||||||
{
|
{
|
||||||
static fc::ecc::private_key dummyPrivate = fc::ecc::private_key::generate();
|
try {
|
||||||
static fc::ecc::public_key dummyPublic = fc::ecc::private_key::generate().get_public_key();
|
|
||||||
TransferOperation* op = new TransferOperation;
|
TransferOperation* op = new TransferOperation;
|
||||||
op->setSender(sender);
|
op->setSender(sender);
|
||||||
op->setReceiver(receiver);
|
op->setReceiver(receiver);
|
||||||
|
|
@ -15,8 +14,30 @@ TransferOperation* OperationBuilder::transfer(ObjectId sender, ObjectId receiver
|
||||||
op->setMemo(memo);
|
op->setMemo(memo);
|
||||||
op->setFeeType(feeType);
|
op->setFeeType(feeType);
|
||||||
auto feeParameters = model.global_properties().parameters.current_fees->get<graphene::chain::transfer_operation>();
|
auto feeParameters = model.global_properties().parameters.current_fees->get<graphene::chain::transfer_operation>();
|
||||||
op->operation().memo = graphene::chain::memo_data();
|
|
||||||
op->operation().memo->set_message(dummyPrivate, dummyPublic, memo.toStdString());
|
|
||||||
op->setFee(op->operation().calculate_fee(feeParameters).value);
|
op->setFee(op->operation().calculate_fee(feeParameters).value);
|
||||||
return op;
|
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;
|
||||||
|
QString memo = QString::fromStdString(m_op.memo->get_message({}, {}));
|
||||||
|
while (memo.endsWith('\0'))
|
||||||
|
memo.chop(1);
|
||||||
|
return memo;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ class TransferOperation : public OperationBase {
|
||||||
Q_PROPERTY(QString memo READ memo WRITE setMemo NOTIFY memoChanged)
|
Q_PROPERTY(QString memo READ memo WRITE setMemo NOTIFY memoChanged)
|
||||||
|
|
||||||
graphene::chain::transfer_operation m_op;
|
graphene::chain::transfer_operation m_op;
|
||||||
QString m_memo;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TransferOperation(){}
|
TransferOperation(){}
|
||||||
|
|
@ -57,9 +56,9 @@ public:
|
||||||
ObjectId receiver() const { return m_op.to.instance.value; }
|
ObjectId receiver() const { return m_op.to.instance.value; }
|
||||||
qint64 amount() const { return m_op.amount.amount.value; }
|
qint64 amount() const { return m_op.amount.amount.value; }
|
||||||
ObjectId amountType() const { return m_op.amount.asset_id.instance.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, and does not get stored in the
|
/// This does not deal with encrypted memos. The memo stored here is unencrypted. The encryption step must be
|
||||||
/// underlying graphene operation. The encryption and storage steps must be handled elsewhere.
|
/// performed elsewhere.
|
||||||
QString memo() const { return m_memo; }
|
QString memo() const;
|
||||||
|
|
||||||
const graphene::chain::transfer_operation& operation() const { return m_op; }
|
const graphene::chain::transfer_operation& operation() const { return m_op; }
|
||||||
graphene::chain::transfer_operation& operation() { return m_op; }
|
graphene::chain::transfer_operation& operation() { return m_op; }
|
||||||
|
|
@ -101,14 +100,9 @@ public Q_SLOTS:
|
||||||
m_op.amount.asset_id = arg;
|
m_op.amount.asset_id = arg;
|
||||||
Q_EMIT amountTypeChanged();
|
Q_EMIT amountTypeChanged();
|
||||||
}
|
}
|
||||||
/// This does not deal with encrypted memos. The memo stored here is unencrypted, and does not get stored in the
|
/// This does not deal with encrypted memos. The memo stored here is unencrypted. The encryption step must be
|
||||||
/// underlying graphene operation. The encryption and storage steps must be handled elsewhere.
|
/// performed elsewhere.
|
||||||
void setMemo(QString memo) {
|
void setMemo(QString memo);
|
||||||
if (memo == m_memo)
|
|
||||||
return;
|
|
||||||
m_memo = memo;
|
|
||||||
Q_EMIT memoChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void feeChanged();
|
void feeChanged();
|
||||||
|
|
@ -136,7 +130,7 @@ public:
|
||||||
OperationBuilder(ChainDataModel& model, QObject* parent = nullptr)
|
OperationBuilder(ChainDataModel& model, QObject* parent = nullptr)
|
||||||
: QObject(parent), model(model){}
|
: QObject(parent), model(model){}
|
||||||
|
|
||||||
Q_INVOKABLE TransferOperation* transfer(ObjectId sender, ObjectId receiver,
|
Q_INVOKABLE TransferOperation* transfer(ObjectId sender, ObjectId receiver, qint64 amount,
|
||||||
qint64 amount, ObjectId amountType, QString memo, ObjectId feeType);
|
ObjectId amountType, QString memo, ObjectId feeType);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,11 @@ OperationBase* Transaction::operationAt(int index) const {
|
||||||
|
|
||||||
void Transaction::appendOperation(OperationBase* op)
|
void Transaction::appendOperation(OperationBase* op)
|
||||||
{
|
{
|
||||||
|
if (op == nullptr)
|
||||||
|
{
|
||||||
|
qWarning("Unable to append null operation to transaction");
|
||||||
|
return;
|
||||||
|
}
|
||||||
op->setParent(this);
|
op->setParent(this);
|
||||||
m_transaction.operations.push_back(op->genericOperation());
|
m_transaction.operations.push_back(op->genericOperation());
|
||||||
Q_EMIT operationsChanged();
|
Q_EMIT operationsChanged();
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,6 @@ private:
|
||||||
Q_PROPERTY(Status status READ status WRITE setStatus NOTIFY statusChanged)
|
Q_PROPERTY(Status status READ status WRITE setStatus NOTIFY statusChanged)
|
||||||
Q_PROPERTY(QQmlListProperty<OperationBase> operations READ operations NOTIFY operationsChanged)
|
Q_PROPERTY(QQmlListProperty<OperationBase> operations READ operations NOTIFY operationsChanged)
|
||||||
|
|
||||||
Status m_status;
|
Status m_status = Unbroadcasted;
|
||||||
graphene::chain::transaction m_transaction;
|
graphene::chain::transaction m_transaction;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,10 @@ QML_DECLARE_TYPE(Crypto)
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
fc::thread::current().set_name( "main" );
|
#ifndef NDEBUG
|
||||||
|
QQmlDebuggingEnabler enabler;
|
||||||
|
#endif
|
||||||
|
fc::thread::current().set_name("main");
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
app.setApplicationName("Graphene Client");
|
app.setApplicationName("Graphene Client");
|
||||||
app.setOrganizationDomain("cryptonomex.org");
|
app.setOrganizationDomain("cryptonomex.org");
|
||||||
|
|
@ -28,6 +31,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
qRegisterMetaType<std::function<void()>>();
|
qRegisterMetaType<std::function<void()>>();
|
||||||
qRegisterMetaType<ObjectId>();
|
qRegisterMetaType<ObjectId>();
|
||||||
|
qRegisterMetaType<QList<OperationBase*>>();
|
||||||
|
|
||||||
qmlRegisterType<Asset>("Graphene.Client", 0, 1, "Asset");
|
qmlRegisterType<Asset>("Graphene.Client", 0, 1, "Asset");
|
||||||
qmlRegisterType<Balance>("Graphene.Client", 0, 1, "Balance");
|
qmlRegisterType<Balance>("Graphene.Client", 0, 1, "Balance");
|
||||||
|
|
@ -49,7 +53,6 @@ int main(int argc, char *argv[])
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||||
#else
|
#else
|
||||||
QQmlDebuggingEnabler enabler;
|
|
||||||
engine.load(QUrl(QStringLiteral("qml/main.qml")));
|
engine.load(QUrl(QStringLiteral("qml/main.qml")));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
37
programs/light_client/qml/FormBase.qml
Normal file
37
programs/light_client/qml/FormBase.qml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,6 +33,8 @@ Rectangle {
|
||||||
form.completed.connect(function(){state = "HIDDEN"; internal.callbackArgs = arguments})
|
form.completed.connect(function(){state = "HIDDEN"; internal.callbackArgs = arguments})
|
||||||
if (closedCallback instanceof Function)
|
if (closedCallback instanceof Function)
|
||||||
internal.callback = closedCallback
|
internal.callback = closedCallback
|
||||||
|
// Notify the form that it's about to go live
|
||||||
|
form.display({})
|
||||||
state = "SHOWN"
|
state = "SHOWN"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,10 @@ Flipable {
|
||||||
|
|
||||||
property Component frontComponent
|
property Component frontComponent
|
||||||
property Component backComponent
|
property Component backComponent
|
||||||
property GrapheneApplication app
|
|
||||||
signal canceled
|
signal display(var arg)
|
||||||
signal completed
|
signal canceled(var arg)
|
||||||
|
signal completed(var arg)
|
||||||
|
|
||||||
property bool flipped: false
|
property bool flipped: false
|
||||||
|
|
||||||
|
|
@ -19,13 +20,11 @@ Flipable {
|
||||||
front = frontComponent.createObject(flipable, {app: app, enabled: Qt.binding(function(){return !flipped})})
|
front = frontComponent.createObject(flipable, {app: app, enabled: Qt.binding(function(){return !flipped})})
|
||||||
front.canceled.connect(function() { canceled.apply(this, arguments) })
|
front.canceled.connect(function() { canceled.apply(this, arguments) })
|
||||||
front.completed.connect(function() {
|
front.completed.connect(function() {
|
||||||
if (back.hasOwnProperty("arguments"))
|
back.display.apply(this, arguments)
|
||||||
back.arguments = arguments
|
|
||||||
flipped = true
|
flipped = true
|
||||||
})
|
})
|
||||||
back.canceled.connect(function() {
|
back.canceled.connect(function() {
|
||||||
if (front.hasOwnProperty("arguments"))
|
front.display.apply(this, arguments)
|
||||||
front.arguments = arguments
|
|
||||||
flipped = false
|
flipped = false
|
||||||
})
|
})
|
||||||
back.completed.connect(function() { completed.apply(this, arguments) })
|
back.completed.connect(function() { completed.apply(this, arguments) })
|
||||||
|
|
@ -35,8 +34,10 @@ Flipable {
|
||||||
id: rotation
|
id: rotation
|
||||||
origin.x: flipable.width/2
|
origin.x: flipable.width/2
|
||||||
origin.y: flipable.height/2
|
origin.y: flipable.height/2
|
||||||
axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis
|
// set axis.y to 1 to rotate around y-axis
|
||||||
angle: 0 // the default angle
|
axis.x: 0; axis.y: 1; axis.z: 0
|
||||||
|
// the default angle
|
||||||
|
angle: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
states: State {
|
states: State {
|
||||||
|
|
|
||||||
67
programs/light_client/qml/TransactionConfirmationForm.qml
Normal file
67
programs/light_client/qml/TransactionConfirmationForm.qml
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
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 "."
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
|
||||||
|
property Transaction trx
|
||||||
|
|
||||||
|
Component.onCompleted: console.log("Made a transaction confirmation form")
|
||||||
|
Component.onDestruction: console.log("Destroyed a transaction confirmation form")
|
||||||
|
|
||||||
|
onDisplay: {
|
||||||
|
trx = app.createTransaction()
|
||||||
|
console.log(JSON.stringify(arg))
|
||||||
|
for (var op in arg)
|
||||||
|
trx.appendOperation(arg[op])
|
||||||
|
console.log(JSON.stringify(trx))
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: transactionDelegate
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: Scaling.cm(10)
|
||||||
|
height: childrenRect.height + Scaling.cm(1)
|
||||||
|
radius: Scaling.mm(3)
|
||||||
|
color: "#EEEEEE"
|
||||||
|
border.width: Scaling.mm(.25)
|
||||||
|
border.color: "black"
|
||||||
|
|
||||||
|
Column {
|
||||||
|
y: Scaling.cm(.5)
|
||||||
|
x: y
|
||||||
|
width: parent.width - Scaling.cm(1)
|
||||||
|
Repeater {
|
||||||
|
model: trx.operations
|
||||||
|
Label {
|
||||||
|
property Asset transferAsset: app.model.getAsset(modelData.amountType)
|
||||||
|
property Asset feeAsset: app.model.getAsset(modelData.feeType)
|
||||||
|
text: qsTr("Transfer %1 %2 from %3 to %4\nFee: %5 %6").arg(transferAsset.formatAmount(modelData.amount))
|
||||||
|
.arg(transferAsset.symbol)
|
||||||
|
.arg(app.model.getAccount(modelData.sender).name)
|
||||||
|
.arg(app.model.getAccount(modelData.receiver).name)
|
||||||
|
.arg(feeAsset.formatAmount(modelData.fee))
|
||||||
|
.arg(feeAsset.symbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loader {
|
||||||
|
sourceComponent: trx && Array.prototype.slice.call(trx.operations).length > 0? transactionDelegate : undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,13 +10,8 @@ import "."
|
||||||
/**
|
/**
|
||||||
* This is the form for transferring some amount of asset from one account to another.
|
* This is the form for transferring some amount of asset from one account to another.
|
||||||
*/
|
*/
|
||||||
Rectangle {
|
FormBase {
|
||||||
id: root
|
id: base
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
property GrapheneApplication app
|
|
||||||
signal canceled
|
|
||||||
signal completed(TransferOperation op)
|
|
||||||
|
|
||||||
/// The Account object for the sender
|
/// The Account object for the sender
|
||||||
property alias senderAccount: senderPicker.account
|
property alias senderAccount: senderPicker.account
|
||||||
|
|
@ -34,11 +29,6 @@ Rectangle {
|
||||||
Component.onCompleted: console.log("Made a transfer form")
|
Component.onCompleted: console.log("Made a transfer form")
|
||||||
Component.onDestruction: console.log("Destroyed a transfer form")
|
Component.onDestruction: console.log("Destroyed a transfer form")
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width - Scaling.cm(2)
|
|
||||||
spacing: Scaling.mm(5)
|
|
||||||
|
|
||||||
AccountPicker {
|
AccountPicker {
|
||||||
id: senderPicker
|
id: senderPicker
|
||||||
// The senderPicker is really the heart of the form. Everything else in the form adjusts based on the account
|
// The senderPicker is really the heart of the form. Everything else in the form adjusts based on the account
|
||||||
|
|
@ -47,7 +37,7 @@ Rectangle {
|
||||||
// to have a maximum value equal to the account's balance in that asset. The transfer button enables only when
|
// 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.
|
// both accounts are set, and a nonzero amount is selected to be transferred.
|
||||||
|
|
||||||
app: root.app
|
app: base.app
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumWidth: Scaling.cm(5)
|
Layout.minimumWidth: Scaling.cm(5)
|
||||||
Component.onCompleted: setFocus()
|
Component.onCompleted: setFocus()
|
||||||
|
|
@ -60,7 +50,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
AccountPicker {
|
AccountPicker {
|
||||||
id: recipientPicker
|
id: recipientPicker
|
||||||
app: root.app
|
app: base.app
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumWidth: Scaling.cm(5)
|
Layout.minimumWidth: Scaling.cm(5)
|
||||||
placeholderText: qsTr("Recipient")
|
placeholderText: qsTr("Recipient")
|
||||||
|
|
@ -116,8 +106,7 @@ Rectangle {
|
||||||
id: transferButton
|
id: transferButton
|
||||||
text: qsTr("Transfer")
|
text: qsTr("Transfer")
|
||||||
enabled: senderPicker.account && recipientPicker.account && senderPicker.account !== recipientPicker.account && amountField.value
|
enabled: senderPicker.account && recipientPicker.account && senderPicker.account !== recipientPicker.account && amountField.value
|
||||||
onClicked: completed(operation)
|
onClicked: completed([operation()])
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ ApplicationWindow {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
var front = Qt.createComponent("TransferForm.qml")
|
var front = Qt.createComponent("TransferForm.qml")
|
||||||
// TODO: make back into a preview and confirm dialog
|
// TODO: make back into a preview and confirm dialog
|
||||||
var back = Qt.createComponent("TransferForm.qml")
|
var back = Qt.createComponent("TransactionConfirmationForm.qml")
|
||||||
formBox.showForm(Qt.createComponent("FormFlipper.qml"), {frontComponent: front, backComponent: back},
|
formBox.showForm(Qt.createComponent("FormFlipper.qml"), {frontComponent: front, backComponent: back},
|
||||||
function() {
|
function() {
|
||||||
console.log("Closed form")
|
console.log("Closed form")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>main.qml</file>
|
<file>main.qml</file>
|
||||||
|
<file>FormBase.qml</file>
|
||||||
<file>TransferForm.qml</file>
|
<file>TransferForm.qml</file>
|
||||||
|
<file>TransactionConfirmationForm.qml</file>
|
||||||
<file>FormBox.qml</file>
|
<file>FormBox.qml</file>
|
||||||
<file>FormFlipper.qml</file>
|
<file>FormFlipper.qml</file>
|
||||||
<file>Scaling.qml</file>
|
<file>Scaling.qml</file>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue