diff --git a/CMakeLists.txt b/CMakeLists.txt index 1fe8685..49ae587 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,7 +292,9 @@ target_link_libraries(scratchpad set( LIBRARY_SRC_FILES + src/daemon/MoneroDaemonModel.cpp src/daemon/MoneroDaemon.cpp + src/wallet/MoneroWalletModel.cpp src/wallet/MoneroWallet.cpp src/utils/MoneroUtils.cpp ) diff --git a/src/daemon/MoneroDaemon.cpp b/src/daemon/MoneroDaemon.cpp index 67f4ba6..aae0a7f 100644 --- a/src/daemon/MoneroDaemon.cpp +++ b/src/daemon/MoneroDaemon.cpp @@ -1,284 +1,9 @@ #include "MoneroDaemon.h" -#include "utils/MoneroUtils.h" -#include "include_base_utils.h" -#include "common/util.h" - /** - * Public interface for libmonero-cpp library. + * Public library intereface. */ namespace monero { - // ------------------------- INITIALIZE CONSTANTS --------------------------- - - const string MoneroTx::DEFAULT_PAYMENT_ID = string("0000000000000000"); - - // -------------------------- MODEL SERIALIZATION --------------------------- - - string SerializableStruct::serialize() const { - return MoneroUtils::serialize(toPropertyTree()); - } - - boost::property_tree::ptree MoneroBlockHeader::toPropertyTree() const { - //cout << "MoneroBlockHeader::toPropertyTree(block)" << endl; - boost::property_tree::ptree node; - if (id != boost::none) node.put("id", *id); - if (height != boost::none) node.put("height", *height); - if (timestamp != boost::none) node.put("timestamp", *timestamp); - if (size != boost::none) node.put("size", *size); - if (weight != boost::none) node.put("weight", *weight); - if (longTermWeight != boost::none) node.put("longTermWeight", *longTermWeight); - if (depth != boost::none) node.put("depth", *depth); - if (difficulty != boost::none) node.put("difficulty", *difficulty); - if (cumulativeDifficulty != boost::none) node.put("cumulativeDifficulty", *height); - if (majorVersion != boost::none) node.put("majorVersion", *majorVersion); - if (minorVersion != boost::none) node.put("minorVersion", *minorVersion); - if (nonce != boost::none) node.put("nonce", *nonce); - if (coinbaseTxId != boost::none) node.put("coinbaseTxId", *coinbaseTxId); - if (numTxs != boost::none) node.put("numTxs", *numTxs); - if (orphanStatus != boost::none) node.put("orphanStatus", *orphanStatus); - if (prevId != boost::none) node.put("prevId", *prevId); - if (reward != boost::none) node.put("reward", *reward); - if (powHash != boost::none) node.put("powHash", *powHash); - return node; - } - - boost::property_tree::ptree MoneroBlock::toPropertyTree() const { - //cout << "MoneroBlock::toPropertyTree(block)" << endl; - boost::property_tree::ptree node = MoneroBlockHeader::toPropertyTree(); - if (hex != boost::none) node.put("hex", *hex); - if (coinbaseTx != boost::none) node.add_child("coinbaseTx", (*coinbaseTx)->toPropertyTree()); - node.add_child("txs", MoneroUtils::toPropertyTree(txs)); - // TODO: handle txIds - return node; - } - - boost::property_tree::ptree MoneroTx::toPropertyTree() const { - //cout << "MoneroTx::txToPropertyTree(tx)" << endl; - boost::property_tree::ptree node; - if (id != boost::none) node.put("id", *id); - if (version != boost::none) node.put("version", *version); - if (isCoinbase != boost::none) node.put("isCoinbase", *isCoinbase); - if (paymentId != boost::none) node.put("paymentId", *paymentId); - if (fee != boost::none) node.put("fee", *fee); - if (mixin != boost::none) node.put("mixin", *mixin); - if (doNotRelay != boost::none) node.put("doNotRelay", *doNotRelay); - if (isRelayed != boost::none) node.put("isRelayed", *isRelayed); - if (isConfirmed != boost::none) node.put("isConfirmed", *isConfirmed); - if (inTxPool != boost::none) node.put("inTxPool", *inTxPool); - if (numConfirmations != boost::none) node.put("numConfirmations", *numConfirmations); - if (unlockTime != boost::none) node.put("unlockTime", *unlockTime); - if (lastRelayedTimestamp != boost::none) node.put("lastRelayedTimestamp", *lastRelayedTimestamp); - if (receivedTimestamp != boost::none) node.put("receivedTimestamp", *receivedTimestamp); - if (isDoubleSpend != boost::none) node.put("isDoubleSpend", *isDoubleSpend); - if (key != boost::none) node.put("key", *key); - if (fullHex != boost::none) node.put("fullHex", *fullHex); - if (prunedHex != boost::none) node.put("prunedHex", *prunedHex); - if (prunableHex != boost::none) node.put("prunableHex", *prunableHex); - if (prunableHash != boost::none) node.put("prunableHash", *prunableHash); - if (size != boost::none) node.put("size", *size); - if (weight != boost::none) node.put("weight", *weight); - if (!vins.empty()) throw runtime_error("not implemented"); - if (!vouts.empty()) throw runtime_error("not implemented"); - if (!outputIndices.empty()) throw runtime_error("not implemented"); - if (metadata != boost::none) node.put("metadata", *metadata); - if (commonTxSets != boost::none) throw runtime_error("not implemented"); - if (!extra.empty()) throw runtime_error("not implemented"); - if (rctSignatures != boost::none) throw runtime_error("not implemented"); - if (rctSigPrunable != boost::none) throw runtime_error("not implemented"); - if (isKeptByBlock != boost::none) node.put("isKeptByBlock", *isKeptByBlock); - if (isFailed != boost::none) node.put("isFailed", *isFailed); - if (lastFailedHeight != boost::none) node.put("lastFailedHeight", *lastFailedHeight); - if (lastFailedId != boost::none) node.put("lastFailedId", *lastFailedId); - if (maxUsedBlockHeight != boost::none) node.put("maxUsedBlockHeight", *maxUsedBlockHeight); - if (maxUsedBlockId != boost::none) node.put("maxUsedBlockId", *maxUsedBlockId); - if (!signatures.empty()) throw runtime_error("not implemented"); - return node; - } - - // -------------------------- MODEL IMPLEMENTATION -------------------------- - - void MoneroBlockHeader::merge(const shared_ptr& header) { - cout << "MoneroBlockHeader::merge()" << endl; -// if (this == &header) return; -// id = MoneroUtils::reconcile(id, header.id); -// height = MoneroUtils::reconcile(height, header.height, boost::none, boost::none, true); // height can increase -// timestamp = MoneroUtils::reconcile(timestamp, header.timestamp, boost::none, boost::none, true); // timestamp can increase -// size = MoneroUtils::reconcile(size, header.size); -// weight = MoneroUtils::reconcile(weight, header.weight); -// longTermWeight = MoneroUtils::reconcile(longTermWeight, header.longTermWeight); -// depth = MoneroUtils::reconcile(depth, header.depth); -// difficulty = MoneroUtils::reconcile(difficulty, header.difficulty); -// cumulativeDifficulty = MoneroUtils::reconcile(cumulativeDifficulty, header.cumulativeDifficulty); -// majorVersion = MoneroUtils::reconcile(majorVersion, header.majorVersion); -// minorVersion = MoneroUtils::reconcile(minorVersion, header.minorVersion); -// nonce = MoneroUtils::reconcile(nonce, header.nonce); -// coinbaseTxId = MoneroUtils::reconcile(coinbaseTxId, header.coinbaseTxId); -// numTxs = MoneroUtils::reconcile(numTxs, header.numTxs); -// orphanStatus = MoneroUtils::reconcile(orphanStatus, header.orphanStatus); -// prevId = MoneroUtils::reconcile(prevId, header.prevId); -// reward = MoneroUtils::reconcile(reward, header.reward); -// powHash = MoneroUtils::reconcile(powHash, header.powHash); - } - - void MoneroBlock::merge(const shared_ptr& block) { - cout << "MoneroBlock::merge()" << endl; -// if (this == &block) return; -// -// // merge header fields -// MoneroBlockHeader::merge(block); -// -// // merge coinbase tx -// if (coinbaseTx == boost::none) coinbaseTx = block.coinbaseTx; -// else if (block.coinbaseTx != boost::none) (**coinbaseTx).merge(**block.coinbaseTx); -// -// // merge non-coinbase txs -// if (txs.empty()) txs = block.txs; -// else if (!block.txs.empty()) { -// for (const shared_ptr& thatTx : block.txs) { -// bool found = false; -// for (const shared_ptr& thisTx : txs) { -// if (thatTx->id == thisTx->id) { -// thisTx->merge(*thatTx); -// found = true; -// break; -// } -// } -// if (!found) txs.push_back(thatTx); -// } -// } -// if (!txs.empty()) { -// for (const shared_ptr& tx : txs) { -// tx->block = shared_ptr(this); -// } -// } -// -// // merge other fields -// hex = MoneroUtils::reconcile(hex, block.hex); -// //txIds = MoneroUtils::reconcile(txIds, block.txIds); // TODO: implement -// cout << "Returning from block::merge()!" << endl; - } - - void MoneroTx::merge(const shared_ptr& self, const shared_ptr& other) { - cout << "MoneroTx::merge()" << endl; -// if (this == &tx) return; -// id = MoneroUtils::reconcile(id, tx.id); -// version = MoneroUtils::reconcile(version, tx.version); -// paymentId = MoneroUtils::reconcile(paymentId, tx.paymentId); -// fee = MoneroUtils::reconcile(fee, tx.fee); -// mixin = MoneroUtils::reconcile(mixin, tx.mixin); -// isConfirmed = MoneroUtils::reconcile(isConfirmed, tx.isConfirmed); -// doNotRelay = MoneroUtils::reconcile(doNotRelay, tx.doNotRelay); -// isRelayed = MoneroUtils::reconcile(isRelayed, tx.isRelayed); -// isDoubleSpend = MoneroUtils::reconcile(isDoubleSpend, tx.isDoubleSpend); -// key = MoneroUtils::reconcile(key, tx.key); -// fullHex = MoneroUtils::reconcile(fullHex, tx.fullHex); -// prunedHex = MoneroUtils::reconcile(prunedHex, tx.prunedHex); -// prunableHex = MoneroUtils::reconcile(prunableHex, tx.prunableHex); -// prunableHash = MoneroUtils::reconcile(prunableHash, tx.prunableHash); -// size = MoneroUtils::reconcile(size, tx.size); -// weight = MoneroUtils::reconcile(weight, tx.weight); -// //outputIndices = MoneroUtils::reconcile(outputIndices, tx.outputIndices); // TODO -// metadata = MoneroUtils::reconcile(metadata, tx.metadata); -// commonTxSets = MoneroUtils::reconcile(commonTxSets, tx.commonTxSets); -// //extra = MoneroUtils::reconcile(extra, tx.extra); // TODO -// rctSignatures = MoneroUtils::reconcile(rctSignatures, tx.rctSignatures); -// rctSigPrunable = MoneroUtils::reconcile(rctSigPrunable, tx.rctSigPrunable); -// isKeptByBlock = MoneroUtils::reconcile(isKeptByBlock, tx.isKeptByBlock); -// isFailed = MoneroUtils::reconcile(isFailed, tx.isFailed); -// lastFailedHeight = MoneroUtils::reconcile(lastFailedHeight, tx.lastFailedHeight); -// lastFailedId = MoneroUtils::reconcile(lastFailedId, tx.lastFailedId); -// maxUsedBlockHeight = MoneroUtils::reconcile(maxUsedBlockHeight, tx.maxUsedBlockHeight); -// maxUsedBlockId = MoneroUtils::reconcile(maxUsedBlockId, tx.maxUsedBlockId); -// //signatures = MoneroUtils::reconcile(signatures, tx.signatures); // TODO -// unlockTime = MoneroUtils::reconcile(unlockTime, tx.unlockTime); -// numConfirmations = MoneroUtils::reconcile(numConfirmations, tx.numConfirmations); -// -// // merge vins -// if (!tx.vins.empty()) { -// for (const shared_ptr& merger : tx.vins) { -// bool merged = false; -// merger->tx = shared_ptr(this); // TODO: can this cause this* to be deleted prematurely? -// for (const shared_ptr& mergee : vins) { -// if ((*mergee->keyImage)->hex == (*merger->keyImage)->hex) { -// mergee->merge(*merger); -// merged = true; -// break; -// } -// } -// if (!merged) vins.push_back(merger); -// } -// } -// -// // merge vouts -// if (!tx.vouts.empty()) { -// for (const shared_ptr& vout : tx.vouts) vout->tx = shared_ptr(this); -// if (vouts.empty()) vouts = tx.vouts; -// else { -// -// // determine if key images present -// int numKeyImages = 0; -// for (const shared_ptr vout : vouts) { -// if (vout->keyImage != boost::none) { -// if ((*vout->keyImage)->hex == boost::none) throw runtime_error("Key image hex cannot be null"); -// numKeyImages++; -// } -// } -// for (const shared_ptr& vout : tx.vouts) { -// if (vout->keyImage != boost::none) { -// if ((*vout->keyImage)->hex == boost::none) throw runtime_error("Key image hex cannot be null"); -// numKeyImages++; -// } -// } -// if (numKeyImages != 0 && vouts.size() + tx.vouts.size() != numKeyImages) throw runtime_error("Some vouts have a key image and some do not"); -// -// // merge by key images -// if (numKeyImages > 0) { -// for (const shared_ptr& merger : tx.vouts) { -// bool merged = false; -// for (const shared_ptr& mergee : vouts) { -// if ((*mergee->keyImage)->hex == (*merger->keyImage)->hex) { -// mergee->merge(*merger); -// merged = true; -// break; -// } -// } -// if (!merged) vouts.push_back(merger); -// } -// } -// -// // merge by position -// else { -// if (vouts.size() != tx.vouts.size()) throw runtime_error("Vout sizes are different"); -// for (int i = 0; i < tx.vouts.size(); i++) { -// vouts.at(i)->merge(*tx.vouts.at(i)); -// } -// } -// } -// } -// -// // handle unrelayed -> relayed -> confirmed -// if (*isConfirmed) { -// inTxPool = false; -// receivedTimestamp = boost::none; -// lastRelayedTimestamp = boost::none; -// } else { -// inTxPool = MoneroUtils::reconcile(inTxPool, tx.inTxPool, boost::none, true, boost::none); // unrelayed -> tx pool -// receivedTimestamp = MoneroUtils::reconcile(receivedTimestamp, tx.receivedTimestamp, boost::none, boost::none, false); // take earliest receive time -// lastRelayedTimestamp = MoneroUtils::reconcile(lastRelayedTimestamp, tx.lastRelayedTimestamp, boost::none, boost::none, true); // take latest relay time -// } - } - - void MoneroKeyImage::merge(const shared_ptr& self, const shared_ptr& other) { - cout << "MoneroKeyImage::merge()" << endl; - throw runtime_error("Not implemented"); - } - - void MoneroOutput::merge(const shared_ptr& self, const shared_ptr& other) { - cout << "MoneroOutput::merge()" << endl; - throw runtime_error("Not implemented"); - } - - // ---------------------------- PRIVATE HELPERS ----------------------------- - + // TODO: MoneroDaemon class implementation } diff --git a/src/daemon/MoneroDaemon.h b/src/daemon/MoneroDaemon.h index 7fde5d3..f24c294 100644 --- a/src/daemon/MoneroDaemon.h +++ b/src/daemon/MoneroDaemon.h @@ -1,209 +1,13 @@ #pragma once -#include -#include +#include "MoneroDaemonModel.h" using namespace std; /** - * Public interface for libmonero-cpp library. + * Public library interface. */ namespace monero { - /** - * Base struct which can be serialized. - * - * TODO: move this to MoneroUtils? - */ - struct SerializableStruct { - - /** - * Serializes the struct to a json string. - * - * @return the struct serialized to a json string - */ - string serialize() const; - - /** - * Convert the struct to a property tree. - * - * @return the converted property tree - */ - virtual boost::property_tree::ptree toPropertyTree() const = 0; - }; - - /** - * Enumerates Monero network types. - */ - enum MoneroNetworkType : uint8_t { - MAINNET = 0, - TESTNET, - STAGENET - }; - - /** - * Models a connection to a daemon. - * - * TODO: switch to boost::optional - */ - struct MoneroRpcConnection { - string uri; - string username; - string password; - //MoneroRpcConnection() : uri(""), username(""), password("")) {} - MoneroRpcConnection(const string& uri = "", const string& username = "", const string& password = "") : uri(uri), username(username), password(password) {} - }; - - // forward declarations - struct MoneroTx; - struct MoneroOutput; - - /** - * Models a Monero block header which contains information about the block. - */ - struct MoneroBlockHeader : public SerializableStruct { - boost::optional id; - boost::optional height; - boost::optional timestamp; - boost::optional size; - boost::optional weight; - boost::optional longTermWeight; - boost::optional depth; - boost::optional difficulty; - boost::optional cumulativeDifficulty; - boost::optional majorVersion; - boost::optional minorVersion; - boost::optional nonce; - boost::optional coinbaseTxId; - boost::optional numTxs; - boost::optional orphanStatus; - boost::optional prevId; - boost::optional reward; - boost::optional powHash; - - boost::property_tree::ptree toPropertyTree() const; - void merge(const shared_ptr& header); - }; - - /** - * Models a Monero block in the blockchain. - */ - struct MoneroBlock : public MoneroBlockHeader { - boost::optional hex; - boost::optional> coinbaseTx; - vector> txs; - vector txIds; - - boost::property_tree::ptree toPropertyTree() const; - void merge(const shared_ptr& block); - }; - - /** - * Models a Monero transaction on the blockchain. - */ - struct MoneroTx : public SerializableStruct { - static const string DEFAULT_PAYMENT_ID; // default payment id "0000000000000000" - boost::optional> block; - boost::optional id; - boost::optional version; - boost::optional isCoinbase; - boost::optional paymentId; - boost::optional fee; - boost::optional mixin; - boost::optional doNotRelay; - boost::optional isRelayed; - boost::optional isConfirmed; - boost::optional inTxPool; - boost::optional numConfirmations; - boost::optional unlockTime; - boost::optional lastRelayedTimestamp; - boost::optional receivedTimestamp; - boost::optional isDoubleSpend; - boost::optional key; - boost::optional fullHex; - boost::optional prunedHex; - boost::optional prunableHex; - boost::optional prunableHash; - boost::optional size; - boost::optional weight; - vector> vins; - vector> vouts; - vector outputIndices; - boost::optional metadata; - boost::optional commonTxSets; - vector extra; - boost::optional rctSignatures; // TODO: implement - boost::optional rctSigPrunable; // TODO: implement - boost::optional isKeptByBlock; - boost::optional isFailed; - boost::optional lastFailedHeight; - boost::optional lastFailedId; - boost::optional maxUsedBlockHeight; - boost::optional maxUsedBlockId; - vector signatures; - - boost::property_tree::ptree toPropertyTree() const; - boost::optional getHeight() const { - if (block == boost::none) return boost::none; - return *((*block)->height); - } - void merge(const shared_ptr& self, const shared_ptr& other); - }; - - /** - * Models a Monero key image. - */ - struct MoneroKeyImage { - boost::optional hex; - boost::optional signature; - - void merge(const shared_ptr& self, const shared_ptr& other); - }; - - /** - * Models a Monero transaction output. - */ - struct MoneroOutput { - shared_ptr tx; - boost::optional> keyImage; - boost::optional amount; - boost::optional index; - vector ringOutputIndices; - boost::optional stealthPublicKey; - - void merge(const shared_ptr& self, const shared_ptr& other); - }; + // TODO: MoneroDaemon class definition } - -//// --------------------------- MODEL SERIALIZATION ---------------------------- -// -//BOOST_CLASS_VERSION(monero::MoneroTx, 0) -//BOOST_CLASS_VERSION(monero::MoneroOutput, 0) -//BOOST_CLASS_VERSION(monero::MoneroKeyImage, 0) -// -//namespace boost { -// namespace serialization { -// template -// inline void serialize(Archive &a, monero::MoneroTx& x, const boost::serialization::version_type ver) -// { -//// a & x.m_L; -//// a & x.m_R; -// } -// -// template -// inline void serialize(Archive &a, monero::MoneroOutput& x, const boost::serialization::version_type ver) -// { -//// a & x.m_signer; -//// a & x.m_LR; -//// a & x.m_partial_key_images; -// } -// -// template -// inline void serialize(Archive &a, monero::MoneroKeyImage& x, const boost::serialization::version_type ver) -// { -//// a & x.m_signer; -//// a & x.m_LR; -//// a & x.m_partial_key_images; -// } -// } // namepsace serialization -//} // namespace boost diff --git a/src/daemon/MoneroDaemonModel.cpp b/src/daemon/MoneroDaemonModel.cpp new file mode 100644 index 0000000..144a5bd --- /dev/null +++ b/src/daemon/MoneroDaemonModel.cpp @@ -0,0 +1,289 @@ +#include "MoneroDaemonModel.h" + +#include "utils/MoneroUtils.h" +#include "include_base_utils.h" +#include "common/util.h" + +/** + * Public library interface. + */ +namespace monero { + + // ------------------------- INITIALIZE CONSTANTS --------------------------- + + const string MoneroTx::DEFAULT_PAYMENT_ID = string("0000000000000000"); + + // -------------------------- MODEL SERIALIZATION --------------------------- + + string SerializableStruct::serialize() const { + return MoneroUtils::serialize(toPropertyTree()); + } + + boost::property_tree::ptree MoneroBlockHeader::toPropertyTree() const { + //cout << "MoneroBlockHeader::toPropertyTree(block)" << endl; + boost::property_tree::ptree node; + if (id != boost::none) node.put("id", *id); + if (height != boost::none) node.put("height", *height); + if (timestamp != boost::none) node.put("timestamp", *timestamp); + if (size != boost::none) node.put("size", *size); + if (weight != boost::none) node.put("weight", *weight); + if (longTermWeight != boost::none) node.put("longTermWeight", *longTermWeight); + if (depth != boost::none) node.put("depth", *depth); + if (difficulty != boost::none) node.put("difficulty", *difficulty); + if (cumulativeDifficulty != boost::none) node.put("cumulativeDifficulty", *height); + if (majorVersion != boost::none) node.put("majorVersion", *majorVersion); + if (minorVersion != boost::none) node.put("minorVersion", *minorVersion); + if (nonce != boost::none) node.put("nonce", *nonce); + if (coinbaseTxId != boost::none) node.put("coinbaseTxId", *coinbaseTxId); + if (numTxs != boost::none) node.put("numTxs", *numTxs); + if (orphanStatus != boost::none) node.put("orphanStatus", *orphanStatus); + if (prevId != boost::none) node.put("prevId", *prevId); + if (reward != boost::none) node.put("reward", *reward); + if (powHash != boost::none) node.put("powHash", *powHash); + return node; + } + + boost::property_tree::ptree MoneroBlock::toPropertyTree() const { + //cout << "MoneroBlock::toPropertyTree(block)" << endl; + boost::property_tree::ptree node = MoneroBlockHeader::toPropertyTree(); + if (hex != boost::none) node.put("hex", *hex); + if (coinbaseTx != boost::none) node.add_child("coinbaseTx", (*coinbaseTx)->toPropertyTree()); + node.add_child("txs", MoneroUtils::toPropertyTree(txs)); + // TODO: handle txIds + return node; + } + + boost::property_tree::ptree MoneroTx::toPropertyTree() const { + //cout << "MoneroTx::txToPropertyTree(tx)" << endl; + boost::property_tree::ptree node; + if (id != boost::none) node.put("id", *id); + if (version != boost::none) node.put("version", *version); + if (isCoinbase != boost::none) node.put("isCoinbase", *isCoinbase); + if (paymentId != boost::none) node.put("paymentId", *paymentId); + if (fee != boost::none) node.put("fee", *fee); + if (mixin != boost::none) node.put("mixin", *mixin); + if (doNotRelay != boost::none) node.put("doNotRelay", *doNotRelay); + if (isRelayed != boost::none) node.put("isRelayed", *isRelayed); + if (isConfirmed != boost::none) node.put("isConfirmed", *isConfirmed); + if (inTxPool != boost::none) node.put("inTxPool", *inTxPool); + if (numConfirmations != boost::none) node.put("numConfirmations", *numConfirmations); + if (unlockTime != boost::none) node.put("unlockTime", *unlockTime); + if (lastRelayedTimestamp != boost::none) node.put("lastRelayedTimestamp", *lastRelayedTimestamp); + if (receivedTimestamp != boost::none) node.put("receivedTimestamp", *receivedTimestamp); + if (isDoubleSpend != boost::none) node.put("isDoubleSpend", *isDoubleSpend); + if (key != boost::none) node.put("key", *key); + if (fullHex != boost::none) node.put("fullHex", *fullHex); + if (prunedHex != boost::none) node.put("prunedHex", *prunedHex); + if (prunableHex != boost::none) node.put("prunableHex", *prunableHex); + if (prunableHash != boost::none) node.put("prunableHash", *prunableHash); + if (size != boost::none) node.put("size", *size); + if (weight != boost::none) node.put("weight", *weight); + if (!vins.empty()) throw runtime_error("not implemented"); + if (!vouts.empty()) throw runtime_error("not implemented"); + if (!outputIndices.empty()) throw runtime_error("not implemented"); + if (metadata != boost::none) node.put("metadata", *metadata); + if (commonTxSets != boost::none) throw runtime_error("not implemented"); + if (!extra.empty()) throw runtime_error("not implemented"); + if (rctSignatures != boost::none) throw runtime_error("not implemented"); + if (rctSigPrunable != boost::none) throw runtime_error("not implemented"); + if (isKeptByBlock != boost::none) node.put("isKeptByBlock", *isKeptByBlock); + if (isFailed != boost::none) node.put("isFailed", *isFailed); + if (lastFailedHeight != boost::none) node.put("lastFailedHeight", *lastFailedHeight); + if (lastFailedId != boost::none) node.put("lastFailedId", *lastFailedId); + if (maxUsedBlockHeight != boost::none) node.put("maxUsedBlockHeight", *maxUsedBlockHeight); + if (maxUsedBlockId != boost::none) node.put("maxUsedBlockId", *maxUsedBlockId); + if (!signatures.empty()) throw runtime_error("not implemented"); + return node; + } + + // -------------------------- MODEL IMPLEMENTATION -------------------------- + + void MoneroBlockHeader::merge(const shared_ptr& self, const shared_ptr& other) { + cout << "MoneroBlockHeader::merge()" << endl; + if (this != &*self) throw runtime_error("this != self"); + if (self == other) return; + id = MoneroUtils::reconcile(id, other->id); + height = MoneroUtils::reconcile(height, other->height, boost::none, boost::none, true); // height can increase + timestamp = MoneroUtils::reconcile(timestamp, other->timestamp, boost::none, boost::none, true); // timestamp can increase + size = MoneroUtils::reconcile(size, other->size); + weight = MoneroUtils::reconcile(weight, other->weight); + longTermWeight = MoneroUtils::reconcile(longTermWeight, other->longTermWeight); + depth = MoneroUtils::reconcile(depth, other->depth); + difficulty = MoneroUtils::reconcile(difficulty, other->difficulty); + cumulativeDifficulty = MoneroUtils::reconcile(cumulativeDifficulty, other->cumulativeDifficulty); + majorVersion = MoneroUtils::reconcile(majorVersion, other->majorVersion); + minorVersion = MoneroUtils::reconcile(minorVersion, other->minorVersion); + nonce = MoneroUtils::reconcile(nonce, other->nonce); + coinbaseTxId = MoneroUtils::reconcile(coinbaseTxId, other->coinbaseTxId); + numTxs = MoneroUtils::reconcile(numTxs, other->numTxs); + orphanStatus = MoneroUtils::reconcile(orphanStatus, other->orphanStatus); + prevId = MoneroUtils::reconcile(prevId, other->prevId); + reward = MoneroUtils::reconcile(reward, other->reward); + powHash = MoneroUtils::reconcile(powHash, other->powHash); + } + + void MoneroBlock::merge(const shared_ptr& self, const shared_ptr& other) { + cout << "MoneroBlock::merge()" << endl; + if (this != &*self) throw runtime_error("this != self"); + if (self == other) return; + + // merge header fields + MoneroBlockHeader::merge(self, other); + + // convert other to MoneroBlock* + MoneroBlock* otherBlock = static_cast(&*other); + + // merge coinbase tx + if (coinbaseTx == boost::none) coinbaseTx = otherBlock->coinbaseTx; + else if (otherBlock->coinbaseTx != boost::none) (**coinbaseTx).merge(*coinbaseTx, *otherBlock->coinbaseTx); + + // merge non-coinbase txs + if (txs.empty()) txs = otherBlock->txs; + else if (!otherBlock->txs.empty()) { + for (const shared_ptr& thatTx : otherBlock->txs) { + bool found = false; + for (const shared_ptr& thisTx : txs) { + if (thatTx->id == thisTx->id) { + thisTx->merge(thisTx, thatTx); + found = true; + break; + } + } + if (!found) txs.push_back(thatTx); + } + } + if (!txs.empty()) { + for (const shared_ptr& tx : txs) { + tx->block = self; + } + } + + // merge other fields + hex = MoneroUtils::reconcile(hex, otherBlock->hex); + //txIds = MoneroUtils::reconcile(txIds, otherBlock->txIds); // TODO: implement + cout << "Returning from block::merge()!" << endl; + } + + void MoneroTx::merge(const shared_ptr& self, const shared_ptr& other) { + cout << "MoneroTx::merge()" << endl; +// if (this == &tx) return; +// id = MoneroUtils::reconcile(id, tx.id); +// version = MoneroUtils::reconcile(version, tx.version); +// paymentId = MoneroUtils::reconcile(paymentId, tx.paymentId); +// fee = MoneroUtils::reconcile(fee, tx.fee); +// mixin = MoneroUtils::reconcile(mixin, tx.mixin); +// isConfirmed = MoneroUtils::reconcile(isConfirmed, tx.isConfirmed); +// doNotRelay = MoneroUtils::reconcile(doNotRelay, tx.doNotRelay); +// isRelayed = MoneroUtils::reconcile(isRelayed, tx.isRelayed); +// isDoubleSpend = MoneroUtils::reconcile(isDoubleSpend, tx.isDoubleSpend); +// key = MoneroUtils::reconcile(key, tx.key); +// fullHex = MoneroUtils::reconcile(fullHex, tx.fullHex); +// prunedHex = MoneroUtils::reconcile(prunedHex, tx.prunedHex); +// prunableHex = MoneroUtils::reconcile(prunableHex, tx.prunableHex); +// prunableHash = MoneroUtils::reconcile(prunableHash, tx.prunableHash); +// size = MoneroUtils::reconcile(size, tx.size); +// weight = MoneroUtils::reconcile(weight, tx.weight); +// //outputIndices = MoneroUtils::reconcile(outputIndices, tx.outputIndices); // TODO +// metadata = MoneroUtils::reconcile(metadata, tx.metadata); +// commonTxSets = MoneroUtils::reconcile(commonTxSets, tx.commonTxSets); +// //extra = MoneroUtils::reconcile(extra, tx.extra); // TODO +// rctSignatures = MoneroUtils::reconcile(rctSignatures, tx.rctSignatures); +// rctSigPrunable = MoneroUtils::reconcile(rctSigPrunable, tx.rctSigPrunable); +// isKeptByBlock = MoneroUtils::reconcile(isKeptByBlock, tx.isKeptByBlock); +// isFailed = MoneroUtils::reconcile(isFailed, tx.isFailed); +// lastFailedHeight = MoneroUtils::reconcile(lastFailedHeight, tx.lastFailedHeight); +// lastFailedId = MoneroUtils::reconcile(lastFailedId, tx.lastFailedId); +// maxUsedBlockHeight = MoneroUtils::reconcile(maxUsedBlockHeight, tx.maxUsedBlockHeight); +// maxUsedBlockId = MoneroUtils::reconcile(maxUsedBlockId, tx.maxUsedBlockId); +// //signatures = MoneroUtils::reconcile(signatures, tx.signatures); // TODO +// unlockTime = MoneroUtils::reconcile(unlockTime, tx.unlockTime); +// numConfirmations = MoneroUtils::reconcile(numConfirmations, tx.numConfirmations); +// +// // merge vins +// if (!tx.vins.empty()) { +// for (const shared_ptr& merger : tx.vins) { +// bool merged = false; +// merger->tx = shared_ptr(this); // TODO: can this cause this* to be deleted prematurely? +// for (const shared_ptr& mergee : vins) { +// if ((*mergee->keyImage)->hex == (*merger->keyImage)->hex) { +// mergee->merge(*merger); +// merged = true; +// break; +// } +// } +// if (!merged) vins.push_back(merger); +// } +// } +// +// // merge vouts +// if (!tx.vouts.empty()) { +// for (const shared_ptr& vout : tx.vouts) vout->tx = shared_ptr(this); +// if (vouts.empty()) vouts = tx.vouts; +// else { +// +// // determine if key images present +// int numKeyImages = 0; +// for (const shared_ptr vout : vouts) { +// if (vout->keyImage != boost::none) { +// if ((*vout->keyImage)->hex == boost::none) throw runtime_error("Key image hex cannot be null"); +// numKeyImages++; +// } +// } +// for (const shared_ptr& vout : tx.vouts) { +// if (vout->keyImage != boost::none) { +// if ((*vout->keyImage)->hex == boost::none) throw runtime_error("Key image hex cannot be null"); +// numKeyImages++; +// } +// } +// if (numKeyImages != 0 && vouts.size() + tx.vouts.size() != numKeyImages) throw runtime_error("Some vouts have a key image and some do not"); +// +// // merge by key images +// if (numKeyImages > 0) { +// for (const shared_ptr& merger : tx.vouts) { +// bool merged = false; +// for (const shared_ptr& mergee : vouts) { +// if ((*mergee->keyImage)->hex == (*merger->keyImage)->hex) { +// mergee->merge(*merger); +// merged = true; +// break; +// } +// } +// if (!merged) vouts.push_back(merger); +// } +// } +// +// // merge by position +// else { +// if (vouts.size() != tx.vouts.size()) throw runtime_error("Vout sizes are different"); +// for (int i = 0; i < tx.vouts.size(); i++) { +// vouts.at(i)->merge(*tx.vouts.at(i)); +// } +// } +// } +// } +// +// // handle unrelayed -> relayed -> confirmed +// if (*isConfirmed) { +// inTxPool = false; +// receivedTimestamp = boost::none; +// lastRelayedTimestamp = boost::none; +// } else { +// inTxPool = MoneroUtils::reconcile(inTxPool, tx.inTxPool, boost::none, true, boost::none); // unrelayed -> tx pool +// receivedTimestamp = MoneroUtils::reconcile(receivedTimestamp, tx.receivedTimestamp, boost::none, boost::none, false); // take earliest receive time +// lastRelayedTimestamp = MoneroUtils::reconcile(lastRelayedTimestamp, tx.lastRelayedTimestamp, boost::none, boost::none, true); // take latest relay time +// } + } + + void MoneroKeyImage::merge(const shared_ptr& self, const shared_ptr& other) { + cout << "MoneroKeyImage::merge()" << endl; + throw runtime_error("Not implemented"); + } + + void MoneroOutput::merge(const shared_ptr& self, const shared_ptr& other) { + cout << "MoneroOutput::merge()" << endl; + throw runtime_error("Not implemented"); + } + + // ---------------------------- PRIVATE HELPERS ----------------------------- + +} diff --git a/src/daemon/MoneroDaemonModel.h b/src/daemon/MoneroDaemonModel.h new file mode 100644 index 0000000..d9f453b --- /dev/null +++ b/src/daemon/MoneroDaemonModel.h @@ -0,0 +1,176 @@ +#pragma once + +#include +#include + +using namespace std; + +/** + * Public interface for libmonero-cpp library. + */ +namespace monero { + + /** + * Base struct which can be serialized. + * + * TODO: move this to MoneroUtils? + */ + struct SerializableStruct { + + /** + * Serializes the struct to a json string. + * + * @return the struct serialized to a json string + */ + string serialize() const; + + /** + * Convert the struct to a property tree. + * + * @return the converted property tree + */ + virtual boost::property_tree::ptree toPropertyTree() const = 0; + }; + + /** + * Enumerates Monero network types. + */ + enum MoneroNetworkType : uint8_t { + MAINNET = 0, + TESTNET, + STAGENET + }; + + /** + * Models a connection to a daemon. + * + * TODO: switch to boost::optional + */ + struct MoneroRpcConnection { + string uri; + string username; + string password; + //MoneroRpcConnection() : uri(""), username(""), password("")) {} + MoneroRpcConnection(const string& uri = "", const string& username = "", const string& password = "") : uri(uri), username(username), password(password) {} + }; + + // forward declarations + struct MoneroTx; + struct MoneroOutput; + + /** + * Models a Monero block header which contains information about the block. + */ + struct MoneroBlockHeader : public SerializableStruct { + boost::optional id; + boost::optional height; + boost::optional timestamp; + boost::optional size; + boost::optional weight; + boost::optional longTermWeight; + boost::optional depth; + boost::optional difficulty; + boost::optional cumulativeDifficulty; + boost::optional majorVersion; + boost::optional minorVersion; + boost::optional nonce; + boost::optional coinbaseTxId; + boost::optional numTxs; + boost::optional orphanStatus; + boost::optional prevId; + boost::optional reward; + boost::optional powHash; + + boost::property_tree::ptree toPropertyTree() const; + void merge(const shared_ptr& self, const shared_ptr& other); + }; + + /** + * Models a Monero block in the blockchain. + */ + struct MoneroBlock : public MoneroBlockHeader { + boost::optional hex; + boost::optional> coinbaseTx; + vector> txs; + vector txIds; + + boost::property_tree::ptree toPropertyTree() const; + void merge(const shared_ptr& self, const shared_ptr& other); + }; + + /** + * Models a Monero transaction on the blockchain. + */ + struct MoneroTx : public SerializableStruct { + static const string DEFAULT_PAYMENT_ID; // default payment id "0000000000000000" + boost::optional> block; + boost::optional id; + boost::optional version; + boost::optional isCoinbase; + boost::optional paymentId; + boost::optional fee; + boost::optional mixin; + boost::optional doNotRelay; + boost::optional isRelayed; + boost::optional isConfirmed; + boost::optional inTxPool; + boost::optional numConfirmations; + boost::optional unlockTime; + boost::optional lastRelayedTimestamp; + boost::optional receivedTimestamp; + boost::optional isDoubleSpend; + boost::optional key; + boost::optional fullHex; + boost::optional prunedHex; + boost::optional prunableHex; + boost::optional prunableHash; + boost::optional size; + boost::optional weight; + vector> vins; + vector> vouts; + vector outputIndices; + boost::optional metadata; + boost::optional commonTxSets; + vector extra; + boost::optional rctSignatures; // TODO: implement + boost::optional rctSigPrunable; // TODO: implement + boost::optional isKeptByBlock; + boost::optional isFailed; + boost::optional lastFailedHeight; + boost::optional lastFailedId; + boost::optional maxUsedBlockHeight; + boost::optional maxUsedBlockId; + vector signatures; + + boost::property_tree::ptree toPropertyTree() const; + boost::optional getHeight() const { + if (block == boost::none) return boost::none; + return *((*block)->height); + } + void merge(const shared_ptr& self, const shared_ptr& other); + }; + + /** + * Models a Monero key image. + */ + struct MoneroKeyImage { + boost::optional hex; + boost::optional signature; + + void merge(const shared_ptr& self, const shared_ptr& other); + }; + + /** + * Models a Monero transaction output. + */ + struct MoneroOutput { + shared_ptr tx; + boost::optional> keyImage; + boost::optional amount; + boost::optional index; + vector ringOutputIndices; + boost::optional stealthPublicKey; + + void merge(const shared_ptr& self, const shared_ptr& other); + }; +} diff --git a/src/wallet/MoneroWallet.cpp b/src/wallet/MoneroWallet.cpp index aacac8e..9b6bd3d 100644 --- a/src/wallet/MoneroWallet.cpp +++ b/src/wallet/MoneroWallet.cpp @@ -1,4 +1,4 @@ -#include "wallet/MoneroWallet.h" +#include "MoneroWallet.h" #include "utils/MoneroUtils.h" #include @@ -11,112 +11,10 @@ using namespace cryptonote; using namespace epee; /** - * Public interface for libmonero-cpp library. + * Public library interface. */ namespace monero { - // -------------------------- MODEL SERIALIZATION --------------------------- - - boost::property_tree::ptree MoneroAccount::toPropertyTree() const { - //cout << "MoneroAccount::toPropertyTree(node)" << endl; - boost::property_tree::ptree node; - if (index != boost::none) node.put("index", *index); - if (primaryAddress != boost::none) node.put("primaryAddress", *primaryAddress); - if (balance != boost::none) node.put("balance", *balance); - if (unlockedBalance != boost::none) node.put("unlockedBalance", *unlockedBalance); - if (!subaddresses.empty()) node.add_child("subaddresses", MoneroUtils::toPropertyTree(subaddresses)); -// if (!subaddresses.empty()) { -// boost::property_tree::ptree subaddressesNode; -// for (const MoneroSubaddress& subaddress : subaddresses) { -// subaddressesNode.push_back(std::make_pair("", subaddress.toPropertyTree())); -// } -// node.add_child("subaddresses", subaddressesNode); -// } - return node; - } - - boost::property_tree::ptree MoneroSubaddress::toPropertyTree() const { - //cout << "MoneroSubaddress::toPropertyTree(node)" << endl; - boost::property_tree::ptree node; - if (accountIndex != boost::none) node.put("accountIndex", *accountIndex); - if (index != boost::none) node.put("index", *index); - if (address != boost::none) node.put("address", *address); - if (label != boost::none) node.put("label", *label); - if (balance != boost::none) node.put("balance", *balance); - if (unlockedBalance != boost::none) node.put("unlockedBalance", *unlockedBalance); - if (numUnspentOutputs != boost::none) node.put("numUnspentOutputs", *numUnspentOutputs); - if (isUsed != boost::none) node.put("isUsed", *isUsed); - if (numBlocksToUnlock != boost::none) node.put("numBlocksToUnlock", *numBlocksToUnlock); - return node; - } - - boost::property_tree::ptree MoneroTxWallet::toPropertyTree() const { - //cout << "MoneroTxWallet::toPropertyTree(node)" << endl; - boost::property_tree::ptree node = MoneroTx::toPropertyTree(); - if (!vouts.empty()) throw runtime_error("vouts not implemented"); - if (!incomingTransfers.empty()) node.add_child("incomingTransfers", MoneroUtils::toPropertyTree(incomingTransfers)); - if (outgoingTransfer != boost::none) throw runtime_error("outgoingTransfers not implemented"); - if (note != boost::none) node.put("note", *note); - return node; - } - - boost::property_tree::ptree MoneroTxRequest::toPropertyTree() const { - //cout << "MoneroTxRequest::toPropertyTree(node)" << endl; - throw runtime_error("Not implemented"); - } - - boost::property_tree::ptree MoneroTransfer::toPropertyTree() const { - //cout << "MoneroTransfer::toPropertyTree(node)" << endl; - boost::property_tree::ptree node; - if (amount != boost::none) node.put("amount", *amount); - if (accountIndex != boost::none) node.put("accountIndex", *accountIndex); - if (numSuggestedConfirmations != boost::none) node.put("numSuggestedConfirmations", *numSuggestedConfirmations); - return node; - } - - boost::property_tree::ptree MoneroIncomingTransfer::toPropertyTree() const { - //cout << "MoneroIncomingTransfer::toPropertyTree(node)" << endl; - boost::property_tree::ptree node = MoneroTransfer::toPropertyTree(); - if (subaddressIndex != boost::none) node.put("subaddressIndex", *subaddressIndex); - if (address != boost::none) node.put("address", *address); - return node; - } - - boost::property_tree::ptree MoneroOutgoingTransfer::toPropertyTree() const { - //cout << "MoneroOutgoingTransfer::toPropertyTree(node)" << endl; - boost::property_tree::ptree node = MoneroTransfer::toPropertyTree(); - if (!subaddressIndices.empty()) throw runtime_error("subaddressIndices not implemented"); - if (!addresses.empty()) throw runtime_error("addresses not implemented"); - if (!destinations.empty()) throw runtime_error("destinations not implemented"); - return node; - } - - //boost::property_tree::ptree MoneroUtils::txWalletToPropertyTree(const MoneroTxWallet& tx) { - // cout << "txWalletToPropertyTree(tx)" << endl; - // boost::property_tree::ptree txNode = txToPropertyTree(tx); - // if (!tx.vouts.empty()) throw runtime_error("not implemented"); - // if (!tx.incomingTransfers.empty()) throw runtime_error("not implemented"); - // if (tx.outgoingTransfer != boost::none) throw runtime_error("not implemented"); - // if (tx.numSuggestedConfirmations != boost::none) txNode.put("numSuggestedConfirmations", *tx.numSuggestedConfirmations); - // if (tx.note != boost::none) txNode.put("note", *tx.note); - // return txNode; - //} - // - //boost::property_tree::ptree MoneroUtils::txRequestToPropertyTree(const MoneroTxRequest& tx) { - // cout << "txWalletToPropertyTree(tx)" << endl; - // boost::property_tree::ptree txNode = txWalletToPropertyTree(tx); - // if (tx.isOutgoing != boost::none) txNode.put("isOutgoing", *tx.isOutgoing); - // if (tx.isIncoming != boost::none) txNode.put("isIncoming", *tx.isIncoming); - // if (!tx.txIds.empty()) throw runtime_error("not implemented"); - // if (tx.hasPaymentId != boost::none) txNode.put("hasPaymentId", *tx.hasPaymentId); - // if (!tx.paymentIds.empty()) throw runtime_error("not implemented"); - // if (tx.minHeight != boost::none) txNode.put("minHeight", *tx.minHeight); - // if (tx.maxHeight != boost::none) txNode.put("maxHeight", *tx.maxHeight); - // if (tx.includeOutputs != boost::none) txNode.put("includeOutputs", *tx.includeOutputs); - // if (tx.transferRequest != boost::none) throw runtime_error("not implemented"); - // return txNode; - //} - // ----------------------- UNDECLARED PRIVATE HELPERS ----------------------- // TODO: static? diff --git a/src/wallet/MoneroWallet.h b/src/wallet/MoneroWallet.h index ed581ac..084cbb2 100644 --- a/src/wallet/MoneroWallet.h +++ b/src/wallet/MoneroWallet.h @@ -1,6 +1,6 @@ #pragma once -#include "daemon/MoneroDaemon.h" +#include "MoneroWalletModel.h" #include "wallet/wallet2.h" using namespace std; @@ -8,196 +8,10 @@ using namespace crypto; using namespace monero; /** - * Public interface for libmonero-cpp library. + * Public library interface. */ namespace monero { - struct Wallet2Listener; - - // ----------------------------------- MODEL -------------------------------- - - /** - * Models a result of syncing a wallet. - */ - struct MoneroSyncResult { - uint64_t numBlocksFetched; - bool receivedMoney; - MoneroSyncResult() {} - MoneroSyncResult(const uint64_t numBlocksFetched, const bool receivedMoney) : numBlocksFetched(numBlocksFetched), receivedMoney(receivedMoney) {} - }; - - /** - * Models a Monero subaddress. - */ - struct MoneroSubaddress : public SerializableStruct { - boost::optional accountIndex; - boost::optional index; - boost::optional address; - boost::optional label; - boost::optional balance; - boost::optional unlockedBalance; - boost::optional numUnspentOutputs; - boost::optional isUsed; - boost::optional numBlocksToUnlock; -// BEGIN_KV_SERIALIZE_MAP() -// KV_SERIALIZE(accountIndex) -// KV_SERIALIZE(index) -// KV_SERIALIZE(address) -// KV_SERIALIZE(label) -// KV_SERIALIZE(balance) -// KV_SERIALIZE(unlockedBalance) -// KV_SERIALIZE(numUnspentOutputs) -// KV_SERIALIZE(isUsed) -// KV_SERIALIZE(numBlocksToUnlock) -// END_KV_SERIALIZE_MAP() - - boost::property_tree::ptree toPropertyTree() const; - //void merge(const shared_ptr& self, const shared_ptr& other); - }; - - /** - * Models a Monero account. - */ - struct MoneroAccount : public SerializableStruct { - boost::optional index; - boost::optional primaryAddress; - boost::optional label; - boost::optional balance; - boost::optional unlockedBalance; - boost::optional tag; - vector subaddresses; -// BEGIN_KV_SERIALIZE_MAP() -// KV_SERIALIZE(index) -// KV_SERIALIZE(primaryAddress) -// KV_SERIALIZE(label) -// KV_SERIALIZE(balance) -// KV_SERIALIZE(unlockedBalance) -// KV_SERIALIZE(tag) -// KV_SERIALIZE(subaddresses) -// END_KV_SERIALIZE_MAP() - - boost::property_tree::ptree toPropertyTree() const; - }; - - // forward declarations - struct MoneroTxWallet; - struct MoneroTxRequest; - - /** - * Models an outgoing transfer destination. - */ - struct MoneroDestination { - boost::optional address; - boost::optional amount; - }; - - /** - * Models a base transfer of funds to or from the wallet. - */ - struct MoneroTransfer : SerializableStruct { - shared_ptr tx; - boost::optional isIncoming; - boost::optional amount; - boost::optional accountIndex; - boost::optional numSuggestedConfirmations; - - boost::property_tree::ptree toPropertyTree() const; - boost::optional getIsOutgoing() const { - if (isIncoming == boost::none ) return boost::none; - return !(*isIncoming); - } - }; - - /** - * Models an incoming transfer of funds to the wallet. - */ - struct MoneroIncomingTransfer : public MoneroTransfer { - boost::optional subaddressIndex; - boost::optional address; - - boost::property_tree::ptree toPropertyTree() const; - }; - - /** - * Models an outgoing transfer of funds from the wallet. - */ - struct MoneroOutgoingTransfer : public MoneroTransfer { - vector subaddressIndices; - vector addresses; - vector> destinations; - - boost::property_tree::ptree toPropertyTree() const; - }; - - /** - * Models a Monero output with wallet extensions. - */ - struct MoneroOutputWallet : public MoneroOutput { - boost::optional accountIndex; - boost::optional subaddressIndex; - boost::optional isSpent; - boost::optional isUnlocked; - boost::optional isFrozen; - }; - - /** - * Models a Monero transaction in the context of a wallet. - */ - struct MoneroTxWallet : public MoneroTx { - vector> incomingTransfers; - boost::optional> outgoingTransfer; - boost::optional note; - - boost::property_tree::ptree toPropertyTree() const; - }; - - /** - * Configures a request to retrieve transfers. - * - * All transfers are returned except those that do not meet the criteria defined in this request. - */ - struct MoneroTransferRequest : public MoneroTransfer { - boost::optional isIncoming; - boost::optional address; - vector addresses; - boost::optional subaddressIndex; - vector subaddressIndices; - vector> destinations; - boost::optional hasDestinations; - boost::optional> txRequest; - }; - - /** - * Configures a request to retrieve transactions. - * - * All transactions are returned except those that do not meet the criteria defined in this request. - */ - struct MoneroTxRequest : public MoneroTxWallet { - boost::optional isOutgoing; - boost::optional isIncoming; - vector txIds; - boost::optional hasPaymentId; - vector paymentIds; - boost::optional minHeight; - boost::optional maxHeight; - boost::optional includeOutputs; - boost::optional> transferRequest; - - boost::optional getHeight() { return block == boost::none ? boost::none : (*block)->height; } - boost::property_tree::ptree toPropertyTree() const; - }; - - /** - * Configures a request to retrieve wallet outputs (i.e. outputs that the wallet has or had the - * ability to spend). - * - * All outputs are returned except those that do not meet the criteria defined in this request. - */ - struct MoneroOutputRequest : public MoneroOutput { - vector subaddressIndices; - boost::optional> txRequest; - }; - // --------------------------------- LISTENERS ------------------------------ // class i_wallet2_callback @@ -260,6 +74,9 @@ namespace monero { virtual void onNewBlock(MoneroBlock& block) {}; }; + // forward declaration of internal wallet2 listener + struct Wallet2Listener; + // ---------------------------- WALLET INTERFACE ---------------------------- /** @@ -1271,36 +1088,3 @@ namespace monero { shared_ptr buildTxWithIncomingTransfer(const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd) const; }; } - -//// --------------------------- MODEL SERIALIZATION ---------------------------- -// -//BOOST_CLASS_VERSION(monero::MoneroWallet, 0) -//BOOST_CLASS_VERSION(monero::MoneroAccount, 0) -//BOOST_CLASS_VERSION(monero::MoneroSubaddress, 0) -// -//namespace boost { -// namespace serialization { -// template -// inline void serialize(Archive &a, monero::MoneroWallet& x, const boost::serialization::version_type ver) -// { -//// a & x.m_L; -//// a & x.m_R; -// } -// -// template -// inline void serialize(Archive &a, monero::MoneroAccount& x, const boost::serialization::version_type ver) -// { -//// a & x.m_signer; -//// a & x.m_LR; -//// a & x.m_partial_key_images; -// } -// -// template -// inline void serialize(Archive &a, monero::MoneroSubaddress& x, const boost::serialization::version_type ver) -// { -//// a & x.m_signer; -//// a & x.m_LR; -//// a & x.m_partial_key_images; -// } -// } // namepsace serialization -//} // namespace boost diff --git a/src/wallet/MoneroWalletModel.cpp b/src/wallet/MoneroWalletModel.cpp new file mode 100644 index 0000000..0c551b1 --- /dev/null +++ b/src/wallet/MoneroWalletModel.cpp @@ -0,0 +1,120 @@ +#include "MoneroWallet.h" + +#include "utils/MoneroUtils.h" +#include +#include + +using namespace std; +using namespace cryptonote; // TODO: delete? +using namespace epee; // TODO: delete? + +/** + * Public library interface. + */ +namespace monero { + + // -------------------------- MODEL SERIALIZATION --------------------------- + + boost::property_tree::ptree MoneroAccount::toPropertyTree() const { + //cout << "MoneroAccount::toPropertyTree(node)" << endl; + boost::property_tree::ptree node; + if (index != boost::none) node.put("index", *index); + if (primaryAddress != boost::none) node.put("primaryAddress", *primaryAddress); + if (balance != boost::none) node.put("balance", *balance); + if (unlockedBalance != boost::none) node.put("unlockedBalance", *unlockedBalance); + if (!subaddresses.empty()) node.add_child("subaddresses", MoneroUtils::toPropertyTree(subaddresses)); +// if (!subaddresses.empty()) { +// boost::property_tree::ptree subaddressesNode; +// for (const MoneroSubaddress& subaddress : subaddresses) { +// subaddressesNode.push_back(std::make_pair("", subaddress.toPropertyTree())); +// } +// node.add_child("subaddresses", subaddressesNode); +// } + return node; + } + + boost::property_tree::ptree MoneroSubaddress::toPropertyTree() const { + //cout << "MoneroSubaddress::toPropertyTree(node)" << endl; + boost::property_tree::ptree node; + if (accountIndex != boost::none) node.put("accountIndex", *accountIndex); + if (index != boost::none) node.put("index", *index); + if (address != boost::none) node.put("address", *address); + if (label != boost::none) node.put("label", *label); + if (balance != boost::none) node.put("balance", *balance); + if (unlockedBalance != boost::none) node.put("unlockedBalance", *unlockedBalance); + if (numUnspentOutputs != boost::none) node.put("numUnspentOutputs", *numUnspentOutputs); + if (isUsed != boost::none) node.put("isUsed", *isUsed); + if (numBlocksToUnlock != boost::none) node.put("numBlocksToUnlock", *numBlocksToUnlock); + return node; + } + + boost::property_tree::ptree MoneroTxWallet::toPropertyTree() const { + //cout << "MoneroTxWallet::toPropertyTree(node)" << endl; + boost::property_tree::ptree node = MoneroTx::toPropertyTree(); + if (!vouts.empty()) throw runtime_error("vouts not implemented"); + if (!incomingTransfers.empty()) node.add_child("incomingTransfers", MoneroUtils::toPropertyTree(incomingTransfers)); + if (outgoingTransfer != boost::none) throw runtime_error("outgoingTransfers not implemented"); + if (note != boost::none) node.put("note", *note); + return node; + } + + boost::property_tree::ptree MoneroTxRequest::toPropertyTree() const { + //cout << "MoneroTxRequest::toPropertyTree(node)" << endl; + throw runtime_error("Not implemented"); + } + + boost::property_tree::ptree MoneroTransfer::toPropertyTree() const { + //cout << "MoneroTransfer::toPropertyTree(node)" << endl; + boost::property_tree::ptree node; + if (amount != boost::none) node.put("amount", *amount); + if (accountIndex != boost::none) node.put("accountIndex", *accountIndex); + if (numSuggestedConfirmations != boost::none) node.put("numSuggestedConfirmations", *numSuggestedConfirmations); + return node; + } + + boost::property_tree::ptree MoneroIncomingTransfer::toPropertyTree() const { + //cout << "MoneroIncomingTransfer::toPropertyTree(node)" << endl; + boost::property_tree::ptree node = MoneroTransfer::toPropertyTree(); + if (subaddressIndex != boost::none) node.put("subaddressIndex", *subaddressIndex); + if (address != boost::none) node.put("address", *address); + return node; + } + + boost::property_tree::ptree MoneroOutgoingTransfer::toPropertyTree() const { + //cout << "MoneroOutgoingTransfer::toPropertyTree(node)" << endl; + boost::property_tree::ptree node = MoneroTransfer::toPropertyTree(); + if (!subaddressIndices.empty()) throw runtime_error("subaddressIndices not implemented"); + if (!addresses.empty()) throw runtime_error("addresses not implemented"); + if (!destinations.empty()) throw runtime_error("destinations not implemented"); + return node; + } + + //boost::property_tree::ptree MoneroUtils::txWalletToPropertyTree(const MoneroTxWallet& tx) { + // cout << "txWalletToPropertyTree(tx)" << endl; + // boost::property_tree::ptree txNode = txToPropertyTree(tx); + // if (!tx.vouts.empty()) throw runtime_error("not implemented"); + // if (!tx.incomingTransfers.empty()) throw runtime_error("not implemented"); + // if (tx.outgoingTransfer != boost::none) throw runtime_error("not implemented"); + // if (tx.numSuggestedConfirmations != boost::none) txNode.put("numSuggestedConfirmations", *tx.numSuggestedConfirmations); + // if (tx.note != boost::none) txNode.put("note", *tx.note); + // return txNode; + //} + // + //boost::property_tree::ptree MoneroUtils::txRequestToPropertyTree(const MoneroTxRequest& tx) { + // cout << "txWalletToPropertyTree(tx)" << endl; + // boost::property_tree::ptree txNode = txWalletToPropertyTree(tx); + // if (tx.isOutgoing != boost::none) txNode.put("isOutgoing", *tx.isOutgoing); + // if (tx.isIncoming != boost::none) txNode.put("isIncoming", *tx.isIncoming); + // if (!tx.txIds.empty()) throw runtime_error("not implemented"); + // if (tx.hasPaymentId != boost::none) txNode.put("hasPaymentId", *tx.hasPaymentId); + // if (!tx.paymentIds.empty()) throw runtime_error("not implemented"); + // if (tx.minHeight != boost::none) txNode.put("minHeight", *tx.minHeight); + // if (tx.maxHeight != boost::none) txNode.put("maxHeight", *tx.maxHeight); + // if (tx.includeOutputs != boost::none) txNode.put("includeOutputs", *tx.includeOutputs); + // if (tx.transferRequest != boost::none) throw runtime_error("not implemented"); + // return txNode; + //} + + // ----------------------- UNDECLARED PRIVATE HELPERS ----------------------- + +} diff --git a/src/wallet/MoneroWalletModel.h b/src/wallet/MoneroWalletModel.h new file mode 100644 index 0000000..7aaf449 --- /dev/null +++ b/src/wallet/MoneroWalletModel.h @@ -0,0 +1,194 @@ +#pragma once + +#include "daemon/MoneroDaemonModel.h" + +using namespace std; +using namespace monero; + +/** + * Public library interface. + */ +namespace monero { + + /** + * Models a result of syncing a wallet. + */ + struct MoneroSyncResult { + uint64_t numBlocksFetched; + bool receivedMoney; + MoneroSyncResult() {} + MoneroSyncResult(const uint64_t numBlocksFetched, const bool receivedMoney) : numBlocksFetched(numBlocksFetched), receivedMoney(receivedMoney) {} + }; + + /** + * Models a Monero subaddress. + */ + struct MoneroSubaddress : public SerializableStruct { + boost::optional accountIndex; + boost::optional index; + boost::optional address; + boost::optional label; + boost::optional balance; + boost::optional unlockedBalance; + boost::optional numUnspentOutputs; + boost::optional isUsed; + boost::optional numBlocksToUnlock; +// BEGIN_KV_SERIALIZE_MAP() +// KV_SERIALIZE(accountIndex) +// KV_SERIALIZE(index) +// KV_SERIALIZE(address) +// KV_SERIALIZE(label) +// KV_SERIALIZE(balance) +// KV_SERIALIZE(unlockedBalance) +// KV_SERIALIZE(numUnspentOutputs) +// KV_SERIALIZE(isUsed) +// KV_SERIALIZE(numBlocksToUnlock) +// END_KV_SERIALIZE_MAP() + + boost::property_tree::ptree toPropertyTree() const; + //void merge(const shared_ptr& self, const shared_ptr& other); + }; + + /** + * Models a Monero account. + */ + struct MoneroAccount : public SerializableStruct { + boost::optional index; + boost::optional primaryAddress; + boost::optional label; + boost::optional balance; + boost::optional unlockedBalance; + boost::optional tag; + vector subaddresses; +// BEGIN_KV_SERIALIZE_MAP() +// KV_SERIALIZE(index) +// KV_SERIALIZE(primaryAddress) +// KV_SERIALIZE(label) +// KV_SERIALIZE(balance) +// KV_SERIALIZE(unlockedBalance) +// KV_SERIALIZE(tag) +// KV_SERIALIZE(subaddresses) +// END_KV_SERIALIZE_MAP() + + boost::property_tree::ptree toPropertyTree() const; + }; + + // forward declarations + struct MoneroTxWallet; + struct MoneroTxRequest; + + /** + * Models an outgoing transfer destination. + */ + struct MoneroDestination { + boost::optional address; + boost::optional amount; + }; + + /** + * Models a base transfer of funds to or from the wallet. + */ + struct MoneroTransfer : SerializableStruct { + shared_ptr tx; // TODO *** switch to MoneroTxWallet *** + boost::optional isIncoming; + boost::optional amount; + boost::optional accountIndex; + boost::optional numSuggestedConfirmations; + + boost::property_tree::ptree toPropertyTree() const; + boost::optional getIsOutgoing() const { + if (isIncoming == boost::none ) return boost::none; + return !(*isIncoming); + } + }; + + /** + * Models an incoming transfer of funds to the wallet. + */ + struct MoneroIncomingTransfer : public MoneroTransfer { + boost::optional subaddressIndex; + boost::optional address; + + boost::property_tree::ptree toPropertyTree() const; + }; + + /** + * Models an outgoing transfer of funds from the wallet. + */ + struct MoneroOutgoingTransfer : public MoneroTransfer { + vector subaddressIndices; + vector addresses; + vector> destinations; + + boost::property_tree::ptree toPropertyTree() const; + }; + + /** + * Models a Monero output with wallet extensions. + */ + struct MoneroOutputWallet : public MoneroOutput { + boost::optional accountIndex; + boost::optional subaddressIndex; + boost::optional isSpent; + boost::optional isUnlocked; + boost::optional isFrozen; + }; + + /** + * Models a Monero transaction in the context of a wallet. + */ + struct MoneroTxWallet : public MoneroTx { + vector> incomingTransfers; + boost::optional> outgoingTransfer; + boost::optional note; + + boost::property_tree::ptree toPropertyTree() const; + }; + + /** + * Configures a request to retrieve transfers. + * + * All transfers are returned except those that do not meet the criteria defined in this request. + */ + struct MoneroTransferRequest : public MoneroTransfer { + boost::optional isIncoming; + boost::optional address; + vector addresses; + boost::optional subaddressIndex; + vector subaddressIndices; + vector> destinations; + boost::optional hasDestinations; + boost::optional> txRequest; + }; + + /** + * Configures a request to retrieve transactions. + * + * All transactions are returned except those that do not meet the criteria defined in this request. + */ + struct MoneroTxRequest : public MoneroTxWallet { + boost::optional isOutgoing; + boost::optional isIncoming; + vector txIds; + boost::optional hasPaymentId; + vector paymentIds; + boost::optional minHeight; + boost::optional maxHeight; + boost::optional includeOutputs; + boost::optional> transferRequest; + + boost::optional getHeight() { return block == boost::none ? boost::none : (*block)->height; } + boost::property_tree::ptree toPropertyTree() const; + }; + + /** + * Configures a request to retrieve wallet outputs (i.e. outputs that the wallet has or had the + * ability to spend). + * + * All outputs are returned except those that do not meet the criteria defined in this request. + */ + struct MoneroOutputRequest : public MoneroOutput { + vector subaddressIndices; + boost::optional> txRequest; + }; +}