diff --git a/src/daemon/MoneroDaemon.cpp b/src/daemon/MoneroDaemon.cpp index ba5affd..130d72b 100644 --- a/src/daemon/MoneroDaemon.cpp +++ b/src/daemon/MoneroDaemon.cpp @@ -96,6 +96,9 @@ namespace monero { return node; } + // -------------------------- MODEL IMPLEMENTATION -------------------------- + + // ---------------------------- PRIVATE HELPERS ----------------------------- } diff --git a/src/daemon/MoneroDaemon.h b/src/daemon/MoneroDaemon.h index 42200cd..3cb1b36 100644 --- a/src/daemon/MoneroDaemon.h +++ b/src/daemon/MoneroDaemon.h @@ -55,9 +55,47 @@ namespace monero { }; // forward declarations - struct MoneroBlock; + 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; + }; + + /** + * 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; + }; + /** * Models a Monero transaction on the blockchain. */ @@ -103,44 +141,10 @@ namespace monero { vector signatures; boost::property_tree::ptree toPropertyTree() const; - }; - - /** - * 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; - }; - - /** - * 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; + boost::optional getHeight() const { + if (block == boost::none) return boost::none; + return *((*block)->height); + } }; /** diff --git a/src/wallet/MoneroWallet.cpp b/src/wallet/MoneroWallet.cpp index 724b14d..e83c222 100644 --- a/src/wallet/MoneroWallet.cpp +++ b/src/wallet/MoneroWallet.cpp @@ -174,9 +174,107 @@ namespace monero { // return incomingTransfer; // } - void mergeTx(vector> txs, const shared_ptr& tx) { + /** + * Merges a transaction into a unique set of transactions. + * + * TODO monero-core: skipIfAbsent only necessary because incoming payments not returned + * when sent from/to same account #4500 + * + * @param txs are existing transactions to merge into + * @param tx is the transaction to merge into the existing txs + * @param skipIfAbsent specifies if the tx should not be added if it doesn't already exist + * @returns the merged tx + */ + void mergeTx(vector> txs, const shared_ptr& tx, bool skipIfAbsent) { cout << "mergeTx()" << endl; - throw runtime_error("Not implemented"); + if (tx->id == boost::none) throw runtime_error("Tx id is not initialized"); + for (const auto& aTx: txs) { + + // merge tx + if (*aTx->id == *tx->id) { + + // merge blocks if confirmed, txs otherwise + if (aTx->block != boost::none || tx->block != boost::none) { + if (aTx->block == boost::none) { + aTx->block = shared_ptr(new MoneroBlock()); + vector> txs; + txs.push_back(tx); + (*aTx->block)->txs = txs; + (*aTx->block)->height = *tx->getHeight(); + } + if (tx->block == boost::none) { + tx->block = shared_ptr(new MoneroBlock()); + vector> txs; + txs.push_back(tx); + (*tx->block)->txs = txs; + (*tx->block)->height = *aTx->getHeight(); + } + aTx->block->merge(tx->block); + } else { + aTx->merge(*tx); + } + return; + } + + // merge common block of different txs + if (tx->getHeight() != boost::none && *tx->getHeight() == *aTx->getHeight()) + (*aTx-block)->merge(*tx->block); + //if (aTx.getIsConfirmed()) assertTrue(aTx.getBlock().getTxs().contains(aTx)); + } + } + + // add tx if it doesn't already exist unless skipped + if (!skipIfAbsent) { + txs.push_back(tx); + } else { + cout << "WARNING: tx does not already exist" << endl; // TODO: proper logging + } + } + + /** + * Merges a transaction into a unique set of transactions. + * + * TODO monero-wallet-rpc: skipIfAbsent only necessary because incoming payments not returned + * when sent from/to same account + * + * @param txs are existing transactions to merge into + * @param tx is the transaction to merge into the existing txs + * @param skipIfAbsent specifies if the tx should not be added + * if it doesn't already exist. Only necessasry to handle + * missing incoming payments from #4500. // TODO + * @returns the merged tx + */ + private static void mergeTx(List txs, MoneroTxWallet tx, boolean skipIfAbsent) { + assertNotNull(tx.getId()); + for (MoneroTxWallet aTx : txs) { + + // merge tx + if (aTx.getId().equals(tx.getId())) { + + // merge blocks which only exist when confirmed + if (aTx.getBlock() != null || tx.getBlock() != null) { + if (aTx.getBlock() == null) aTx.setBlock(new MoneroBlock().setTxs(new ArrayList(Arrays.asList(aTx))).setHeight(tx.getHeight())); + if (tx.getBlock() == null) tx.setBlock(new MoneroBlock().setTxs(new ArrayList(Arrays.asList(tx))).setHeight(aTx.getHeight())); + aTx.getBlock().merge(tx.getBlock()); + } else { + aTx.merge(tx); + } + return; + } + + // merge common block of different txs + if (tx.getHeight() != null && tx.getHeight().equals(aTx.getHeight())) { + aTx.getBlock().merge(tx.getBlock()); + if (aTx.getIsConfirmed()) assertTrue(aTx.getBlock().getTxs().contains(aTx)); + } + } + + // add tx if it doesn't already exist unless skipped + if (!skipIfAbsent) { + txs.add(tx); + } else { + LOGGER.warn("WARNING: tx does not already exist"); + } } // ----------------------------- WALLET LISTENER ---------------------------- @@ -629,8 +727,8 @@ namespace monero { wallet2->get_payments(payments, minHeight, maxHeight, accountIndex, subaddressIndices); for (std::list>::const_iterator i = payments.begin(); i != payments.end(); ++i) { shared_ptr tx = buildTxWithIncomingTransfer(i->second.m_tx_hash, i->first, i->second); - //mergeTx(txs, tx); // TODO - txs.push_back(tx); + mergeTx(txs, tx); // TODO + //txs.push_back(tx); } } diff --git a/src/wallet/MoneroWallet.h b/src/wallet/MoneroWallet.h index 09c9768..9e4fe7e 100644 --- a/src/wallet/MoneroWallet.h +++ b/src/wallet/MoneroWallet.h @@ -101,6 +101,10 @@ namespace monero { boost::optional numSuggestedConfirmations; boost::property_tree::ptree toPropertyTree() const; + boost::optional getIsOutgoing() const { + if (isIncoming == boost::none ) return boost::none; + return !(*isIncoming); + } }; /** @@ -160,11 +164,6 @@ namespace monero { vector> destinations; boost::optional hasDestinations; boost::optional> txRequest; - - boost::optional getIsOutgoing() const { - if (isIncoming == boost::none ) return boost::none; - return !(*isIncoming); - } }; /**