[GUI] Initial work on Transaction type
This commit is contained in:
parent
bfa3e71393
commit
8cab71c584
7 changed files with 228 additions and 45 deletions
|
|
@ -27,6 +27,7 @@ add_executable(light_client
|
||||||
Asset.cpp
|
Asset.cpp
|
||||||
Account.cpp
|
Account.cpp
|
||||||
Balance.cpp
|
Balance.cpp
|
||||||
|
Transaction.cpp
|
||||||
main.cpp ${QML_QRC} ${QML})
|
main.cpp ${QML_QRC} ${QML})
|
||||||
|
|
||||||
if (CMAKE_VERSION VERSION_LESS 3.0)
|
if (CMAKE_VERSION VERSION_LESS 3.0)
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,21 @@
|
||||||
|
|
||||||
#include <fc/smart_ref_impl.hpp>
|
#include <fc/smart_ref_impl.hpp>
|
||||||
|
|
||||||
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();
|
static fc::ecc::private_key dummyPrivate = fc::ecc::private_key::generate();
|
||||||
static fc::ecc::public_key dummyPublic = fc::ecc::private_key::generate().get_public_key();
|
static fc::ecc::public_key dummyPublic = fc::ecc::private_key::generate().get_public_key();
|
||||||
TransferOperation op;
|
TransferOperation* op = new TransferOperation;
|
||||||
op.setSender(sender);
|
op->setSender(sender);
|
||||||
op.setReceiver(receiver);
|
op->setReceiver(receiver);
|
||||||
op.setAmount(amount);
|
op->setAmount(amount);
|
||||||
op.setAmountType(amountType);
|
op->setAmountType(amountType);
|
||||||
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 = graphene::chain::memo_data();
|
||||||
op.operation().memo->set_message(dummyPrivate, dummyPublic, memo.toStdString());
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,51 +5,128 @@
|
||||||
#include <graphene/chain/protocol/transfer.hpp>
|
#include <graphene/chain/protocol/transfer.hpp>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QtQml>
|
||||||
|
|
||||||
class TransferOperation {
|
class OperationBase : public QObject {
|
||||||
Q_GADGET
|
Q_OBJECT
|
||||||
Q_PROPERTY(qint64 fee READ fee)
|
Q_PROPERTY(OperationType operationType READ operationType CONSTANT STORED false)
|
||||||
Q_PROPERTY(ObjectId feeType READ feeType)
|
|
||||||
Q_PROPERTY(ObjectId sender READ sender WRITE setSender)
|
public:
|
||||||
Q_PROPERTY(ObjectId receiver READ receiver WRITE setReceiver)
|
enum OperationType {
|
||||||
Q_PROPERTY(qint64 amount READ amount WRITE setAmount)
|
TransferOperationType = graphene::chain::operation::tag<graphene::chain::transfer_operation>::value
|
||||||
Q_PROPERTY(ObjectId amountType READ amountType WRITE setAmountType)
|
};
|
||||||
Q_PROPERTY(QString memo READ memo WRITE setMemo)
|
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)
|
||||||
|
|
||||||
graphene::chain::transfer_operation m_op;
|
graphene::chain::transfer_operation m_op;
|
||||||
QString m_memo;
|
QString m_memo;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE qint64 fee() const { return m_op.fee.amount.value; }
|
TransferOperation(){}
|
||||||
Q_INVOKABLE void setFee(qint64 fee) { m_op.fee.amount = fee; }
|
TransferOperation(const graphene::chain::transfer_operation& op)
|
||||||
|
: m_op(op) {}
|
||||||
|
|
||||||
Q_INVOKABLE ObjectId feeType() const { return m_op.fee.asset_id.instance.value; }
|
virtual OperationBase::OperationType operationType() const override {
|
||||||
Q_INVOKABLE void setFeeType(ObjectId feeType) { m_op.fee.asset_id = feeType; }
|
return OperationBase::TransferOperationType;
|
||||||
|
}
|
||||||
Q_INVOKABLE ObjectId sender() const { return m_op.from.instance.value; }
|
virtual graphene::chain::operation genericOperation() const override {
|
||||||
Q_INVOKABLE void setSender(ObjectId sender) { m_op.from = sender; }
|
return m_op;
|
||||||
|
}
|
||||||
Q_INVOKABLE ObjectId receiver() const { return m_op.to.instance.value; }
|
|
||||||
Q_INVOKABLE void setReceiver(ObjectId receiver) { m_op.to = receiver; }
|
|
||||||
|
|
||||||
Q_INVOKABLE qint64 amount() const { return m_op.amount.amount.value; }
|
|
||||||
Q_INVOKABLE void setAmount(qint64 amount) { m_op.amount.amount = amount; }
|
|
||||||
|
|
||||||
Q_INVOKABLE ObjectId amountType() const { return m_op.amount.asset_id.instance.value; }
|
|
||||||
Q_INVOKABLE void setAmountType(ObjectId assetType) { m_op.amount.asset_id = assetType; }
|
|
||||||
|
|
||||||
|
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, and does not get stored in the
|
/// This does not deal with encrypted memos. The memo stored here is unencrypted, and does not get stored in the
|
||||||
/// underlying graphene operation. The encryption and storage steps must be handled elsewhere.
|
/// underlying graphene operation. The encryption and storage steps must be handled elsewhere.
|
||||||
Q_INVOKABLE QString memo() const { return m_memo; }
|
QString memo() const { return m_memo; }
|
||||||
/// This does not deal with encrypted memos. The memo stored here is unencrypted, and does not get stored in the
|
|
||||||
/// underlying graphene operation. The encryption and storage steps must be handled elsewhere.
|
|
||||||
Q_INVOKABLE void setMemo(QString memo) { m_memo = memo; }
|
|
||||||
|
|
||||||
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; }
|
||||||
};
|
|
||||||
Q_DECLARE_METATYPE(TransferOperation)
|
|
||||||
|
|
||||||
|
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, and does not get stored in the
|
||||||
|
/// underlying graphene operation. The encryption and storage steps must be handled elsewhere.
|
||||||
|
void setMemo(QString memo) {
|
||||||
|
if (memo == m_memo)
|
||||||
|
return;
|
||||||
|
m_memo = memo;
|
||||||
|
Q_EMIT memoChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
class OperationBuilder : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
@ -59,6 +136,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, ObjectId amountType, QString memo, ObjectId feeType);
|
qint64 amount, ObjectId amountType, QString memo, ObjectId feeType);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
48
programs/light_client/Transaction.cpp
Normal file
48
programs/light_client/Transaction.cpp
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
m_transaction.operations.push_back(op->genericOperation());
|
||||||
|
Q_EMIT operationsChanged();
|
||||||
|
}
|
||||||
50
programs/light_client/Transaction.hpp
Normal file
50
programs/light_client/Transaction.hpp
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||||
|
|
||||||
|
#include <graphene/chain/protocol/transaction.hpp>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQmlListProperty>
|
||||||
|
|
||||||
|
class OperationBase;
|
||||||
|
class Transaction : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Status { Unbroadcasted, Pending, Complete, Failed };
|
||||||
|
Q_ENUM(Status);
|
||||||
|
|
||||||
|
Status status() const { return m_status; }
|
||||||
|
QQmlListProperty<OperationBase> operations();
|
||||||
|
|
||||||
|
OperationBase* operationAt(int index) const;
|
||||||
|
void appendOperation(OperationBase* op);
|
||||||
|
int operationCount() const {
|
||||||
|
return m_transaction.operations.size();
|
||||||
|
}
|
||||||
|
void clearOperations() {
|
||||||
|
m_transaction.operations.clear();
|
||||||
|
Q_EMIT operationsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setStatus(Status status)
|
||||||
|
{
|
||||||
|
if (status == m_status)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_status = status;
|
||||||
|
emit statusChanged(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void statusChanged(Status status);
|
||||||
|
void operationsChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_PROPERTY(Status status READ status WRITE setStatus NOTIFY statusChanged)
|
||||||
|
Q_PROPERTY(QQmlListProperty<OperationBase> operations READ operations NOTIFY operationsChanged)
|
||||||
|
|
||||||
|
Status m_status;
|
||||||
|
graphene::chain::transaction m_transaction;
|
||||||
|
};
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "GrapheneApplication.hpp"
|
#include "GrapheneApplication.hpp"
|
||||||
#include "ChainDataModel.hpp"
|
#include "ChainDataModel.hpp"
|
||||||
|
#include "Transaction.hpp"
|
||||||
#include "Operations.hpp"
|
#include "Operations.hpp"
|
||||||
#include "Balance.hpp"
|
#include "Balance.hpp"
|
||||||
|
|
||||||
|
|
@ -27,13 +28,17 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
qRegisterMetaType<std::function<void()>>();
|
qRegisterMetaType<std::function<void()>>();
|
||||||
qRegisterMetaType<ObjectId>();
|
qRegisterMetaType<ObjectId>();
|
||||||
qRegisterMetaType<TransferOperation>();
|
|
||||||
|
|
||||||
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");
|
||||||
qmlRegisterType<Account>("Graphene.Client", 0, 1, "Account");
|
qmlRegisterType<Account>("Graphene.Client", 0, 1, "Account");
|
||||||
qmlRegisterType<ChainDataModel>("Graphene.Client", 0, 1, "DataModel");
|
qmlRegisterType<ChainDataModel>("Graphene.Client", 0, 1, "DataModel");
|
||||||
qmlRegisterType<GrapheneApplication>("Graphene.Client", 0, 1, "GrapheneApplication");
|
qmlRegisterType<GrapheneApplication>("Graphene.Client", 0, 1, "GrapheneApplication");
|
||||||
|
qmlRegisterType<Transaction>("Graphene.Client", 0, 1, "Transaction");
|
||||||
|
|
||||||
|
qmlRegisterInterface<OperationBase>("OperationBase");
|
||||||
|
qmlRegisterType<TransferOperation>("Graphene.Client", 0, 1, "TransferOperation");
|
||||||
|
|
||||||
qmlRegisterUncreatableType<OperationBuilder>("Graphene.Client", 0, 1, "OperationBuilder",
|
qmlRegisterUncreatableType<OperationBuilder>("Graphene.Client", 0, 1, "OperationBuilder",
|
||||||
QStringLiteral("OperationBuilder cannot be created from QML"));
|
QStringLiteral("OperationBuilder cannot be created from QML"));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ Rectangle {
|
||||||
|
|
||||||
var op = app.operationBuilder.transfer(0, 0, amountField.value * precisionAdjustment,
|
var op = app.operationBuilder.transfer(0, 0, amountField.value * precisionAdjustment,
|
||||||
balance.type.id, memoField.text, 0)
|
balance.type.id, memoField.text, 0)
|
||||||
|
|
||||||
return qsTr("Fee:<br/>") + op.fee / precisionAdjustment + " CORE"
|
return qsTr("Fee:<br/>") + op.fee / precisionAdjustment + " CORE"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue